Unity3D Shader编程】之十二 可编程Shader初步 & 漫反射可编程Shader的实现

本系列文章由@浅墨_毛星云 出品,转载请注明出处。  
文章链接: http://blog.csdn.net/poem_qianmo/article/details/50812177
作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442
本文工程使用的Unity3D版本: 5.2.1 




好久不见。

从这篇文章开始, Shader系列博文将继续开始更新。且在这次重启,这个系列文章会更多专注于实际Shader的书写,力求推出更多具有特色和实用性的Shader,概念性的东西在原则上上是不会再多讲的。

 

作为可编程Shader系列的第一篇文章,本文将从最简化的可编程Shader开始,逐步变换与实现一个漫反射(也就是实现Lambert光照模型)顶点&片段Shader,可以说,是在讲述一个可编程Shader的进化史。

 

依然是先放出游戏场景的exe和运行截图。

 

 

 

 

【可运行的本文配套exe游戏场景请点击这里下载】






 

一、可编程Shader的书写初步

 



我们知道,在Unity中,Shader可以分成如下三种基本类型:

 

1.固定功能着色器(FixedFunction Shader)

2.表面着色器(SurfaceShader)

3.顶点着色器&片段着色器(Vertex Shader & Fragment Shader)

 

我们也知道,可编程Shader是Unity Shader之中功能最强大、最自由的形态。就让我们开始一个最简单的单色可编程Shader的书写。

 



1.单色Shader的书写



单色Shader算是比较精简的可编程Shader。直接上注释好的代码:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //单色顶点&片段着色器  
  2.   
  3. Shader "浅墨Shader编程/Volume12/1.SimpleShader"  
  4. {  
  5.     //------------------------------------【唯一的子着色器】------------------------------------  
  6.     SubShader  
  7.     {  
  8.         //--------------------------------唯一的通道-------------------------------  
  9.         Pass  
  10.         {  
  11.             //===========开启CG着色器语言编写模块============  
  12.             CGPROGRAM  
  13.   
  14.             //编译指令:告知编译器顶点和片段着色函数的名称  
  15.             #pragma vertex vert  
  16.             #pragma fragment frag  
  17.   
  18.             //--------------------------------【顶点着色函数】-----------------------------  
  19.             // 输入:POSITION语义(坐标位置)  
  20.             // 输出:SV_POSITION语义(像素位置)  
  21.             //---------------------------------------------------------------------------------  
  22.             float4 vert(float4 vertexPos : POSITION) : SV_POSITION  
  23.             {  
  24.                 //坐标系变换  
  25.                 //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
  26.                 return mul(UNITY_MATRIX_MVP,vertexPos);  
  27.             }  
  28.               
  29.             //--------------------------------【片段着色函数】-----------------------------  
  30.             // 输入:无  
  31.             // 输出:COLOR语义(颜色值)  
  32.             //---------------------------------------------------------------------------------  
  33.             float4 frag(void): COLOR  
  34.             {  
  35.                 //返回单色  
  36.                 return float4(0.0, 0.6, 0.8, 1.0);  
  37.             }  
  38.               
  39.             //===========结束CG着色器语言编写模块===========  
  40.             ENDCG  
  41.         }  
  42.     }  
  43. }  


这个Shader也就是先指定一下顶点和片段着色器的名称,然后在顶点着色器中进行一下坐标空间的转换,在片段着色器中直接返回一个固定的蓝色,仅此而已。

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

 

 




2.单色可调Shader的书写

 


