标签:
存储法线的一张贴图,法线的 xyz 的值被映射成为对应的 RGB 值。即法线的分量由(-1,1)映射成(0,127)。法线贴图一般呈蓝色,因为大多数朝向 (0,0,1)的法线被映射成为了 (0,0,127)。
切空间是在某一点所有的切向量组成的线性空间。也就是说,在模型每个顶点中,都存在这样的一个切空间坐标系,以模型顶点为圆心,再加上TBN3个轴(Tangent,Binormal,Normal),N是顶点的法线方向,T、B两个向量是顶点切平面的2个向量,一般T的方向是纹理坐标u的方向,B的方向通过TN叉乘计算得到。而法线贴图就是记录该空间下顶点的法线方向,它不是固定(0,0,1)而是在切空中间中的扰动值。现在,我们要将切空间下的法线值,变换到世界空间,计算光照。
首先,我们需要计算出切空间到世界空间的变换矩阵。切空间由3个向量定义,Tangent,Binormal,Normal;我们在模型顶点中已知Tangent和Normal的值,那么Binormal可以通过前2个向量的叉乘来取得。
1 struct vertexInput{ 2 float4 vertex:POSITION; 3 float3 normal:NORMAL; 4 float4 texcoord:TEXCOORD0; 5 float4 tangent:TANGENT; 6 };
计算世界空间的Normal: o.normalWorld = normalize( mul(float4(v.normal,0.0),_World2Object).xyz); 法线转世界空间不同于顶点,需要解决缩放问题。
计算世界空间的Tangent: o.tangentWorld = normalize( mul(_Object2World, v.tangent).xyz);
计算世界空间的Binormal: o.binormalWorld = normalize( cross(o.normalWorld,o.tangentWorld)*v.tangent.w); 叉乘:右手定则。
有了这3个切向量,我们就可以定义变换矩阵:
1 float3x3 local2WorldTranspose=float3x3( 2 i.tangentWorld, 3 i.binormalWorld, 4 i.normalWorld 5 );
三个向量构建出了的矩阵,而这三个向量分别对应了World Space中的tangent、binormal和normal的方向,这三个方向对应了Tangent Space中的三个坐标轴的方向。这里有一点点难懂。但其实就是一个坐标变换,如果我们想得到从坐标系A转换到坐标系B的一个变换矩阵,我们只需用A中B的三个坐标轴的方向、按X、Y、Z轴的顺序构建一个矩阵即可(这里需要注意是行向量还是列向量,例如OpenGL使用的是列向量)。
最后将采样的法线纹理通过该转换矩阵,转换到世界坐标,进行光照计算即可。
1. 在属性中声明:
_BumpMap ("Normal Texture", 2D) = "bump" {} _BumpDepth("_Bump Depth",Range(-2,2.0)) = 1
2. Pass中声明变量:
float4 _BumpMap_ST; uniform float _BumpDepth;
3. 在 Vertex 函数中计算世界空间的TBN:
o.normalWorld = normalize( mul(float4(v.normal,0.0),_World2Object).xyz); o.tangentWorld = normalize( mul(_Object2World, v.tangent).xyz); o.binormalWorld = normalize( cross(o.normalWorld,o.tangentWorld)*v.tangent.w);
4. 在 Fragment 函数中构建切空间到世界空间的转换矩阵:
float3x3 local2WorldTranspose=float3x3(
i.tangentWorld,
i.binormalWorld,
i.normalWorld
);
5. 在 Fragment 函数中采样法线贴图,转换到[-1,1],并转换到世界空间:
float4 texN = tex2D(_BumpMap,i.tex.xy*_BumpMap_ST.xy+_BumpMap_ST.zw);
float3 localCoords = float3(2.0*texN.ag-float2(1.0,1.0),0.0); localCoords.z = _BumpDepth;
float3 normalDirection = normalize(mul(local2WorldTranspose,localCoords));
Unity采用的压缩方式是DXT5nm。有A和G两个通道。对于法线(x,y,z) A对应x,G对应y。范围依然是[0,1], 依然需要转换至[-1,1]。
6. 计算光照,并输出:
float3 diffuseReflection = saturate( dot(normalDirection,lightDirection)); return float4(texN.xyz*diffuseReflection,1.0);
源代码:
1 Shader "JQM/NoamalMap_1" 2 { 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 _BumpMap ("Normal Texture", 2D) = "bump" {} 7 _BumpDepth("_Bump Depth",Range(-2,2.0)) = 1 8 } 9 10 SubShader 11 { 12 13 Pass 14 { 15 Tags { "LightMode"="ForwardBase" } 16 17 CGPROGRAM 18 #pragma vertex vert 19 #pragma fragment frag 20 //#pragma exclude_renderers flash //给指定平台编译 21 22 #include "UnityCG.cginc" 23 24 //使用自定义变量 25 sampler2D _MainTex; 26 float4 _MainTex_ST; 27 sampler2D _BumpMap; 28 float4 _BumpMap_ST; 29 uniform float _BumpDepth; 30 31 //使用Unity定义的变量 32 uniform float4 _LightColor0; 33 34 //输入结构体 35 struct vertexInput{ 36 float4 vertex:POSITION; 37 float3 normal:NORMAL; 38 float4 texcoord:TEXCOORD0; 39 float4 tangent:TANGENT; 40 }; 41 42 //输出结构体 43 struct vertexOutput{ 44 float4 pos:SV_POSITION; 45 float4 tex:TEXCOORD0; 46 float4 posWorld:TEXCOORD1; 47 float3 normalWorld:TEXCOORD2; 48 float3 tangentWorld:TEXCOORD3; 49 float3 binormalWorld:TEXCOORD4; 50 }; 51 52 vertexOutput vert (vertexInput v) 53 { 54 vertexOutput o; 55 56 o.normalWorld = normalize( mul(float4(v.normal,0.0),_World2Object).xyz); 57 o.tangentWorld = normalize( mul(_Object2World, v.tangent).xyz); 58 o.binormalWorld = normalize( cross(o.normalWorld,o.tangentWorld)*v.tangent.w); 59 60 o.posWorld = mul(_Object2World, v.vertex); 61 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 62 o.tex = v.texcoord; 63 64 return o; 65 } 66 67 fixed4 frag (vertexOutput i) : COLOR 68 { 69 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz- i.posWorld.xyz); 70 float3 lightDirection; 71 float atten; 72 73 if(_WorldSpaceLightPos0.w==0.0)//平行光 74 { 75 atten = 1.0; 76 lightDirection = normalize(_WorldSpaceLightPos0.xyz); 77 } 78 else 79 { 80 float3 fragmentToLightSource = _WorldSpaceLightPos0.xyz -i.posWorld.xyz; 81 float distance = length(fragmentToLightSource); 82 atten = 1.0/distance; 83 lightDirection = normalize(fragmentToLightSource); 84 } 85 86 //Texture Map 87 float4 tex = tex2D(_MainTex,i.tex.xy*_MainTex_ST.xy+_MainTex_ST.zw); 88 float4 texN = tex2D(_BumpMap,i.tex.xy*_BumpMap_ST.xy+_BumpMap_ST.zw); 89 90 //UnpackNormal [0,1] 转换成[-1,1] 91 float3 localCoords = float3(2.0*texN.ag-float2(1.0,1.0),0.0); 92 localCoords.z = _BumpDepth; 93 94 //normal transpose matrix 95 float3x3 local2WorldTranspose=float3x3( 96 i.tangentWorld, 97 i.binormalWorld, 98 i.normalWorld 99 ); 100 101 102 ////calculate normal direction 103 float3 normalDirection = normalize(mul(local2WorldTranspose,localCoords)); 104 ////灯光 105 float3 diffuseReflection = saturate( dot(normalDirection,lightDirection)); 106 107 108 return float4(texN.xyz*diffuseReflection,1.0); 109 110 111 } 112 ENDCG 113 } 114 } 115 }
纹理+材质颜色+高光+边缘光+漫反射+环境光+法线贴图:
源代码:
1 Shader "JQM/NoamalMap" 2 { 3 Properties 4 { 5 _Color("Color", color) = (1.0,1.0,1.0,1.0) 6 _MainTex ("Texture", 2D) = "white" {} 7 _BumpMap ("Normal Texture", 2D) = "bump" {} 8 _BumpDepth("_Bump Depth",Range(-2,2.0)) = 1 9 _SpecColor("Specular Color", color) = (1.0,1.0,1.0,1.0) 10 _Shininess("Shininess",float) = 10 11 _RimColor("Rim Coloe Color", color) = (1.0,1.0,1.0,1.0) 12 _RimPower("Rim Power",Range(0.1,10.0)) = 3.0 13 } 14 15 SubShader 16 { 17 18 Pass 19 { 20 Tags { "LightMode"="ForwardBase" } 21 22 CGPROGRAM 23 #pragma vertex vert 24 #pragma fragment frag 25 //#pragma exclude_renderers flash //给指定平台编译 26 27 #include "UnityCG.cginc" 28 29 //使用自定义变量 30 sampler2D _MainTex; 31 float4 _MainTex_ST; 32 sampler2D _BumpMap; 33 float4 _BumpMap_ST; 34 uniform float4 _Color; 35 uniform float4 _SpecColor; 36 uniform float4 _RimColor; 37 uniform float _Shininess; 38 uniform float _RimPower; 39 uniform float _BumpDepth; 40 41 //使用Unity定义的变量 42 uniform float4 _LightColor0; 43 44 //输入结构体 45 struct vertexInput{ 46 float4 vertex:POSITION; 47 float3 normal:NORMAL; 48 float4 texcoord:TEXCOORD0; 49 float4 tangent:TANGENT; 50 }; 51 52 //输出结构体 53 struct vertexOutput{ 54 float4 pos:SV_POSITION; 55 float4 tex:TEXCOORD0; 56 float4 posWorld:TEXCOORD1; 57 float3 normalWorld:TEXCOORD2; 58 float3 tangentWorld:TEXCOORD3; 59 float3 binormalWorld:TEXCOORD4; 60 }; 61 62 vertexOutput vert (vertexInput v) 63 { 64 vertexOutput o; 65 66 o.normalWorld = normalize( mul(float4(v.normal,0.0),_World2Object).xyz); 67 o.tangentWorld = normalize( mul(_Object2World, v.tangent).xyz); 68 o.binormalWorld = normalize( cross(o.normalWorld,o.tangentWorld)*v.tangent.w); 69 70 o.posWorld = mul(_Object2World, v.vertex); 71 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 72 o.tex = v.texcoord; 73 74 return o; 75 } 76 77 fixed4 frag (vertexOutput i) : COLOR 78 { 79 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz- i.posWorld.xyz); 80 float3 lightDirection; 81 float atten; 82 83 if(_WorldSpaceLightPos0.w==0.0)//平行光 84 { 85 atten = 1.0; 86 lightDirection = normalize(_WorldSpaceLightPos0.xyz); 87 } 88 else 89 { 90 float3 fragmentToLightSource = _WorldSpaceLightPos0.xyz -i.posWorld.xyz; 91 float distance = length(fragmentToLightSource); 92 atten = 1.0/distance; 93 lightDirection = normalize(fragmentToLightSource); 94 } 95 96 //Texture Map 97 float4 tex = tex2D(_MainTex,i.tex.xy*_MainTex_ST.xy+_MainTex_ST.zw); 98 float4 texN = tex2D(_BumpMap,i.tex.xy*_BumpMap_ST.xy+_BumpMap_ST.zw); 99 100 //UnpackNormal [0,1] 转换成[-1,1] 101 float3 localCoords = float3(2.0*texN.ag-float2(1.0,1.0),0.0); 102 localCoords.z = _BumpDepth; 103 104 //normal transpose matrix 105 float3x3 local2WorldTranspose=float3x3( 106 i.tangentWorld, 107 i.binormalWorld, 108 i.normalWorld 109 ); 110 111 //calculate normal direction 112 float3 normalDirection = normalize(mul(local2WorldTranspose,localCoords)); 113 //float3 normalDirection = normalize(mul(localCoords,local2WorldTranspose)); 114 115 //灯光 116 float3 diffuseReflection = atten * _LightColor0.xyz * saturate( dot(normalDirection,lightDirection)); 117 float3 specularReflection = atten * _LightColor0.xyz * _SpecColor.rgb*saturate( dot(normalDirection,lightDirection))*pow(saturate(dot(reflect(-lightDirection,normalDirection),viewDirection)),_Shininess); 118 119 //Rim Light 120 float rim= 1-dot(normalize(viewDirection),normalDirection); 121 float3 rimLighting = atten * _LightColor0.xyz * _RimColor.rgb*saturate(dot(normalDirection,lightDirection))*pow(rim,_RimPower); 122 123 float3 lightFinal = rimLighting + diffuseReflection+specularReflection+UNITY_LIGHTMODEL_AMBIENT.xyz; 124 125 126 127 return float4(tex*lightFinal*_Color.xyz,1.0); 128 129 } 130 ENDCG 131 } 132 133 Pass 134 { 135 Tags { "LightMode"="ForwardAdd" } 136 Blend One One 137 138 CGPROGRAM 139 #pragma vertex vert 140 #pragma fragment frag 141 //#pragma exclude_renderers flash //给指定平台编译 142 143 #include "UnityCG.cginc" 144 145 //使用自定义变量 146 sampler2D _MainTex; 147 float4 _MainTex_ST; 148 sampler2D _BumpMap; 149 float4 _BumpMap_ST; 150 uniform float4 _Color; 151 uniform float4 _SpecColor; 152 uniform float4 _RimColor; 153 uniform float _Shininess; 154 uniform float _RimPower; 155 uniform float _BumpDepth; 156 157 //使用Unity定义的变量 158 uniform float4 _LightColor0; 159 160 //输入结构体 161 struct vertexInput{ 162 float4 vertex:POSITION; 163 float3 normal:NORMAL; 164 float4 texcoord:TEXCOORD0; 165 float4 tangent:TANGENT; 166 }; 167 168 //输出结构体 169 struct vertexOutput{ 170 float4 pos:SV_POSITION; 171 float4 tex:TEXCOORD0; 172 float4 posWorld:TEXCOORD1; 173 float3 normalWorld:TEXCOORD2; 174 float3 tangentWorld:TEXCOORD3; 175 float3 binormalWorld:TEXCOORD4; 176 }; 177 178 vertexOutput vert (vertexInput v) 179 { 180 vertexOutput o; 181 182 o.normalWorld = normalize( mul(float4(v.normal,0.0),_World2Object).xyz); 183 o.tangentWorld = normalize( mul(_Object2World, v.tangent).xyz); 184 o.binormalWorld = normalize( cross(o.normalWorld,o.tangentWorld)*v.tangent.w); 185 186 o.posWorld = mul(_Object2World, v.vertex); 187 o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 188 o.tex = v.texcoord; 189 190 return o; 191 } 192 193 fixed4 frag (vertexOutput i) : COLOR 194 { 195 float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz- i.posWorld.xyz); 196 float3 lightDirection; 197 float atten; 198 199 if(_WorldSpaceLightPos0.w==0.0)//平行光 200 { 201 atten = 1.0; 202 lightDirection = normalize(_WorldSpaceLightPos0.xyz); 203 } 204 else 205 { 206 float3 fragmentToLightSource = _WorldSpaceLightPos0.xyz -i.posWorld.xyz; 207 float distance = length(fragmentToLightSource); 208 atten = 1.0/distance; 209 lightDirection = normalize(fragmentToLightSource); 210 } 211 212 //Texture Map 213 float4 texN = tex2D(_BumpMap, i.tex.xy*_BumpMap_ST.xy+_BumpMap_ST.zw); 214 215 //UnpackNormal 216 float3 localCoords = float3(2.0*texN.ag-float2(1.0,1.0),0.0); 217 localCoords.z = _BumpDepth; 218 219 //normal transpose matrix 220 float3x3 local2WorldTranspose=float3x3( 221 i.tangentWorld, 222 i.binormalWorld, 223 i.normalWorld 224 ); 225 226 //calculate normal direction 227 float3 normalDirection = normalize(mul(local2WorldTranspose,localCoords)); 228 //float3 normalDirection = normalize(mul(localCoords, local2WorldTranspose)); 229 230 //灯光 231 float3 diffuseReflection = atten * _LightColor0.xyz * saturate( dot(normalDirection,lightDirection)); 232 float3 specularReflection = diffuseReflection * _SpecColor.rgb*saturate( dot(normalDirection,lightDirection))*pow(saturate(dot(reflect(-lightDirection,normalDirection),viewDirection)),_Shininess); 233 234 //Rim Light 235 float rim= 1-dot(normalize(viewDirection),normalDirection); 236 float3 rimLighting = _LightColor0.xyz * _RimColor.rgb*saturate(dot(normalDirection,lightDirection))*pow(rim,_RimPower); 237 238 float3 lightFinal = rimLighting + diffuseReflection+specularReflection; 239 240 return float4(lightFinal*_Color.xyz,1.0); 241 } 242 ENDCG 243 } 244 245 } 246 }
标签:
原文地址:http://www.cnblogs.com/jqm304775992/p/4898550.html