【UnityShader】凹凸映射(世界空间下)

切线空间下的计算我们已经涉及过,下文来介绍在世界空间下实现凹凸映射:

与切线空间下不同的是,我们在v2f结构体中做了修改:

struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;//纹理采样用 两个纹理所以用float4
//切线空间到世界空间变化矩阵的每一行 w分量中存储世界顶点坐标
float4 Trow0:TEXCOORD1;
float4 Trow1:TEXCOORD2;
float4 Trow2:TEXCOORD3;
};

Trow矩阵分开每行分别计算。其中w分量存储世界空间下的顶点坐标。

切线空间到世界空间将空间坐标轴的矢量表示按列排列即可

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Custom/Chapter7-NormalMapWorldSpace"
{
  Properties{
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _BumpMap ("Normal Map", 2D) = "bump" {}
        _BumpScale ("Bump Scale", Float) = 1.0
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader{        
        pass{
            Tags{"LightMode" = "ForwardBase"}

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "Lighting.cginc"

        fixed4 _Color;
        sampler2D _MainTex;
        sampler2D _BumpMap;
        float4 _BumpMap_ST;
        float _BumpScale;
        fixed4 _Specular;
        float _Gloss;
        float4 _MainTex_ST;//纹理类型属性

        struct a2v{
            float4 vertex:POSITION;
            float3 normal:NORMAL;
            float4 tangent:TANGENT; //顶点切线方向填充,四维的原因是要用w决定副切线的方向性
            float4 texcoord:TEXCOORD0;//存储纹理坐标  
        };
        struct v2f{
            float4 pos:SV_POSITION;
            float4 uv:TEXCOORD0;//纹理采样用 两个纹理所以用float4
            //切线空间到世界空间变化矩阵的每一行 w分量中存储世界顶点坐标
            float4 Trow0:TEXCOORD1;
            float4 Trow1:TEXCOORD2;   
            float4 Trow2:TEXCOORD3;
        };
        v2f vert(a2v v){
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);

            o.uv.xy = v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
            o.uv.zw = v.texcoord.xy*_BumpMap_ST.xy+_BumpMap_ST.zw;  //两张纹理,分别存储纹理坐标
            
            float3 worldPos = mul(unity_ObjectToWorld,v.vertex);
            fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
            fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
            fixed3 worldBinnormal = cross(worldNormal,worldTangent)*v.tangent.w;

            //计算转化矩阵 
            o.Trow0 = float4(worldTangent.x,worldBinnormal.x,worldNormal.x,worldPos.x);
            o.Trow1 = float4(worldTangent.y,worldBinnormal.y,worldNormal.y,worldPos.y);
            o.Trow2 = float4(worldTangent.z,worldBinnormal.z,worldNormal.z,worldPos.z);
            return o;
        }

         fixed4 frag(v2f i):SV_Target{
           float3 worldPos = float3(i.Trow0.w,i.Trow1.w,i.Trow2.w);
           fixed3 LightDir = normalize(UnityWorldSpaceLightDir(worldPos));
           fixed3 ViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
           //首先对法线纹理进行采样
           fixed4 packedNormal = tex2D(_BumpMap,i.uv.zw);
           //对像素反映射得到原来的发现方向,由于再切线空间先 保证z为正
           //纹理类型不为Normal map时  Unity一般为Normal map 所以下面方式计算的话结果会出差
           //tangentNormal.xy = (packedNormal.xy*2-1)*_BumpScale;
           //tangentNormal.z = sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy)));//因为是单位矢量,所以可以通过xy计算z
         
           fixed3 bump = UnpackNormal(packedNormal);  
           bump.xy *=_BumpScale;
           bump.z = sqrt(1.0-saturate(dot(bump.xy,bump.xy)));
           bump = normalize(half3(dot(i.Trow0.xyz,bump),dot(i.Trow1.xyz,bump),dot(i.Trow2.xyz,bump)));   //法线坐标转化 通过点乘实现3x3 * 3*1
           
           fixed3 albedo = tex2D(_MainTex,i.uv).rgb*_Color.rgb;
           fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
           fixed3 diffuse = _LightColor0.rgb*albedo*max(0,dot(bump,LightDir));

           fixed3 halfDir = normalize(LightDir+ViewDir);

           fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(0,dot(bump,halfDir)),_Gloss);

           return fixed4(ambient+diffuse+specular,1.0);
        }
        ENDCG
        }
    }
    FallBack "Diffuse"
}

视觉上两个空间的计算并没有什么差别。

posted @ 2021-12-05 21:36  MATSUろ  阅读(96)  评论(0)    收藏  举报