仅仅Hard encoding硬编码颜色怎么可以,我们想要可调节,可玩的Shader。这不,来一个Properties属性块,里面定义一个Color属性,然后在片段着色器里替换一下float4型的固定颜色不就好了。所以,代码如下:

 

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //单色可调顶点&片段着色器  
  2.   
  3. Shader "浅墨Shader编程/Volume12/2.ColorChange"  
  4. {  
  5.     //------------------------------------【属性值】------------------------------------  
  6.     Properties  
  7.     {  
  8.         //主纹理  
  9.         _Color("Color", Color) = (1,1,1,1)  
  10.     }  
  11.   
  12.     //------------------------------------【唯一的子着色器】------------------------------------  
  13.     SubShader  
  14.     {  
  15.         //--------------------------------唯一的通道-------------------------------  
  16.         Pass  
  17.         {  
  18.             //===========开启CG着色器语言编写模块============  
  19.             CGPROGRAM  
  20.   
  21.             //指定顶点与片段着色器名称  
  22.             #pragma vertex vert  
  23.             #pragma fragment frag  
  24.   
  25.             //--------------------------------【顶点着色函数】-----------------------------  
  26.             // 输入:POSITION语义(坐标位置)  
  27.             // 输出:SV_POSITION语义(像素位置)  
  28.             //---------------------------------------------------------------------------------  
  29.             float4 vert(float4 vertexPos : POSITION) : SV_POSITION  
  30.             {  
  31.                 //坐标系变换  
  32.                 //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
  33.                 return mul(UNITY_MATRIX_MVP, vertexPos);  
  34.             }  
  35.             uniform float4 _Color;  
  36.   
  37.             //--------------------------------【片段着色函数】-----------------------------  
  38.             // 输入:无  
  39.             // 输出:COLOR语义(颜色值)  
  40.             //---------------------------------------------------------------------------------  
  41.             float4 frag(void) : COLOR  
  42.             {  
  43.                 //返回单色  
  44.                 return _Color;  
  45.             }  
  46.   
  47.             //===========结束CG着色器语言编写模块===========  
  48.             ENDCG  
  49.         }  
  50.     }  
  51. }  

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

 


于是,我们便可以用这边的调色板对此Shader进行颜色的调节。

接下来,看点更有意思的Shader。

 

 

3.RGB Cube Shader



这个是Unity旧版官方文档中出现的一个Shader示例,先上原版RGB Cube的Shader代码,稍后对其进行改造升级:

 

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //RGB立方体  
  2.   
  3. Shader "浅墨Shader编程/Volume12/3.RGB cube"  
  4. {  
  5.     //------------------------------------【唯一的子着色器】------------------------------------  
  6.     SubShader  
  7.     {  
  8.         //--------------------------------唯一的通道-------------------------------  
  9.         Pass  
  10.         {  
  11.             //===========开启CG着色器语言编写模块============  
  12.             CGPROGRAM  
  13.   
  14.             //编译指令:告知编译器顶点和片段着色函数的名称  
  15.             #pragma vertex vert  
  16.             #pragma fragment frag  
  17.   
  18.             //顶点着色器输出结构  
  19.             struct vertexOutput  
  20.             {  
  21.                 float4 positon : SV_POSITION;//空间位置  
  22.                 float4 color : TEXCOORD0;//0级纹理坐标  
  23.             };  
  24.   
  25.             //--------------------------------【顶点着色函数】-----------------------------  
  26.             // 输入:POSITION语义  
  27.             // 输出:顶点输出结构体  
  28.             //---------------------------------------------------------------------------------  
  29.             vertexOutput vert(float4 vertexPos : POSITION)  
  30.             {  
  31.                 //实例化一个vertexOutput输出结构  
  32.                 vertexOutput output;  
  33.   
  34.                 //坐标系变换:将三维空间中的坐标投影到二维窗口  
  35.                 output.positon = mul(UNITY_MATRIX_MVP, vertexPos);  
  36.                 //输出颜色为顶点位置加上一个颜色偏移量  
  37.                 output.color = vertexPos + float4(0.2, 0.2, 0.2, 0.0);  
  38.   
  39.                 //返回最终的值  
  40.                 return output;  
  41.             }  
  42.   
  43.             //--------------------------------【片段着色函数】-----------------------------  
  44.             // 输入:vertexOutput结构体  
  45.             // 输出:COLOR语义(颜色值)  
  46.             //---------------------------------------------------------------------------------  
  47.             float4 frag(vertexOutput input) : COLOR  
  48.             {  
  49.                 //直接返回输入的颜色值  
  50.                 return input.color;  
  51.             }  
  52.   
  53.             //===========结束CG着色器语言编写模块===========  
  54.             ENDCG  
  55.         }  
  56.     }  
  57. }  

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

 


 




4.颜色单项可调的RGB Cube



