大家好,欢迎来到IT知识分享网。
法线贴图 (Normal Map) 是一种凹凸贴图 (Bump Map)。它们是一种特殊的纹理,可让您将表面细节(如凹凸、凹槽和划痕)添加到模型,从而捕捉光线,就像由真实几何体表示一样。
获取源码
法线贴图原理
在模型制作中,我们可以真实的去制作出凹凸感,但是这样会增加模型的面数,增加性能。那么有什么办法,不改模型的面数,就能出现凹凸感呢,那便是使用法线贴图,使用一个2D纹理来储存法线数据。
光照到物体上再通过反射光到人眼,当有凹凸面时,那么反射光线与平面是不一样的,从而产生凹凸感。而反射光线跟物体的法线有关,如果我们修改法线方向,那么反射的光线也会随之改变,当照射到人眼时,便会产生凹凸的感觉,也就模拟了真实的凹凸物体。
现实中我们无法做到,但是在计算机中,我们就可以做到,通过计算,实现一种模拟的凹凸感,用一张2D纹理来存储我们的法线数据,来修复模型的法线,从而实现凹凸的感觉。
在切线空间中,法线的方向使用z轴来表示,法线方向为(0,0,1)。法线向量从z轴方向往其他方向偏移,即修改x,y的值,法线向量方向便发生了变化,同时再经过光照计算得到反射光方向也发生了偏移,便产生了凹凸感。此时通过2d纹理如何来表示这种改变呢?由于法线的范围为-1~1,而颜色的范围为0 ~1,经过下面公式计算得到颜色值
vec3 rgb_normal = (normal + 1)/2; // 从 [-1,1] 转换至 [0,1]
vec3 normal = (rgb_normal)*2-1 从 [0,1]转换至 [-1,1]
实际应用
fixed3 normalDir = UnpackNormal(noramlColor);
思考:那么为什么我们不直接使用 normal = (rgb_normal)*2-1
的计算方法获得法线方向呢?因为无法得到正确的结果。
查看源码看看UnpackNormal方法
inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal) { fixed3 normal; normal.xy = packednormal.wy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1) // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5 fixed3 UnpackNormalmapRGorAG(fixed4 packednormal) { // This do the trick packednormal.x *= packednormal.w; fixed3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } inline fixed3 UnpackNormal(fixed4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #elif defined(UNITY_ASTC_NORMALMAP_ENCODING) return UnpackNormalDXT5nm(packednormal); #else return UnpackNormalmapRGorAG(packednormal); #endif }
当我们把纹理类型设置成Normal map时,Unity根据不同平台对纹理进行压缩(例如使用DXT5nm格式),从而减少减少法线纹理占用的内存空间。UnpackNormal函数内根据不同的压缩格式进行了判断,通过UnpackNormal函数来针对不同的压缩格式对法线纹理进行正确的采样。如源码中的UnpackNormalDXT5nm
、UnpackNormalmapRGorAG
函数。
完整代码:
Shader "My/tietu2" { Properties { _MainTex("Main Tex",2D) = "white"{} _NormalMap("Normal Map",2D) = "bump"{} _Range("Range",Range(0,1)) = 0.5 } SubShader { Tags{"LightMode" = "ForwardBase" } Pass{ CGPROGRAM #include "Lighting.cginc" #pragma vertex vert; #pragma fragment frag; float _Range; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalMap; float4 _NormalMap_ST; struct a2v { float4 vertex:POSITION; float4 texcoord:TEXCOORD0; float3 normal:NORMAL; float4 tangent:TANGENT; }; struct v2f { float4 uv:TEXCOORD0; float4 svPos:SV_POSITION; float3 normal:TEXCOORD1; float3 lightDir:TEXCOORD2; }; v2f vert(a2v v) { v2f f; f.svPos = UnityObjectToClipPos(v.vertex); f.uv.xy = v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; f.uv.zw = v.texcoord.xy*_NormalMap_ST.xy+_NormalMap_ST.zw;; // f.normal = UnityObjectToWorldNormal(v.normal); TANGENT_SPACE_ROTATION;//调用之后,会得到一个矩阵rotation,这个矩阵用来把模型空间下的方向转换为切线空间 f.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex));//把光从模型空间,转为切线空间 return f; } fixed4 frag(v2f f):SV_Target{ fixed3 texColor = tex2D(_MainTex,f.uv.xy); half4 noramlColor = tex2D(_NormalMap,f.uv.zw); fixed3 normalDir = UnpackNormal(noramlColor); normalDir = normalize(normalDir); fixed3 lightDir = normalize(f.lightDir); fixed3 texColo = _LightColor0.rgb*texColor*max(0,dot(normalDir,lightDir)*0.5+0.5); fixed3 color = texColo+UNITY_LIGHTMODEL_AMBIENT.rgb; return fixed4(color,1); } ENDCG } } FallBack "Diffuse" }
调整凹凸感
fixed3 normalDir = UnpackNormal(noramlColor); normalDir.xy = normalDir.xy*_BumpScale; //(dot(xy,xy))=x*x+y*y //由于偏移后的法线是归一化的,因此满足x2 + y2 + z2 = 1 //所以z=sqrt(1-(x2+y2)) normalDir.z = sqrt(1.0 - saturate(dot(normalDir.xy,normalDir.xy))); normalDir = normalize(normalDir);
Shader "My/tietu3" { Properties { _MainTex("Main Tex",2D) = "white"{} _NormalMap("Normal Map",2D) = "bump"{} _BumpScale("Bump Scale",Float) =1 } SubShader { Tags{"LightMode" = "ForwardBase" } Pass{ CGPROGRAM #include "Lighting.cginc" #pragma vertex vert; #pragma fragment frag; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalMap; float4 _NormalMap_ST; float _BumpScale; struct a2v { float4 vertex:POSITION; float4 texcoord:TEXCOORD0; float3 normal:NORMAL; float4 tangent:TANGENT; }; struct v2f { float4 uv:TEXCOORD0; float4 svPos:SV_POSITION; float3 normal:TEXCOORD1; float3 lightDir:TEXCOORD2; }; v2f vert(a2v v) { v2f f; f.svPos = UnityObjectToClipPos(v.vertex); f.uv.xy = v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; f.uv.zw = v.texcoord.xy*_NormalMap_ST.xy+_NormalMap_ST.zw;; // f.normal = UnityObjectToWorldNormal(v.normal); TANGENT_SPACE_ROTATION;//调用之后,会得到一个矩阵rotation,这个矩阵用来把模型空间下的方向转换为切线空间 f.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex));//把光从模型空间,转为切线空间 return f; } fixed4 frag(v2f f):SV_Target{ fixed3 texColor = tex2D(_MainTex,f.uv.xy); half4 noramlColor = tex2D(_NormalMap,f.uv.zw); fixed3 normalDir = UnpackNormal(noramlColor); normalDir.xy = normalDir.xy*_BumpScale; //(dot(xy,xy))=x*x+y*y //由于偏移后的法线是归一化的,因此满足x2 + y2 + z2 = 1 //所以z=sqrt(1-(x2+y2)) normalDir.z = sqrt(1.0 - saturate(dot(normalDir.xy,normalDir.xy))); normalDir = normalize(normalDir); fixed3 lightDir = normalize(f.lightDir); fixed3 texColo = texColor*max(0,dot(normalDir,lightDir)*0.5+0.5); fixed3 color = texColo+UNITY_LIGHTMODEL_AMBIENT.rgb; return fixed4(color,1); } ENDCG } } FallBack "Diffuse" }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/132927.html