Unity Shader 漫反射的实现

Posted 爱裸奔的小亮亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }
Diffuse Vertex

片元函数的漫反射(半兰伯特光照模型)

 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 }
Diffuse Fragment

这里通过截图显示一下兰伯特光照模型和半兰伯特光照模型的区别。

通过背面就很明显看得出了。这篇文章就到这里了。

以上是关于Unity Shader 漫反射的实现的主要内容,如果未能解决你的问题,请参考以下文章

通过使用Unity Shader实现基础光照效果

Unity shader学习之逐像素漫反射光照模型

Unity Shader 光照模型(基础公式和代码实现)

解读Unity中的CG编写Shader系列7——漫反射

[Unity Shader] 逐顶点光照和逐片元光照

Cg入门16:Fragment shader - 片段级光照