我们在上述Shader的基础上,加上一个可调节的属性,代码如下:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //RGB立方体单项可调  
  2.   
  3. Shader "浅墨Shader编程/Volume12/4.RGB cube v2"  
  4. {  
  5.     //------------------------------------【属性值】------------------------------------  
  6.     Properties  
  7.     {  
  8.         //单项颜色调节变量  
  9.         _ColorValue("Color", Range(0.0, 1.0)) = 0.6  
  10.     }  
  11.   
  12.     //------------------------------------【唯一的子着色器】------------------------------------  
  13.     SubShader  
  14.     {  
  15.         //--------------------------------唯一的通道-------------------------------  
  16.         Pass  
  17.         {  
  18.             //===========开启CG着色器语言编写模块============  
  19.             CGPROGRAM  
  20.   
  21.             //编译指令:告知编译器顶点和片段着色函数的名称  
  22.             #pragma vertex vert  
  23.             #pragma fragment frag  
  24.   
  25.             //顶点着色器输出结构  
  26.             struct vertexOutput  
  27.             {  
  28.                 float4 positon : SV_POSITION;  
  29.                 float4 color : TEXCOORD0;  
  30.             };  
  31.   
  32.             //变量声明  
  33.             uniform float _ColorValue;  
  34.   
  35.             //--------------------------------【顶点着色函数】-----------------------------  
  36.             // 输入:POSITION语义  
  37.             // 输出:顶点输出结构体  
  38.             //---------------------------------------------------------------------------------  
  39.             vertexOutput vert(float4 vertexPos : POSITION)  
  40.             {  
  41.                 //实例化一个vertexOutput输出结构  
  42.                 vertexOutput output;  
  43.   
  44.                 //坐标系变换:将三维空间中的坐标投影到二维窗口  
  45.                 output.positon = mul(UNITY_MATRIX_MVP, vertexPos);  
  46.                 //输出颜色为顶点位置加上一个颜色偏移量  
  47.                 output.color = vertexPos + float4(_ColorValue, _ColorValue, _ColorValue, 0.0);  
  48.   
  49.                 //返回最终的值  
  50.                 return output;  
  51.             }  
  52.               
  53.             //--------------------------------【片段着色函数】-----------------------------  
  54.             // 输入:vertexOutput结构体  
  55.             // 输出:COLOR语义(颜色值)  
  56.             //---------------------------------------------------------------------------------  
  57.             float4 frag(vertexOutput input) : COLOR  
  58.             {  
  59.                 //直接返回输入的颜色值  
  60.                 return input.color;  
  61.             }  
  62.   
  63.             //===========结束CG着色器语言编写模块===========  
  64.             ENDCG  
  65.         }  
  66.     }  
  67. }  

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


 

 



5.三色分量可调的RGB Cube



在上述Shader的基础上,加上三个可调节的属性,分别代表RGB三色的分布情况。那么Shader代码就变成了:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //RGB立方体三色可调  
  2.   
  3. Shader "浅墨Shader编程/Volume12/5.RGB cube v3"  
  4. {  
  5.     //------------------------------------【属性值】------------------------------------  
  6.     Properties  
  7.     {  
  8.         //红色  
  9.         _ColorValueRed("ColorRed", Range(0.0, 1.0)) = 0.2  
  10.         //绿色  
  11.         _ColorValueGreen("ColorGreen", Range(0.0, 1.0)) = 0.5  
  12.         //蓝色  
  13.         _ColorValueBlue("ColorBlue", Range(0.0, 1.0)) = 0.6  
  14.     }  
  15.       
  16.     //------------------------------------【唯一的子着色器】------------------------------------  
  17.     SubShader  
  18.     {  
  19.         //--------------------------------唯一的通道-------------------------------  
  20.         Pass  
  21.         {  
  22.             //===========开启CG着色器语言编写模块============  
  23.             CGPROGRAM  
  24.   
  25.             //编译指令:告知编译器顶点和片段着色函数的名称  
  26.             #pragma vertex vert  
  27.             #pragma fragment frag   
  28.   
  29.             //顶点着色器输出结构  
  30.             struct vertexOutput  
  31.             {  
  32.                 float4 positon : SV_POSITION;  
  33.                 float4 color : TEXCOORD0;  
  34.             };  
  35.   
  36.             //变量声明  
  37.             uniform float _ColorValueRed;  
  38.             uniform float _ColorValueGreen;  
  39.             uniform float _ColorValueBlue;  
  40.   
  41.             //--------------------------------【顶点着色函数】-----------------------------  
  42.             // 输入:POSITION语义  
  43.             // 输出:顶点输出结构体  
  44.             //---------------------------------------------------------------------------------  
  45.             vertexOutput vert(float4 vertexPos : POSITION)  
  46.             {  
  47.                 //实例化一个vertexOutput输出结构  
  48.                 vertexOutput output;   
  49.   
  50.                 //坐标系变换:将三维空间中的坐标投影到二维窗口  
  51.                 output.positon = mul(UNITY_MATRIX_MVP, vertexPos);  
  52.                 //输出颜色为顶点位置加上一个颜色偏移量  
  53.                 output.color = vertexPos + float4(_ColorValueRed, _ColorValueGreen, _ColorValueBlue, 0.0);  
  54.   
  55.                 //返回最终的值  
  56.                 return output;  
  57.             }  
  58.   
  59.             //--------------------------------【片段着色函数】-----------------------------  
  60.             // 输入:vertexOutput结构体  
  61.             // 输出:COLOR语义(颜色值)  
  62.             //---------------------------------------------------------------------------------  
  63.             float4 frag(vertexOutput input) : COLOR   
  64.             {  
  65.                 //直接返回输入的颜色值  
  66.                 return input.color;  
  67.             }  
  68.   
  69.             //===========结束CG着色器语言编写模块===========  
  70.             ENDCG  
  71.             }  
  72.         }  
  73. }  

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

 

 

 OK,热身差不多就这么多,接下来看看漫反射的原理与Shader实现。

 




 

