【浅墨Unity3D Shader编程】之十三 单色透明Shader & 标准镜面高光Shader

发表于2016-07-23
评论0 2.6k浏览
  本次更新放出的Shader为透明系列的3个Shader和标准的镜面高光Shader的两个Shader。由易到难,由入门级到应用级,难度梯度合理。
  依然是先放出游戏场景的exe和运行截图。
 

  本期用的模型为妙蛙草。


  OK,直奔主题吧。
 
一、单色透明Shader
  在上篇文章中单色透明的基础上进行改造,加入alpha混合,构成了这篇文章的第一个Shader——单色透明Shader。具体代码如下:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//透明单色Shader 
   
   
Shader "浅墨Shader编程/Volume13/1.SimpleAlphaShader"  
    //------------------------------------【唯一的子着色器】------------------------------------ 
    SubShader 
    {    
        //设置Queue为透明,在所有非透明几何体绘制之后再进行绘制 
        Tags{ "Queue" = "Transparent"
   
        Pass 
        
            //不写入深度缓冲,为了不遮挡住其他物体 
            ZWrite Off 
   
            //选取Alpha混合方式 
            Blend  SrcAlpha SrcAlpha 
            //Blend SrcAlpha OneMinusSrcAlpha 
   
            //===========开启CG着色器语言编写模块============ 
            CGPROGRAM 
   
            //编译指令:告知编译器顶点和片段着色函数的名称 
            #pragma vertex vert  
            #pragma fragment frag 
   
            //--------------------------------【顶点着色函数】----------------------------- 
            // 输入:POSITION语义(坐标位置) 
            // 输出:SV_POSITION语义(像素位置) 
            //--------------------------------------------------------------------------------- 
            float4 vert(float4 vertexPos : POSITION) : SV_POSITION 
            
                //坐标系变换 
                //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 
                return mul(UNITY_MATRIX_MVP, vertexPos); 
            
   
            //--------------------------------【片段着色函数】----------------------------- 
            // 输入:无 
            // 输出:COLOR语义(颜色值) 
            //--------------------------------------------------------------------------------- 
            float4 frag(void) : COLOR 
            
                //返回单色 
                return float4(0.3, 1.0, 0.1, 0.6); 
            
   
            //===========结束CG着色器语言编写模块=========== 
            ENDCG 
        
    
}

  将其施用于材质之上的效果如下:

 

  实景效果如下:
 

二、颜色可以调版单色透明Shader
  老规矩,让颜色可调,来一个Properties属性块,替换掉Hard encoding硬编码的颜色。所以,代码如下:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//颜色可以调版单色透明Shader 
   
   
Shader "浅墨Shader编程/Volume13/2.ColorChangeAlpha" 
    //------------------------------------【属性值】------------------------------------ 
    Properties 
    
        //颜色值 
        _ColorWithAlpha("ColorWithAlpha", Color) = (0.9, 0.1, 0.1, 0.5) 
    
   
    //------------------------------------【唯一的子着色器】------------------------------------ 
    SubShader 
    
        //设置Queue为透明,在所有非透明几何体绘制之后再进行绘制 
        Tags{ "Queue" = "Transparent"
   
        //--------------------------------唯一的通道------------------------------- 
        Pass 
        
            //不写入深度缓冲,为了不遮挡住其他物体 
            ZWrite Off 
   
            //选取Alpha混合方式 
            Blend  SrcAlpha SrcAlpha 
            //Blend SrcAlpha OneMinusSrcAlpha 
   
            //===========开启CG着色器语言编写模块============ 
            CGPROGRAM 
   
            //编译指令:告知编译器顶点和片段着色函数的名称 
            #pragma vertex vert  
            #pragma fragment frag 
   
            //变量声明 
            uniform float4 _ColorWithAlpha; 
   
            //--------------------------------【顶点着色函数】----------------------------- 
            // 输入:POSITION语义(坐标位置) 
            // 输出:SV_POSITION语义(像素位置) 
            //--------------------------------------------------------------------------------- 
            float4 vert(float4 vertexPos : POSITION) : SV_POSITION 
            
                //坐标系变换 
                //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 
                return mul(UNITY_MATRIX_MVP, vertexPos); 
            
   
            //--------------------------------【片段着色函数】----------------------------- 
            // 输入:无 
            // 输出:COLOR语义(颜色值) 
            //--------------------------------------------------------------------------------- 
            float4 frag(void) : COLOR 
            
                //返回自定义的RGBA颜色 
                return _ColorWithAlpha; 
            
   
            //===========结束CG着色器语言编写模块=========== 
            ENDCG 
        
    
}

  将其施用于材质之上的效果如下:


  这边的调色板中,,除了RGB三色,还有Alpha值可以进行调节。
  实景效果如下:

 
 
三、双面双色颜色可以调版透明Shader
  我们可以利用Cull语句,分别在两个Pass中Cull Front和Cull Back,以让材质的正面和反面显示出不同的颜色。代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//双面双色颜色可以调版透明Shader 
   
Shader "浅墨Shader编程/Volume13/3.TwoSideColorChangeAlpha"  
    //------------------------------------【属性值】------------------------------------ 
    Properties 
    
        //正面颜色值 
        _ColorWithAlpha_Front("ColorWithAlpha_Front", Color) = (0.9, 0.1, 0.1, 0.5) 
        //背面颜色值 
        _ColorWithAlpha_Back("ColorWithAlpha_Back", Color) = (0.1, 0.3, 0.9, 0.5) 
    
   
    //------------------------------------【唯一的子着色器】------------------------------------ 
    SubShader 
    
        //设置Queue为透明,在所有非透明几何体绘制之后再进行绘制 
        Tags{ "Queue" = "Transparent"
   
        //------------------------【通道1:渲染正面】------------------------- 
        Pass 
        
            //剔除背面,渲染正面 
            Cull Back 
            //不写入深度缓冲,为了不遮挡住其他物体 
            ZWrite Off  
   
            //选取Alpha混合方式 
            Blend SrcAlpha OneMinusSrcAlpha 
            //Blend  SrcAlpha SrcAlpha 
   
            //===========开启CG着色器语言编写模块============ 
            CGPROGRAM 
   
            //编译指令:告知编译器顶点和片段着色函数的名称 
            #pragma vertex vert  
            #pragma fragment frag 
   
            //变量声明 
            uniform float4 _ColorWithAlpha_Front; 
   
            //--------------------------------【顶点着色函数】----------------------------- 
            // 输入:POSITION语义(坐标位置) 
            // 输出:SV_POSITION语义(像素位置) 
            //--------------------------------------------------------------------------------- 
            float4 vert(float4 vertexPos : POSITION) : SV_POSITION 
            
                //坐标系变换 
                //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 
                return mul(UNITY_MATRIX_MVP, vertexPos); 
            
   
            //--------------------------------【片段着色函数】----------------------------- 
            // 输入:无 
            // 输出:COLOR语义(颜色值) 
            //--------------------------------------------------------------------------------- 
            float4 frag(void) : COLOR 
            
                //返回自定义的RGBA颜色 
                return _ColorWithAlpha_Front; 
            
   
            //===========结束CG着色器语言编写模块=========== 
            ENDCG 
        
   
        //------------------------【通道2:渲染背面】------------------------- 
        Pass 
        
            //剔除正面,渲染背面 
            Cull Front 
   
            //不写入深度缓冲,为了不遮挡住其他物体 
            ZWrite Off 
   
            //选取Alpha混合方式 
            Blend SrcAlpha OneMinusSrcAlpha 
            //Blend  SrcAlpha SrcAlpha 
   
            //===========开启CG着色器语言编写模块============ 
            CGPROGRAM 
   
            //编译指令:告知编译器顶点和片段着色函数的名称 
            #pragma vertex vert  
            #pragma fragment frag 
   
            //变量声明 
            uniform float4 _ColorWithAlpha_Back; 
   
            //--------------------------------【顶点着色函数】----------------------------- 
            // 输入:POSITION语义(坐标位置) 
            // 输出:SV_POSITION语义(像素位置) 
            //--------------------------------------------------------------------------------- 
            float4 vert(float4 vertexPos : POSITION) : SV_POSITION 
            
                //坐标系变换 
                //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 
                return mul(UNITY_MATRIX_MVP, vertexPos); 
            
   
            //--------------------------------【片段着色函数】----------------------------- 
            // 输入:无 
            // 输出:COLOR语义(颜色值) 
            //--------------------------------------------------------------------------------- 
            float4 frag(void) : COLOR 
            
                //返回自定义的RGBA颜色 
                return _ColorWithAlpha_Back; 
            
   
            //===========结束CG着色器语言编写模块=========== 
            ENDCG 
        
    

  将其施用于材质之上的效果如下:

 

  可以看到,有两个可供调节的颜色选项,分别表示材质正面和反面的透明颜色,而材质最终表现出来的透明颜色,是这两种颜色的混合。
在实景表现中,从物体外部和内部,可以看到其显示出了不同的颜色。
  从物体外部看:


  从物体内部看:

 

四、镜面反射(Specular)Shader
  上篇文章中讲到了Diffuse光照(漫反射光照)与实现它的Shader,经常与其相提并论的是Specular光照(镜面反射光照,或称高光)。这里,接着我们来放出镜面反射光照的Shader。其具体的原理翻开任何一本图形学的书都可以找到,这边就不多讲,直接放出详细注释的实现代码:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//镜面反射Shader(specular shader ) 
   
Shader "浅墨Shader编程/Volume13/4.Specular" 
    //------------------------------------【属性值】------------------------------------ 
    Properties 
    
        //主颜色 
        _Color("Main Color", Color) = (1, 1, 1, 1) 
        //镜面反射颜色 
        _SpecColor("Specular Color", Color) = (1, 1, 1, 1) 
        //镜面反射光泽度 
        _SpecShininess("Specular Shininess", Range(1.0, 100.0)) = 10.0 
    
   
    //------------------------------------【唯一的子着色器】------------------------------------ 
    SubShader 
    
        //渲染类型设置:不透明 
        Tags{ "RenderType" = "Opaque"
   
   
        //--------------------------------唯一的通道------------------------------- 
        Pass 
        
            //光照模型ForwardBase 
            Tags{ "LightMode" = "ForwardBase"
            //===========开启CG着色器语言编写模块=========== 
            CGPROGRAM 
   
            //编译指令:告知编译器顶点和片段着色函数的名称 
            #pragma vertex vert 
            #pragma fragment frag 
   
            //顶点着色器输入结构 
            struct appdata 
            
                float4 vertex : POSITION;//顶点位置 
                float3 normal : NORMAL;//法线向量坐标 
            }; 
   
            //顶点着色器输出结构 
            struct v2f 
            
                float4 pos : SV_POSITION;//像素位置 
                float3 normal : NORMAL;//法线向量坐标 
                float4 posWorld : TEXCOORD0;//在世界空间中的坐标位置 
            }; 
   
   
            //变量的声明 
            float4 _LightColor0; 
            float4 _Color; 
            float4 _SpecColor; 
            float _SpecShininess; 
   
            //--------------------------------【顶点着色函数】----------------------------- 
            // 输入:顶点输入结构体 
            // 输出:顶点输出结构体 
            //--------------------------------------------------------------------------------- 
            //顶点着色函数 
            v2f vert(appdata IN) 
            
                //【1】声明一个输出结构对象 
                v2f OUT; 
   
                //【2】填充此输出结构 
                //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 
                OUT.pos = mul(UNITY_MATRIX_MVP, IN.vertex); 
                //获得顶点在世界空间中的位置坐标 
                OUT.posWorld = mul(_Object2World, IN.vertex); 
                //获取顶点在世界空间中的法线向量坐标 
                OUT.normal = mul(float4(IN.normal, 0.0), _World2Object).xyz; 
   
                //【3】返回此输出结构对象 
                return OUT; 
            
   
            //--------------------------------【片段着色函数】----------------------------- 
            // 输入:顶点输出结构体 
            // 输出:float4型的像素颜色值 
            //--------------------------------------------------------------------------------- 
            fixed4 frag(v2f IN) : COLOR 
            
                //【1】先准备好需要的参数 
                //获取法线的方向 
                float3 normalDirection = normalize(IN.normal); 
                //获取入射光线的方向 
                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); 
                //获取视角方向 
                float3 viewDirection = normalize(_WorldSpaceCameraPos - IN.posWorld.xyz); 
   
                //【2】计算出漫反射颜色值  Diffuse=LightColor * MainColor * max(0,dot(N,L)) 
                float3 diffuse = _LightColor0.rgb * _Color.rgb * max(0.0, dot(normalDirection, lightDirection)); 
   
                //【3】计算镜面反射颜色值  
                float3 specular; 
                //若是法线方向和入射光方向大于180度,镜面反射值为0 
                if (dot(normalDirection, lightDirection) < 0.0) 
                
                    specular = float3(0.0, 0.0, 0.0); 
                
                //否则,根据公式进行计算 Specular =LightColor * SpecColor *pow(max(0,dot(R,V)),Shiness),R=reflect(-L,N) 
                else 
                
                    float3 reflectDirection = reflect(-lightDirection, normalDirection); 
                        specular = _LightColor0.rgb * _SpecColor.rgb * pow(max(0.0, dot(reflectDirection, viewDirection)), _SpecShininess); 
                
   
                //【4】合并漫反射、镜面反射、环境光的颜色值 
                float4 diffuseSpecularAmbient = float4(diffuse, 1.0) + float4(specular, 1.0) + UNITY_LIGHTMODEL_AMBIENT; 
   
                //【5】将漫反射-镜面反射-环境光的颜色值返回 
                return diffuseSpecularAmbient; 
            
   
            //===========结束CG着色器语言编写模块=========== 
            ENDCG 
        
    
}

  将其施用于材质之上的效果如下:


  其中的Specular Shininess滑条用于调节高光的衰减级数。
  OK,我们将颜色调节一下,看看新的效果:

 
 
