# 【法线贴图】

# 法线贴图

全屏显示

// 在顶点着色器函数里构建切线空间的三个方向 TBN (v.normal是float3,v.tangent是float4)
o.normal = UnityObjectToWorldNormal( v.normal );
o.tangent = UnityObjectToWorldDir( v.tangent.xyz );// 使用Unity内置函数的方式
// o.tangent = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz);
o.btangent = cross( o.normal, o.tangent);
o.btangent *= v.tangent.w * unity_WorldTransformParams.w;// 处理翻转和镜像

// 在片段着色器里进行转换纹理贴图成向量
float3 tangentSpaceNormal = UnpackNormal( tex2D( _NormalTex, i.uv ) );

// 方式A
// 关于矩阵mul(pos,矩阵) 的矩阵先后顺序的问题
// 与Unity内置矩阵一致的方式,相乘时矩阵放[左侧]
float3x3 mtxTangToWord_A = float3x3(
    i.tangent.x,i.btangent.x,i.normal.x,
    i.tangent.y,i.btangent.y,i.normal.y,
    i.tangent.z,i.btangent.z,i.normal.z);
float3 N_A = mul(mtxTangToWord_A,tangentSpaceNormal);

// 方式B
// 与Unity内置矩阵相反的方式,相乘时矩阵放[右侧],其实就是方式A的转置
float3x3 mtxTangToWord_B = float3x3(
    i.tangent.x,i.tangent.y,i.tangent.z,
    i.btangent.x,i.btangent.y,i.btangent.z,
    i.normal.x,i.normal.y,i.normal.z);
// 与上面mtxTangToWord_B结果其实是一样的,这个是简写方式
float3x3 mtxTangToWord_B2 = float3x3(i.tangent,i.btangent,i.normal);
float3 N_B = mul(tangentSpaceNormal,mtxTangToWord_B);

【Shader练习-妲己】

# 高度图转法线图

#define heightMap iChannel0
#define heightMapResolution iChannelResolution[0]
#define normalStrength 10.0
#define textureOffset 1.0
#define pixelToTexelRatio (iResolution.xy/heightMapResolution.xy)

vec2 stdNormalMap(in vec2 uv) 
{
    float height = texture(heightMap, uv).r;
    return -vec2(dFdx(height), dFdy(height)) * pixelToTexelRatio;
}

vec2 texNormalMap(in vec2 uv)
{
    vec2 s = 1.0/heightMapResolution.xy;
    
    float p = texture(heightMap, uv).x;
    float h1 = texture(heightMap, uv + s * vec2(textureOffset,0)).x;
    float v1 = texture(heightMap, uv + s * vec2(0,textureOffset)).x;
       
    return (p - vec2(h1, v1));
}


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    
    float mousePosition = (iMouse.x > 0.1) ? iMouse.x : iResolution.x * 0.5;
    float mouseSplit = round(mousePosition * 0.5) * 2. + 1.;
    
    if (abs(fragCoord.x - mouseSplit) < 1.0) 
    {
        fragColor = vec4(0.);
        return;
    }
    
    vec2 normal = (fragCoord.x > mouseSplit) ? texNormalMap(uv) : stdNormalMap(uv);
    
    normal *= normalStrength;
    normal += 0.5;
    
    fragColor = vec4(normal, 1., 1.);
}

【参考】 (opens new window)

# 控制法线强度

float3 tangentSpaceNormal = UnpackNormal( tex2D( _RockNormals, i.uv ) );
tangentSpaceNormal = normalize(lerp( float3(0,0,1), tangentSpaceNormal, _NormalIntensity ));