二、漫反射的了解与Shader实现




了解Diffuse Reflection



漫反射(Diffuse Reflection),又称Lambert反射,是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射。

在生活中,我们看到的月球的光近乎完全是漫反射。粉笔或者磨砂纸也是漫反射。事实上,任何表面的漫反射看起来都是类似于磨砂表面的效果。

 

而在完美的漫反射的情况下,所观察到的反射光的强度取决于在表面法线矢量和入射光的光线之间的角度的余弦。如图所示,经过归一化的物体表面法线向量N与物体表面正交,光源照射到物体表面的光线方向为L。


也就是说,我们可以使用表面的法线矢量N和入射光方向矢量L,计算漫反射。

 

想要计算出眼睛观察到的漫反射光,需要计算归一化的表面法向量N和归一化的方向光源向量L之间夹角的余弦值,即点积运算N·L。因为任何两个向量a和b的点积运算可以表示为:

而对于已经经过归一化的向量,|a| 和|b|都是1。

如果点积运算N·L为负,那么光源方向就是在表面内部照射过来的,这是错误的。这种情况下,我们就将反射光设为0即可。这可以通过代码max(0, N·L)来实现,这样就确保了在点积结果为负时,得到的结果为0。此外,漫反射光还取决于入射光light和材质的漫反射系数 。而对于一个黑色的表面,材质漫反射系数 为0,而对于白色的表面,材质的漫反射强度就为1.

根据如上的表述,漫反射强度的方程如下:

 

需注意,此公式适用于任何单一的颜色分量(如红、绿、蓝),也适用于颜色分量混合颜色的入射光。

OK,理论部分先说这么多。




实现Diffuse Reflection Shader

 

关于漫反射的可编程Shader实现,总结一下吧:

 

  • 核心代码可以在顶点着色器中实现,也可以在片段着色函数中实现。 
  • 需在世界空间中进行实现,因为世界空间中Unity提供了光源方向。
  •  关于如何获取参数,总结如下:

  • 通过属性properties中指定后传进来。
  • 世界空间的光源方向由unity内置变量_WorldSpaceLightPos0给出。
  • 光源颜色由unity内置变量_LightColor0给出。
  • 环境光颜色通过内置变量UNITY_LIGHTMODEL_AMBIENT给出。
  • 用Tags {"LightMode" = "ForwardBase"}确保上述内置变量的值处于正确的状态。
  • 世界空间下的物体表面的法线向量,可以通过输出参数语义NORMAL来获得物体空间下的表面的法线向量,然后将此向量从物体空间转到世界空间中获得。

OK,下面就开始Shader的书写吧。

 


6.单色可调的漫反射光照Shader书写


