切线空间,即使用顶点的切线作为x轴,法线作为z轴,法线与切线的叉积作为y轴。
使用切线空间存储法线,使得法线纹理可以复用,很好。
在切线空间中计算光照,比在世界空间中计算光照少了很多计算量。在切线空间中计算,需要在顶点中将光线和视角方向转换到切线空间中,而在世界空间中计算时需要在每个片段中将法线从切线空间转换到界面空间。
shader如下:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Custom/Normal map tangent space"{ Properties { _MainTex("Main texture", 2D) = "white" _NormalMap("Normal map", 2D) = "bump" _Gloss("Gloss", float) = 8 } SubShader { Pass { Tags { "LightMode" = "ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct v2f { float4 pos : SV_POSITION; float4 uv : TEXCOORD0; float3 tangentLightDir : TEXCOORD1; float3 tangentViewDir : TEXCOORD2; }; v2f vert(appdata_tan v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; TANGENT_SPACE_ROTATION; float3 objLightDir = ObjSpaceLightDir(v.vertex); o.tangentLightDir = normalize(mul(rotation, objLightDir)); float3 objViewDir = ObjSpaceViewDir(v.vertex); o.tangentViewDir = normalize(mul(rotation, objViewDir)); return o; } sampler2D _MainTex; sampler2D _NormalMap; float _Gloss; float4 frag(v2f i) : SV_TARGET { float4 tex = tex2D(_MainTex, i.uv); float4 normalTex = tex2D(_NormalMap, i.uv); float3 tangentNormal = UnpackNormal(normalTex); // 切线空间 float3 diff = tex.rgb * _LightColor0.rgb * (dot(tangentNormal, i.tangentLightDir) * 0.5 + 0.5); float3 halfDir = i.tangentViewDir + i.tangentLightDir; halfDir = normalize(halfDir); float3 spec = tex.rgb * _LightColor0.rgb * pow(saturate(dot(halfDir, tangentNormal)), _Gloss); float3 col = spec + diff + UNITY_LIGHTMODEL_AMBIENT.rgb; return float4(col, 1); } ENDCG } }}
转载请注明出处:
效果如下:
资源如下: