法线贴图,对主纹理凹凸显示
建模原理
法线贴图:切线空间,存储xy切线,映射法线,法线信息存储在切线空间中。
模型是否凹凸,是由模型顶点决定的,现在实现的法线贴图,控制凹凸,实际上是配合
光照实现的,凹进去的部分,颜色偏暗,突出来的部分,颜色偏亮。
导入法线贴图
贴图类型转换为Normal Map,法线纹理类型。
法线贴图计算
法线贴图:存储有与法线垂直的切线信息,切线信息存储在切线空间中,使用内部
值(x切线,y切线)时,需要将法线转换到世界空间中,再进行光照运算才能得到
正确的结果。调整凹凸深度的参数:用户可配置,可以控制法线长短。
Shader实现
加载两张纹理:主纹理,光照法线纹理(切线空间存储数据)
顶点着色器:
主纹理的UV偏移计算
法线纹理的UV偏移计算
计算切线空间到世界空间的转换矩阵(可变),用于变换光照法线
片元着色器
解压主纹理
解压法线纹理,根据切线信息,转换光照法线信息,将光照法线从切线空间,
转到世界空间
拿法线纹理算出的光照法线,做光照运算。
相关实现代码示例如下所示:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "CreateTest/PhongNormalTexture"
{Properties{//用于显示材质纹理_MainTex("主纹理",2D)="white"{}//用于和主纹理混色_Color("混色",Color)=(1,1,1,1)//法线纹理_BumpTex("法线纹理",2D)="bump"{}//法线深度系数,可以控制法线高度_BumpScale("法线深度系数",Float) = 1_SpecularColor("高光反射材质颜色",Color) = (1,1,1,1)_Gloss("光晕系数",Range(8,256)) = 10}SubShader{Tags{"LightMode" = "ForwardBase"}Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"//导入主纹理数据sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;//导入法线信息sampler2D _BumpTex;float4 _BumpTex_ST;float _BumpScale;//导入高光信息fixed4 _SpecularColor;float _Gloss;//从CPU过来的数据struct c2v{float4 vertex:POSITION;//从CPU传递过来的模型空间下,需要渲染的点float4 texcoord : TEXCOORD0; //因为两张贴图的像素,除颜色外完全重叠,所以纹理坐标点可以通用float4 tangent:TANGENT; //光照法线纹理,因为要计算切线空间下的信息,所以需要渲染点的切线信息float3 normal:NORMAL;};struct v2f{float4 pos:SV_POSITION; //模型空间到裁剪空间转换后的点float4 uv:TEXCOORD1; //因为要算出两张纹理的uv坐标,所以做一个float4,xy存储主纹理UV,zw存储法线纹理UVfloat4 MatrixRowOne:TEXCOORD2; //用于传递从顶点着色器计算好的转换矩阵float4 MatrixRowTwo:TEXCOORD3;float4 MatrixRowThree:TEXCOORD4;};v2f vert(c2v data){v2f r;r.pos = UnityObjectToClipPos(data.vertex);//两张纹理的缩放和偏移可能不同,所以分别计算uv偏移信息,存储在v2f.uvr.uv.xy = data.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;r.uv.zw = data.texcoord.xy * _BumpTex_ST.xy + _BumpTex_ST.zw;//CPU传递过来的切线存储在模型空间下//法线纹理中的,光照法线推算信息,是存储在切线空间下的//CPU传递过来的切线信息与法线纹理中的切线信息,有转换关系,所以可以推算出一个转换矩阵用于切换法线//计算出来的转换矩阵应该是从(切线空间到模型空间)的转换//但是我们需要的是计算(从切线空间到世界空间)的转换矩阵(因为最终的光照运算,需要在世界空间中完成)//所以应该先把CPU传递过来的切线信息,转换到世界空间下//再计算切线的转换矩阵,这时就能得到从(切线空间,到世界空间)的转换矩阵//拥有了转换矩阵,就可以将法线纹理中,求解的法线信息,从切线空间,转换到世界空间下,进而可以计算光照//世界坐标系下的点的位置(片元着色器计算光照需求)float4 worldPos = mul(unity_ObjectToWorld, data.vertex);//世界空间下渲染点的法线信息float3 worldNormal = mul((float3x3)unity_ObjectToWorld, data.normal);//世界空间下切线的方向向量float3 worldTangent = mul((float3x3)unity_ObjectToWorld, data.tangent.xyz);//世界空间下计算与切线和法线垂直的线的方向向量(用于计算转换矩阵)float3 worldBinormal=cross(worldNormal, worldTangent)* data.tangent.w;//需要将转换矩阵,传递给片元着色器,用于转换切线空间下的法线到世界空间中r.MatrixRowOne = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);r.MatrixRowTwo = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);r.MatrixRowThree = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);return r;}fixed4 frag(v2f data) :SV_Target{//世界空间下的点float3 worldPos = float3(data.MatrixRowOne.w,data.MatrixRowTwo.w,data.MatrixRowThree.w);//计算法线纹理中法线信息(重点),解出的法线在切线空间//解法线前,需要先对法线纹理贴图进行采样fixed3 bump = UnpackNormal(tex2D(_BumpTex, data.uv.zw));//通过缩放值,影响凹凸感bump.xy *= _BumpScale;//计算法线高度(数学公式)//法线还没有转换空间,所以计算出的法线,还在切线空间下bump.z = sqrt(1 - max(0, dot(bump.xy, bump.xy)));//通过顶点着色器传递过来的转换矩阵,转换法线,从切线空间到世界空间bump = float3(dot(data.MatrixRowOne.xyz, bump), dot(data.MatrixRowTwo.xyz, bump), dot(data.MatrixRowThree.xyz, bump));//解主纹理fixed4 texColor = tex2D(_MainTex, data.uv.xy) * _Color;//计算漫发射光照fixed3 diffuse = _LightColor0.rgb * texColor.rgb * max(0, dot(normalize(bump), normalize(_WorldSpaceLightPos0.xyz)));//计算高光反射光照fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos);fixed3 refDir = normalize(reflect(-_WorldSpaceLightPos0.xyz, normalize(bump)));fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, refDir)), _Gloss);//Phong光照运算fixed3 color = UNITY_LIGHTMODEL_AMBIENT.xyz * texColor.rgb + diffuse + specular;return fixed4(color, 1);}ENDCG}}Fallback "Diffuse"
}
其实现效果如下图:
左侧为Standard的shader效果,右侧为上面代码下的shader效果,右侧较左边多了高光反射的相应数据,考虑到砖墙等一些粗糙表面现实情况下不会有如此明显的高光,应用此shader文件的同学可以将Phong光照模型的公式中的高光反射部分去除,在其他需要高光反射的情况下再进行视情况添加即可,法线贴图下第一个属性值为法线深度系数(数值0.6处,由于Shader文件未设置成UTF-8导致的中文乱码,读者可自行设置Shader文件的格式或采用英文命名),可通过调节系数对应加强法线纹理效果。
以下内容作者提供一个网址,方便读者下载Amplify Shader Editor,可找到该插件的网址如下:
amplify_shader_pack unity3D_游戏3d模型 免费下载 - 爱给网
插件中提供了很多较高质量动态Shader,有需要的可自行下载。
例:
导入Unity包后即可使用该可视化Shader编辑器,使用的Unity包例:
使用编辑器的过程:
若两边窗口未展开,点击左右上角的银灰色方框即可展开,部分使用示例如下:
更改Shader名称:
添加并编辑某一属性:(以Texture Sample [T]为例)
实现效果如图:
左侧黄色按钮,黄色为未保存,单击使其变为绿色,则成功保存期间的设置。
剩余属性可自行探索。
实现上面代码的功能对应在可视化编辑器中的操作结果如下所示:
其在检查器窗口的情况如下:
该系列专栏为网课课程笔记,仅用于学习参考。