本节首先实现了一个单一光照条件下,可调颜色的漫反射可编程Shader。代码如下。

 

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. Shader "浅墨Shader编程/Volume12/6.Diffuse(Lambert) Shader"  
  2. {  
  3.     //------------------------------------【属性值】------------------------------------  
  4.     Properties  
  5.     {  
  6.         //颜色值  
  7.         _Color("Main Color", Color) = (1, 1, 1, 1)  
  8.   
  9.     }  
  10.   
  11.     //------------------------------------【唯一的子着色器】------------------------------------  
  12.     SubShader  
  13.         {  
  14.             //渲染类型设置:不透明  
  15.             Tags{ "RenderType" = "Opaque" }  
  16.             //设置光照模式:ForwardBase  
  17.             Tags{ "LightingMode" = "ForwardBase" }  
  18.             //细节层次设为:200  
  19.             LOD 200  
  20.   
  21.             //--------------------------------唯一的通道-------------------------------  
  22.             Pass  
  23.             {  
  24.                 //===========开启CG着色器语言编写模块===========  
  25.                 CGPROGRAM  
  26.   
  27.                 //编译指令:告知编译器顶点和片段着色函数的名称  
  28.                 #pragma vertex vert  
  29.                 #pragma fragment frag  
  30.   
  31.                 //包含头文件  
  32.                 #include "UnityCG.cginc"  
  33.   
  34.                 //顶点着色器输入结构  
  35.                 struct appdata  
  36.                 {  
  37.                     float4 vertex : POSITION;//顶点位置  
  38.                     float3 normal : NORMAL;//法线向量坐标  
  39.                 };  
  40.   
  41.                 //顶点着色器输出结构  
  42.                 struct v2f  
  43.                 {  
  44.                     float4 position : SV_POSITION;//像素位置  
  45.                     float3 normal : NORMAL;//法线向量坐标  
  46.                 };  
  47.   
  48.                 //变量声明  
  49.                 float4 _LightColor0;  
  50.                 float4 _Color;  
  51.   
  52.                 //--------------------------------【顶点着色函数】-----------------------------  
  53.                 // 输入:顶点输入结构体  
  54.                 // 输出:顶点输出结构体  
  55.                 //---------------------------------------------------------------------------------  
  56.                 v2f vert(appdata input)  
  57.                 {  
  58.                     //【1】声明一个输出结构对象  
  59.                     v2f output;  
  60.   
  61.                     //【2】填充此输出结构  
  62.                     //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
  63.                     output.position = mul(UNITY_MATRIX_MVP, input.vertex);  
  64.                     //获取顶点在世界空间中的法线向量坐标  
  65.                     output.normal = mul(float4(input.normal, 0.0), _World2Object).xyz;  
  66.   
  67.                     //【3】返回此输出结构对象  
  68.                     return output;  
  69.                 }  
  70.   
  71.   
  72.                 //--------------------------------【片段着色函数】-----------------------------  
  73.                 // 输入:顶点输出结构体  
  74.                 // 输出:float4型的像素颜色值  
  75.                 //---------------------------------------------------------------------------------  
  76.                 fixed4 frag(v2f input) : COLOR  
  77.                 {  
  78.                     //【1】先准备好需要的参数  
  79.                     //获取法线的方向  
  80.                     float3 normalDirection = normalize(input.normal);  
  81.                     //获取入射光线的值与方向  
  82.                     float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);  
  83.   
  84.                     //【2】计算出漫反射颜色值  Diffuse=LightColor * MainColor * max(0,dot(N,L))  
  85.                     float3 diffuse = _LightColor0.rgb * _Color.rgb * max(0.0, dot(normalDirection, lightDirection));  
  86.   
  87.                     //【3】合并漫反射颜色值与环境光颜色值  
  88.                     float4 DiffuseAmbient = float4(diffuse, 1.0) + UNITY_LIGHTMODEL_AMBIENT;  
  89.   
  90.                     //【4】将漫反射-环境光颜色值乘上纹理颜色,并返回  
  91.                     return DiffuseAmbient;  
  92.                 }  
  93.   
  94.                 //===========结束CG着色器语言编写模块===========  
  95.                 ENDCG  
  96.   
  97.             }  
  98.         }  
  99. }  

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


 

 

7.可调颜色和自定义纹理的漫反射光照Shader


