Unity Shader 漫反射的实现
模型的漫反射可以在两个函数中实现,一个是顶点函数,另外一个就是片元函数。而这两个函数的区别又决定了漫反射实现出来的效果,那就是精细度。
因为顶点函数是逐顶点调用,漫反射在顶点函数实现时,对于在一个三角面(三个顶点包含的面)中的像素值是通过插值得到的。所以模型显示的每个像素不是最细化的。
而片元函数是逐像素调用的,若漫反射在片元函数中调用,则会仔细涉及到每个像素,漫反射出来的效果也会更好一些。
下面我就直接放两种Shader代码的实现了。在这里我在片元函数的漫反射直接使用了半兰伯特光照模型。
顶点函数的漫反射(兰伯特光照模型)
1 // 顶点函数漫反射的编写 2 // 兰伯特光照模型 3 // Diffuse = 直射光颜色 * max(cos(反射光,法线),0) 4 Shader "TMoon/02-Diffuse Vertex" { 5 Properties{ 6 _Diffuse("Diffuse Color",Color) = (1,1,1,1) 7 } 8 9 SubShader{ 10 11 Pass{ 12 13 Tags {"LightMode" = "ForwardBase"} 14 15 CGPROGRAM 16 17 // 类似C#的 using 引用类库或者文件 18 // 这里引用了Unity内置的光照的类库 19 // 配合"LightMode" = "ForwarBase"得到Unity场景中的光照信息 20 #include "Lighting.cginc" 21 22 #pragma vertex vert 23 #pragma fragment frag 24 25 //获得面板参数 26 fixed4 _Diffuse; 27 28 // 这里使用第二种方法传值 结构体 29 // application to vertex 30 // 由应用程序传递给顶点函数的参数 31 struct a2v { 32 float4 vertex : POSITION; //应用程序将模型的顶点坐标填充到vertex 33 float3 normal : NORMAL; //应用程序将模型的顶点法线填充到normal 34 }; 35 36 // vertex to fragment 37 // 由顶点函数传递给片元函数的参数 38 struct v2f { 39 float4 position : SV_POSITION; //模型裁剪空间下的顶点坐标 40 float3 color : COLOR0; //COLOR0,COLOR1...这些类型一般是中介,用于存储和传递数据,有时并没有什么实际意义,就是中介而已。 41 }; 42 43 v2f vert(a2v v) { 44 v2f f; 45 // 将模型顶点坐标变换到裁剪空间的坐标并赋值给v2f.position 46 f.position = mul(UNITY_MATRIX_MVP, v.vertex); 47 48 // UNITY_LIGHTMODEL_AMBIENT用来获取环境光 49 // 获取Unity环境光的颜色值 50 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rag; 51 52 // 将模型顶点的法线转换到空间坐标下,方便一致计算 53 // UnityObjectToWorldNormal 把法线方向 模型空间 ——> 世界空间 54 fixed3 normalDir = normalize(UnityObjectToWorldNormal(v.normal)); 55 56 // _WorldSpaceLightPos0 空间坐标下光的方向 57 // 对于每个顶点来说 光的位置就是光的方向 ,因为光是平行光 58 // 在这里我直接理解为了反射光 59 fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); 60 61 // 兰伯特光照模型 62 // _LightColor0.rab 直射光颜色 63 fixed3 diffuse = _LightColor0.rab * max(dot(normalDir, lightDir),0) * _Diffuse.rgb; 64 65 // 将漫反射加上环境光的影响 66 f.color = diffuse + ambient; 67 68 return f; 69 } 70 71 float4 frag(v2f f) : SV_Target{ 72 return fixed4(f.color,1); 73 } 74 75 ENDCG 76 } 77 } 78 79 Fallback "VertexLit" 80 }
片元函数的漫反射(半兰伯特光照模型)
1 // 片元函数漫反射的编写 细节更多 边缘过渡更顺 可以对比顶点函数漫反射 2 // 半兰伯特光照模型 阴影部分不会全黑,如果全黑对玩家不太友好 可以对比兰伯特光照模型 在02-Diffuse Vertex把环境光去掉即可明显对比 3 // Diffuse = 直射光颜色 * (cos(反射光,法线)*0.5+0.5) 4 Shader "TMoon/03-Diffuse Fragment" { 5 Properties{ 6 _Diffuse("Diffuse Color",Color) = (1,1,1,1) 7 } 8 9 SubShader{ 10 11 Pass{ 12 13 Tags {"LightMode" = "ForwardBase"} 14 15 CGPROGRAM 16 17 // 类似C#的 using 引用类库或者文件 18 // 这里引用了Unity内置的光照的类库 19 // 配合"LightMode" = "ForwarBase"得到Unity场景中的光照信息 20 #include "Lighting.cginc" 21 22 #pragma vertex vert 23 #pragma fragment frag 24 25 //获得面板参数 26 fixed4 _Diffuse; 27 28 // application to vertex 29 // 由应用程序传递给顶点函数的参数 30 struct a2v { 31 float4 vertex : POSITION; //应用程序将模型的顶点坐标填充到vertex 32 float3 normal : NORMAL; //应用程序将模型的顶点法线填充到normal 33 }; 34 35 // vertex to fragment 36 // 由顶点函数传递给片元函数的参数 37 struct v2f { 38 float4 position : SV_POSITION; //模型裁剪空间下的顶点坐标 39 float3 worldNormalDir : COLOR0; //COLOR0,COLOR1...这些类型一般是中介,用于存储和传递数据,有时并没有什么实际意义,就是中介而已。 40 }; 41 42 v2f vert(a2v v) { 43 v2f f; 44 45 // 将模型顶点坐标变换到裁剪空间的坐标并赋值给v2f.position 46 f.position = mul(UNITY_MATRIX_MVP, v.vertex); 47 48 // 将模型顶点的法线转换到空间坐标下,方便一致计算 49 // UnityObjectToWorldNormal 把法线方向 模型空间 ——> 世界空间 50 f.worldNormalDir = normalize(UnityObjectToWorldNormal(v.normal)); 51 52 return f; 53 } 54 55 float4 frag(v2f f) : SV_Target{ 56 57 // 反射光 58 fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); 59 60 // 半兰伯特光照模型 61 fixed3 diffuse = _LightColor0.rab * (dot(f.worldNormalDir, lightDir)*0.5+0.5) * _Diffuse.rgb; 62 63 return fixed4(diffuse,1); 64 } 65 66 ENDCG 67 } 68 } 69 70 Fallback "VertexLit" 71 }
这里通过截图显示一下兰伯特光照模型和半兰伯特光照模型的区别。
通过背面就很明显看得出了。这篇文章就到这里了。