五、带纹理载入的specular shader
  当然要支持纹理载入,不然实用性太低。Properties中加入纹理属性,其他的地方进行相应的变化,便可以得到支持纹理载入的specular shader:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//支持纹理载入的specular shader  
   
Shader "浅墨Shader编程/Volume13/5.Specular with Shader" 
    //------------------------------------【属性值】------------------------------------ 
    Properties 
    
        //主纹理 
        _MainTex("Texture", 2D) = "white" {} 
        //主颜色 
        _Color("Main Color", Color) = (1, 1, 1, 1) 
        //镜面反射颜色 
        _SpecColor("Specular Color", Color) = (1, 1, 1, 1) 
        //镜面反射光泽度 
        _SpecShininess("Specular Shininess", Range(1.0, 100.0)) = 10.0 
    
   
    //------------------------------------【唯一的子着色器】------------------------------------ 
    SubShader 
    
        //渲染类型设置:不透明 
        Tags{ "RenderType" = "Opaque"
   
        //--------------------------------唯一的通道------------------------------- 
        Pass 
        
            //光照模型ForwardBase 
            Tags{ "LightMode" = "ForwardBase"
            //===========开启CG着色器语言编写模块=========== 
            CGPROGRAM 
   
            //编译指令:告知编译器顶点和片段着色函数的名称 
            #pragma vertex vert 
            #pragma fragment frag 
   
            //顶点着色器输入结构 
            struct appdata 
            
                float4 vertex : POSITION;//顶点位置 
                float3 normal : NORMAL;//法线向量坐标 
                float2 texcoord : TEXCOORD0;//一级纹理坐标 
            }; 
   
            //顶点着色器输出结构 
            struct v2f 
            
                float4 pos : SV_POSITION;//像素位置 
                float3 normal : NORMAL;//法线向量坐标 
                float2 texcoord : TEXCOORD0;//一级纹理坐标 
                float4 posWorld : TEXCOORD1;//在世界空间中的坐标位置 
            }; 
   
            //变量的声明 
            float4 _LightColor0; 
            float4 _Color; 
            sampler2D _MainTex; 
            float4 _SpecColor; 
            float _SpecShininess; 
   
            //--------------------------------【顶点着色函数】----------------------------- 
            // 输入:顶点输入结构体 
            // 输出:顶点输出结构体 
            //--------------------------------------------------------------------------------- 
            //顶点着色函数 
            v2f vert(appdata IN) 
            
                //【1】声明一个输出结构对象 
                v2f OUT; 
   
                //【2】填充此输出结构 
                //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 
                OUT.pos = mul(UNITY_MATRIX_MVP, IN.vertex); 
                //获得顶点在世界空间中的位置坐标 
                OUT.posWorld = mul(_Object2World, IN.vertex); 
                //获取顶点在世界空间中的法线向量坐标 
                OUT.normal = mul(float4(IN.normal, 0.0), _World2Object).xyz; 
                //输出的纹理坐标也就是输入的纹理坐标 
                OUT.texcoord = IN.texcoord; 
   
                //【3】返回此输出结构对象 
                return OUT; 
            
   
            //--------------------------------【片段着色函数】----------------------------- 
            // 输入:顶点输出结构体 
            // 输出:float4型的像素颜色值 
            //--------------------------------------------------------------------------------- 
            fixed4 frag(v2f IN) : COLOR 
            
                //【1】先准备好需要的参数 
                //获取纹理颜色 
                float4 texColor = tex2D(_MainTex, IN.texcoord); 
                //获取法线的方向 
                float3 normalDirection = normalize(IN.normal); 
                //获取入射光线的方向 
                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); 
                //获取视角方向 
                float3 viewDirection = normalize(_WorldSpaceCameraPos - IN.posWorld.xyz); 
   
                //【2】计算出漫反射颜色值  Diffuse=LightColor * MainColor * max(0,dot(N,L)) 
                float3 diffuse = _LightColor0.rgb * _Color.rgb * max(0.0, dot(normalDirection, lightDirection)); 
   
                //【3】计算镜面反射颜色值  
                float3 specular; 
                //若是法线方向和入射光方向大于180度,镜面反射值为0 
                if (dot(normalDirection, lightDirection) < 0.0) 
                
                    specular = float3(0.0, 0.0, 0.0); 
                
                //否则,根据公式进行计算 Specular =LightColor * SpecColor *pow(max(0,dot(R,V)),Shiness),R=reflect(-L,N) 
                else 
                
                    float3 reflectDirection = reflect(-lightDirection, normalDirection); 
                    specular = _LightColor0.rgb * _SpecColor.rgb * pow(max(0.0, dot(reflectDirection, viewDirection)), _SpecShininess); 
                
   
                //【4】合并漫反射、镜面反射、环境光的颜色值 
                float4 diffuseSpecularAmbient = float4(diffuse, 1.0) + float4(specular, 1.0) + UNITY_LIGHTMODEL_AMBIENT; 
   
                //【5】将漫反射-镜面反射-环境光的颜色值乘以纹理颜色值之后返回 
                return diffuseSpecularAmbient * texColor; 
            
   
            //===========结束CG着色器语言编写模块=========== 
            ENDCG 
        
    

  将其施用于材质之上的效果如下:


  将此Shader施于妙蛙草的模型之上,得到的便是如端游《剑灵》一般油腻腻的画风感觉:


  本文Shader的全家福:


  最后,依然是放出加入特效后的场景截图。


  OK,本篇的内容大致如此,下次更新见。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引