接下来,让我们进一步实现一个可以自定义纹理的漫反射光照的顶点&片段着色器。也就是在Properties属性中加上一项纹理,然后在最终的漫反射颜色计算完成之后,乘上纹理即可。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. Shader "浅墨Shader编程/Volume12/7.Diffuse(Lambert) Shader with Texture"  
  2. {  
  3.     //------------------------------------【属性值】------------------------------------  
  4.     Properties  
  5.     {  
  6.         //主纹理  
  7.         _MainTex("Texture", 2D) = "white"{}  
  8.         //主颜色值  
  9.         _Color("Main Color", Color) = (1, 1, 1, 1)  
  10.   
  11.     }  
  12.   
  13.     //------------------------------------【唯一的子着色器】------------------------------------  
  14.     SubShader  
  15.         {  
  16.             //渲染类型设置:不透明  
  17.             Tags{ "RenderType" = "Opaque" }  
  18.             //设置光照模式:ForwardBase  
  19.             Tags{ "LightingMode" = "ForwardBase" }  
  20.             //细节层次设为:200  
  21.             LOD 200  
  22.   
  23.             //--------------------------------唯一的通道-------------------------------  
  24.             Pass  
  25.             {  
  26.                 //===========开启CG着色器语言编写模块===========  
  27.                 CGPROGRAM  
  28.   
  29.                 //编译指令:告知编译器顶点和片段着色函数的名称  
  30.                 #pragma vertex vert  
  31.                 #pragma fragment frag  
  32.   
  33.                 //包含头文件  
  34.                 #include "UnityCG.cginc"  
  35.   
  36.                 //顶点着色器输入结构  
  37.                 struct appdata  
  38.                 {  
  39.                     float4 vertex : POSITION;//顶点位置  
  40.                     float3 normal : NORMAL;//法线向量坐标  
  41.                     float2 texcoord : TEXCOORD0;//纹理坐标  
  42.                 };  
  43.   
  44.                 //顶点着色器输出结构  
  45.                 struct v2f  
  46.                 {  
  47.                     float4 positon : SV_POSITION;//像素位置  
  48.                     float3 normal : NORMAL;//法线向量坐标  
  49.                     float2 texcoord : TEXCOORD0;//纹理坐标  
  50.                 };  
  51.   
  52.                 //变量声明  
  53.                 float4 _LightColor0;  
  54.                 float4 _Color;  
  55.                 sampler2D _MainTex;  
  56.   
  57.   
  58.                 //--------------------------------【顶点着色函数】-----------------------------  
  59.                 // 输入:顶点输入结构体  
  60.                 // 输出:顶点输出结构体  
  61.                 //---------------------------------------------------------------------------------  
  62.                 v2f vert(appdata input)  
  63.                 {  
  64.                     //【1】声明一个输出结构对象  
  65.                     v2f output;  
  66.   
  67.                     //【2】填充此输出结构  
  68.                     //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口  
  69.                     output.positon = mul(UNITY_MATRIX_MVP, input.vertex);  
  70.                     //获取顶点在世界空间中的法线向量坐标  
  71.                     output.normal = mul(float4(input.normal, 0.0), _World2Object).xyz;  
  72.                     //输出的纹理坐标也就是输入的纹理坐标  
  73.                     output.texcoord = input.texcoord;  
  74.   
  75.                     //【3】返回此输出结构对象  
  76.                     return output;  
  77.                 }  
  78.   
  79.                 //--------------------------------【片段着色函数】-----------------------------  
  80.                 // 输入:顶点输出结构体  
  81.                 // 输出:float4型的像素颜色值  
  82.                 //---------------------------------------------------------------------------------  
  83.                 fixed4 frag(v2f input) : COLOR  
  84.                 {  
  85.                     //【1】先准备好需要的参数  
  86.                     //获取纹理颜色  
  87.                     float4 texColor = tex2D(_MainTex, input.texcoord);  
  88.                     //获取法线的方向  
  89.                     float3 normalDirection = normalize(input.normal);  
  90.                     //获取入射光线的值与方向  
  91.                     float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);  
  92.   
  93.                     //【2】计算出漫反射颜色值  Diffuse=LightColor * MainColor * max(0,dot(N,L))  
  94.                     float3 diffuse = _LightColor0.rgb * _Color.rgb * max(0.0, dot(normalDirection, lightDirection));  
  95.   
  96.                     //【3】合并漫反射颜色值与环境光颜色值  
  97.                     float4 DiffuseAmbient = float4(diffuse, 1.0) + UNITY_LIGHTMODEL_AMBIENT;  
  98.   
  99.                     //【4】将漫反射-环境光颜色值乘上纹理颜色,并返回  
  100.                     return DiffuseAmbient* texColor;  
  101.                 }  
  102.   
  103.                 //===========结束CG着色器语言编写模块===========  
  104.                 ENDCG  
  105.             }  
  106.         }  
  107. }  

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


下图是此漫反射Shader使用到皮卡丘模型上的效果图。

  


今天写的Shader的全家福:

 

 

最后放几张加上镜头特效的场景效果图:

 







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




附1: 本博文相关资源下载链接清单


【百度云】博文游戏场景exe下载

【百度云】博文示例场景资源和源码工程下载   ( PS:工程所用Unity版本为5.2.1)

【Github】本文全部Shader源码




附2:Reference


[1] http://docs.unity3d.com/Manual/SL-Reference.html

[2] https://en.wikibooks.org/wiki/Cg_Programming

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值