【Unity Shader实战】卡通风格的Shader(二)

写在前面


本系列其他文章:


好久没写博客了,一定是因为课程作业比较多,一定不是因为我懒,恩恩。


三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface Shader中实现描边效果的弊端,也就是只对表面平缓的模型有效。这是因为我们是依赖法线和视角的点乘结果来进行描边判断的,因此,对于那些平整的表面,它们的法线通常是一个常量或者会发生突变(例如立方体的每个面),这样就会导致最后的效果并非如我们所愿。如下图所示:



因此,我们有一个更好的方法来实现描边效果,也就是通过两个pass进行渲染——首先渲染对象的背面,用黑色略微向外扩展一点,就是我们的描边效果;然后正常渲染正面即可。而我们应该知道,surface shader是不可以使用pass的。


在这篇里,我们就会学习如何使用Vertex & Fragment Shader来实现上述的过程。很显然,这样的一个过程包含了两个步骤——描边和正常的渲染。


最后的效果如下:




实现描边


上一篇里,我们使用了边缘高光来实现描边。而这篇里,我们将使用一个单独的pass来得到一个更好的效果。这里说的“更好”指的是以下几个方面:

  • 首先是对平整表面的适应性,如上面正方体的例子,这种方法仍可以得到期望的效果;
  • 而且这种方法可以不破坏正面模型的逼真度,也就是说正面模型可以完全不受影响。与之产生对比的是上一篇中的方法,使用边缘光照来实现的描边效果会影响到正面模型的表面,即正面模型也会有强烈的描边效果,而这往往不是我们所期望的。
因此,这个pass的第一个步骤就是剔除正面部分:
  1. Cull Front  
  2. hting Off  

我们先来看frag函数,因此它的工作非常简单!就是输出黑色啦~当然如果你的描边不想要黑色可以在这里改写。
  1. float4 frag(v2f i) : COLOR    
  2. {   
  3.     return float4(0, 0, 0, 1);                 
  4. }   

然后,我们继续计算vert函数部分。我们将会沿着顶点的法线方法向外扩张该点来模拟描边。因此,我们需要在下面的结构体中声明position和normal属性:
  1. struct a2v  
  2. {  
  3.     float4 vertex : POSITION;  
  4.     float3 normal : NORMAL;  
  5. };   
  6.   
  7. struct v2f  
  8. {  
  9.     float4 pos : POSITION;  
  10. };  

接下来,我们定义一个范围在0到1之间的_Outline的变量来控制描边的宽度。最后,vert函数如下:
  1.                      float _Outline;  
  2.   
  3. v2f vert (a2v v)  
  4. {  
  5.     v2f o;  
  6.     o.pos = mul( UNITY_MATRIX_MVP, v.vertex + (float4(v.normal,0) * _Outline));   
  7.     return o;  
  8. }  

它的含义很好理解:把原先的顶点位置v.vertex沿着v.normal的方向扩展_Outline倍后,再转换到投影平面上输出最后的屏幕位置信息。

效果如下:


为了暴露问题,我把描边的宽度调的比较高。那么,问题来了。大家可以看出来哪里不太对吧?没错,就是眼睛和嘴巴的地方。为什么会有哪些很大的黑色色块呢?这是因为眼睛和嘴巴是独立于身体之外的两个网格,它们各自使用了一个新的材质,而它们的深度关系是眼睛和嘴巴在身体的后面(被身体的皮肤包裹嘛),因此在渲染的时候身体的渲染输出像素会覆盖眼睛和嘴巴的部分,也包括身体的描边部分,也就是说身体的黑色描边会覆盖眼睛和嘴巴的正常渲染,而这不是我们所希望的。一种暴力的解决方法就是直接关闭该pass的深度信息。即:
  1. Cull Front  
  2. Lighting Off  
  3. ZWrite Off  

这样一来,这个pass的结果是不写入深度缓存中的,而后面只要有其他材质要渲染该点的像素就会覆盖它。这样的效果如下:


那么,问题又来了。眼睛和嘴巴部分虽然对了,但小怪物的先后关系又乱了,即后面小怪物的身体挡住了前面小怪物的描边。而要解决这个问题,就要写入深度缓存。死循环了有木有!

其实,这说明我们生成描边的方法需要改进。我们回想为什么会出现眼睛和嘴巴那样的错误,是因为我们把描边的宽度调的太大了。我们之所以会这么做(当然这里我是故意的。。。),是因为有时候一些相邻顶点的法线指向非常不同,而为了得到我们想要的感性宽度,我们不得不调整的很大。而上述过程的实质其实就是把背光面的模型放大了而已,我们可以理解成它实际相当于一个新的黑色模型。这样放大的太过分了会发生什么呢?就是穿透和遮挡了。

而正确的方法应该是,把顶点当成轮廓处理而不是一个真正的模型。也就是说,当我们观察背面的某一个顶点时,要把它的Z方向的值扁平化,那么描边的结果就会主要受X和Y方向的影响。因此,

扁平化背面


首先,我们要在视角坐标系中处理描边。因为描边正是基于我们观察的角度而定的。因此,我们要把需要的变量都转换到视角坐标系下处理。这里面涉及两个变量——顶点的位置和顶点的法线。

顶点很好处理,只要使用UNITY_MATRIX_MV即可。法线的转换麻烦一点,这是因为法线并不是真正定义在模型坐标系中的,而是和它是正交的,我们需要使用ModelView转换矩阵的转置矩阵来把法线转换到视角坐标系中。原因可以看这里这里

因此,我们的工作包含下面几个步骤:
  • 把顶点位置转换到视角坐标系;
  • 把法线转换到视角坐标系;
  • 把转换后的法线的z值扁平化,即使其是一个较小的定值,这样所有的背面其实都在一个平面上;
  • 按描边的宽度放缩法线,并添加到转换后顶点的位置上,得到新的视角坐标系中的位置;
  • 把新的位置转换到投影坐标系中。
代码如下:
  1.         v2f vert (a2v v)  
  2.         {  
  3.             v2f o;  
  4.   
  5.             float4 pos = mul( UNITY_MATRIX_MV, v.vertex);   
  6. float3 normal = mul( (float3x3)UNITY_MATRIX_IT_MV, v.normal);    
  7. normal.z = -0.4;  
  8. pos = pos + float4(normalize(normal),0) * _Outline;  
  9. o.pos = mul(UNITY_MATRIX_P, pos);  
  10.   
  11.             return o;  
  12.         }  


卡通化


后面这一部分没什么好解释的,和上一篇的方法一致,同样使用了简化颜色和渐变纹理来模拟卡通效果。


弊端


上一篇里的方法比,这里的方法解决了之前的两个弊端:一个是轮廓宽度无法精确保证,一个是对于法线突变的模型的不适应性。但它也有自己的弊端。最明显的就是,它无法和模型内部的褶皱添加轮廓。例如上面的小怪兽,只有它最外层的边界才有轮廓线,但其内部的肥肉褶皱则无法体现。而这种问题的解决方法,可以依靠第三种更高级的Shader来实现。具体请参见卡通风格的Shader(三)。


代码


我知道大家最想要的还是代码,上面很多人直接略过。Sigh~

还是把完整的代码给出。代码有两种,一种使用了法线纹理,一种没有使用法线纹理。每种都包含了三个pass:第一个pass处理背面进行描边,第二个pass处理正面的forwardbase,第三个pass处理正面的forwardadd。

在Vertex & Fragment中处理法线和光照,简直是噩梦啊!VF虐我千百遍,我却待她如初恋,哎。后面会写一篇这方面的文章,如果有时间的话。。。

首先是没有使用法线纹理的代码:
  1. Shader "MyToon/Toon-Fragment" {  
  2.     Properties {  
  3.         _MainTex ("Base (RGB)", 2D) = "white" {}  
  4.         _Ramp ("Ramp Texture", 2D) = "white" {}  
  5.         _Tooniness ("Tooniness", Range(0.1,20)) = 4  
  6.         _Outline ("Outline", Range(0,1)) = 0.1  
  7.     }  
  8.     SubShader {  
  9.         Tags { "RenderType"="Opaque" }  
  10.         LOD 200  
  11.    
  12.         Pass {  
  13.             Tags { "LightMode"="ForwardBase" }  
  14.               
  15.             Cull Front  
  16.             Lighting Off  
  17.             ZWrite On  
  18.    
  19.             CGPROGRAM  
  20.              
  21.             #pragma vertex vert  
  22.             #pragma fragment frag  
  23.              
  24.             #pragma multi_compile_fwdbase  
  25.   
  26.             #include "UnityCG.cginc"  
  27.               
  28.             float _Outline;  
  29.    
  30.             struct a2v  
  31.             {  
  32.                 float4 vertex : POSITION;  
  33.                 float3 normal : NORMAL;  
  34.             };   
  35.    
  36.             struct v2f  
  37.             {  
  38.                 float4 pos : POSITION;  
  39.             };  
  40.    
  41.             v2f vert (a2v v)  
  42.             {  
  43.                 v2f o;  
  44.   
  45.                 float4 pos = mul( UNITY_MATRIX_MV, v.vertex);   
  46.                 float3 normal = mul( (float3x3)UNITY_MATRIX_IT_MV, v.normal);    
  47.                 normal.z = -0.5;  
  48.                 pos = pos + float4(normalize(normal),0) * _Outline;  
  49.                 o.pos = mul(UNITY_MATRIX_P, pos);  
  50.                   
  51.                 return o;  
  52.             }  
  53.    
  54.             float4 frag(v2f i) : COLOR    
  55.             {   
  56.                 return float4(0, 0, 0, 1);                 
  57.             }   
  58.    
  59.             ENDCG  
  60.         }  
  61.           
  62.         Pass {  
  63.             Tags { "LightMode"="ForwardBase" }  
  64.               
  65.             Cull Back   
  66.             Lighting On  
  67.   
  68.             CGPROGRAM  
  69.  
  70.             #pragma vertex vert  
  71.             #pragma fragment frag  
  72.              
  73.             #pragma multi_compile_fwdbase  
  74.  
  75.             #include "UnityCG.cginc"  
  76.             #include "Lighting.cginc"  
  77.             #include "AutoLight.cginc"  
  78.             #include "UnityShaderVariables.cginc"  
  79.               
  80.   
  81.             sampler2D _MainTex;  
  82.             sampler2D _Ramp;  
  83.   
  84.             float4 _MainTex_ST;  
  85.   
  86.             float _Tooniness;  
  87.    
  88.             struct a2v  
  89.             {  
  90.                 float4 vertex : POSITION;  
  91.                 float3 normal : NORMAL;  
  92.                 float4 texcoord : TEXCOORD0;  
  93.                 float4 tangent : TANGENT;  
  94.             };   
  95.   
  96.             struct v2f  
  97.             {  
  98.                 float4 pos : POSITION;  
  99.                 float2 uv : TEXCOORD0;  
  100.                 float3 normal : TEXCOORD1;  
  101.                 LIGHTING_COORDS(2,3)  
  102.             };  
  103.               
  104.             v2f vert (a2v v)  
  105.             {  
  106.                 v2f o;  
  107.   
  108.                 //Transform the vertex to projection space  
  109.                 o.pos = mul( UNITY_MATRIX_MVP, v.vertex);   
  110.                 o.normal  = mul((float3x3)_Object2World, SCALED_NORMAL);  
  111.                 //Get the UV coordinates  
  112.                 o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);    
  113.                   
  114.                 // pass lighting information to pixel shader  
  115.                 TRANSFER_VERTEX_TO_FRAGMENT(o);  
  116.                 return o;  
  117.             }  
  118.               
  119.             float4 frag(v2f i) : COLOR    
  120.             {   
  121.                 //Get the color of the pixel from the texture  
  122.                 float4 c = tex2D (_MainTex, i.uv);    
  123.                 //Merge the colours  
  124.                 c.rgb = (floor(c.rgb*_Tooniness)/_Tooniness);  
  125.   
  126.                 //Based on the ambient light  
  127.                 float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;  
  128.   
  129.                 //Work out this distance of the light  
  130.                 float atten = LIGHT_ATTENUATION(i);  
  131.                 //Angle to the light  
  132.                 float diff =  dot (normalize(i.normal), normalize(_WorldSpaceLightPos0.xyz));    
  133.                 diff = diff * 0.5 + 0.5;   
  134.                 //Perform our toon light mapping   
  135.                 diff = tex2D(_Ramp, float2(diff, 0.5));  
  136.                 //Update the colour  
  137.                 lightColor += _LightColor0.rgb * (diff * atten);   
  138.                 //Product the final color  
  139.                 c.rgb = lightColor * c.rgb * 2;  
  140.                 return c;   
  141.   
  142.             }   
  143.   
  144.             ENDCG  
  145.         }  
  146.         Pass {  
  147.             Tags { "LightMode"="ForwardAdd" }  
  148.               
  149.             Cull Back   
  150.             Lighting On  
  151.             Blend One One  
  152.   
  153.             CGPROGRAM  
  154.  
  155.             #pragma vertex vert  
  156.             #pragma fragment frag  
  157.              
  158.             #pragma multi_compile_fwdadd  
  159.  
  160.             #include "UnityCG.cginc"  
  161.             #include "Lighting.cginc"  
  162.             #include "AutoLight.cginc"  
  163.             #include "UnityShaderVariables.cginc"  
  164.               
  165.   
  166.             sampler2D _MainTex;  
  167.             sampler2D _Ramp;  
  168.   
  169.             float4 _MainTex_ST;  
  170.   
  171.             float _Tooniness;  
  172.    
  173.             struct a2v  
  174.             {  
  175.                 float4 vertex : POSITION;  
  176.                 float3 normal : NORMAL;  
  177.                 float4 texcoord : TEXCOORD0;  
  178.                 float4 tangent : TANGENT;  
  179.             };   
  180.   
  181.             struct v2f  
  182.             {  
  183.                 float4 pos : POSITION;  
  184.                 float2 uv : TEXCOORD0;  
  185.                 float3 normal : TEXCOORD1;  
  186.                 half3 lightDir : TEXCOORD2;  
  187.                 LIGHTING_COORDS(3,4)  
  188.             };  
  189.               
  190.             v2f vert (a2v v)  
  191.             {  
  192.                 v2f o;  
  193.   
  194.                 //Transform the vertex to projection space  
  195.                 o.pos = mul( UNITY_MATRIX_MVP, v.vertex);   
  196.                 o.normal  = mul((float3x3)_Object2World, SCALED_NORMAL);  
  197.                 o.lightDir = WorldSpaceLightDir( v.vertex );  
  198.                 //Get the UV coordinates  
  199.                 o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);    
  200.                   
  201.                 // pass lighting information to pixel shader  
  202.                 TRANSFER_VERTEX_TO_FRAGMENT(o);  
  203.                 return o;  
  204.             }  
  205.               
  206.             float4 frag(v2f i) : COLOR    
  207.             {   
  208.                 //Get the color of the pixel from the texture  
  209.                 float4 c = tex2D (_MainTex, i.uv);    
  210.                 //Merge the colours  
  211.                 c.rgb = (floor(c.rgb*_Tooniness)/_Tooniness);  
  212.   
  213.                 //Based on the ambient light  
  214.                 float3 lightColor = float3(0);  
  215.   
  216.                 //Work out this distance of the light  
  217.                 float atten = LIGHT_ATTENUATION(i);  
  218.                 //Angle to the light  
  219.                 float diff =  dot (normalize(i.normal), normalize(i.lightDir));    
  220.                 diff = diff * 0.5 + 0.5;   
  221.                 //Perform our toon light mapping   
  222.                 diff = tex2D(_Ramp, float2(diff, 0.5));  
  223.                 //Update the colour  
  224.                 lightColor += _LightColor0.rgb * (diff * atten);   
  225.                 //Product the final color  
  226.                 c.rgb = lightColor * c.rgb * 2;  
  227.                 return c;   
  228.             }   
  229.   
  230.             ENDCG  
  231.         }  
  232.     }  
  233.     FallBack "Diffuse"        
  234. }  

然后是使用了法线纹理的Shader:
  1. Shader "MyToon/Toon-Fragment_Normal" {  
  2.     Properties {  
  3.         _MainTex ("Base (RGB)", 2D) = "white" {}  
  4.         _Bump ("Bump", 2D) = "bump" {}  
  5.         _Ramp ("Ramp Texture", 2D) = "white" {}  
  6.         _Tooniness ("Tooniness", Range(0.1,20)) = 4  
  7.         _Outline ("Outline", Range(0,1)) = 0.1  
  8.     }  
  9.     SubShader {  
  10.         Tags { "RenderType"="Opaque" }  
  11.         LOD 200  
  12.    
  13.         Pass {  
  14.             Tags { "LightMode"="ForwardBase" }  
  15.               
  16.             Cull Front  
  17.             Lighting Off  
  18.             ZWrite On  
  19.    
  20.             CGPROGRAM  
  21.              
  22.             #pragma vertex vert  
  23.             #pragma fragment frag  
  24.              
  25.             #pragma multi_compile_fwdbase  
  26.   
  27.             #include "UnityCG.cginc"  
  28.               
  29.             float _Outline;  
  30.    
  31.             struct a2v  
  32.             {  
  33.                 float4 vertex : POSITION;  
  34.                 float3 normal : NORMAL;  
  35.             };   
  36.    
  37.             struct v2f  
  38.             {  
  39.                 float4 pos : POSITION;  
  40.             };  
  41.    
  42.             v2f vert (a2v v)  
  43.             {  
  44.                 v2f o;  
  45.                   
  46.                 float4 pos = mul( UNITY_MATRIX_MV, v.vertex);   
  47.                 float3 normal = mul( (float3x3)UNITY_MATRIX_IT_MV, v.normal);    
  48.                 normal.z = -0.5;  
  49.                 pos = pos + float4(normalize(normal),0) * _Outline;  
  50.                 o.pos = mul(UNITY_MATRIX_P, pos);  
  51.                   
  52.                 return o;  
  53.             }  
  54.    
  55.             float4 frag(v2f i) : COLOR    
  56.             {   
  57.                 return float4(0, 0, 0, 1);                 
  58.             }   
  59.    
  60.             ENDCG  
  61.         }  
  62.           
  63.         Pass {  
  64.             Tags { "LightMode"="ForwardBase" }  
  65.               
  66.             Cull Back   
  67.             Lighting On  
  68.   
  69.             CGPROGRAM  
  70.  
  71.             #pragma vertex vert  
  72.             #pragma fragment frag  
  73.              
  74.             #pragma multi_compile_fwdbase  
  75.  
  76.             #include "UnityCG.cginc"  
  77.             #include "Lighting.cginc"  
  78.             #include "AutoLight.cginc"  
  79.             #include "UnityShaderVariables.cginc"  
  80.   
  81.             sampler2D _MainTex;  
  82.             sampler2D _Bump;  
  83.             sampler2D _Ramp;  
  84.   
  85.             float4 _MainTex_ST;  
  86.             float4 _Bump_ST;  
  87.   
  88.             float _Tooniness;  
  89.    
  90.             struct a2v  
  91.             {  
  92.                 float4 vertex : POSITION;  
  93.                 float3 normal : NORMAL;  
  94.                 float4 texcoord : TEXCOORD0;  
  95.                 float4 tangent : TANGENT;  
  96.             };   
  97.   
  98.             struct v2f  
  99.             {  
  100.                 float4 pos : POSITION;  
  101.                 float2 uv : TEXCOORD0;  
  102.                 float2 uv2 : TEXCOORD1;  
  103.                 float3 lightDirection : TEXCOORD2;  
  104.                 LIGHTING_COORDS(3,4)  
  105.             };  
  106.               
  107.             v2f vert (a2v v)  
  108.             {  
  109.                 v2f o;  
  110.                 //Create a rotation matrix for tangent space  
  111.                 TANGENT_SPACE_ROTATION;   
  112.                 //Store the light's direction in tangent space  
  113.                 o.lightDirection = mul(rotation, ObjSpaceLightDir(v.vertex));  
  114.                 //Transform the vertex to projection space  
  115.                 o.pos = mul( UNITY_MATRIX_MVP, v.vertex);   
  116.                 //Get the UV coordinates  
  117.                 o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);    
  118.                 o.uv2 = TRANSFORM_TEX (v.texcoord, _Bump);  
  119.                   
  120.                 // pass lighting information to pixel shader  
  121.                 TRANSFER_VERTEX_TO_FRAGMENT(o);  
  122.                 return o;  
  123.             }  
  124.               
  125.             float4 frag(v2f i) : COLOR    
  126.             {   
  127.                 //Get the color of the pixel from the texture  
  128.                 float4 c = tex2D (_MainTex, i.uv);    
  129.                 //Merge the colours  
  130.                 c.rgb = (floor(c.rgb*_Tooniness)/_Tooniness);  
  131.   
  132.                 //Get the normal from the bump map  
  133.                 float3 n =  UnpackNormal(tex2D (_Bump, i.uv2));   
  134.   
  135.                 //Based on the ambient light  
  136.                 float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;  
  137.   
  138.                 //Work out this distance of the light  
  139.                 float atten = LIGHT_ATTENUATION(i);  
  140.                 //Angle to the light  
  141.                 float diff = saturate (dot (n, normalize(i.lightDirection)));    
  142.                 //Perform our toon light mapping   
  143.                 diff = tex2D(_Ramp, float2(diff, 0.5));  
  144.                 //Update the colour  
  145.                 lightColor += _LightColor0.rgb * (diff * atten);   
  146.                 //Product the final color  
  147.                 c.rgb = lightColor * c.rgb * 2;  
  148.                 return c;   
  149.   
  150.             }   
  151.   
  152.             ENDCG  
  153.         }  
  154.         Pass {  
  155.             Tags { "LightMode"="ForwardAdd" }  
  156.               
  157.             Cull Back   
  158.             Lighting On  
  159.             Blend One One  
  160.   
  161.             CGPROGRAM  
  162.  
  163.             #pragma vertex vert  
  164.             #pragma fragment frag  
  165.              
  166.             #pragma multi_compile_fwdadd  
  167.  
  168.             #include "UnityCG.cginc"  
  169.             #include "Lighting.cginc"  
  170.             #include "AutoLight.cginc"  
  171.             #include "UnityShaderVariables.cginc"  
  172.   
  173.             sampler2D _MainTex;  
  174.             sampler2D _Bump;  
  175.             sampler2D _Ramp;  
  176.   
  177.             float4 _MainTex_ST;  
  178.             float4 _Bump_ST;  
  179.   
  180.             float _Tooniness;  
  181.    
  182.             struct a2v  
  183.             {  
  184.                 float4 vertex : POSITION;  
  185.                 float3 normal : NORMAL;  
  186.                 float4 texcoord : TEXCOORD0;  
  187.                 float4 tangent : TANGENT;  
  188.             };   
  189.   
  190.             struct v2f  
  191.             {  
  192.                 float4 pos : POSITION;  
  193.                 float2 uv : TEXCOORD0;  
  194.                 float2 uv2 : TEXCOORD1;  
  195.                 float3 lightDirection : TEXCOORD2;  
  196.                 LIGHTING_COORDS(3,4)  
  197.             };  
  198.               
  199.             v2f vert (a2v v)  
  200.             {  
  201.                 v2f o;  
  202.                 //Create a rotation matrix for tangent space  
  203.                 TANGENT_SPACE_ROTATION;   
  204.                 //Store the light's direction in tangent space  
  205.                 o.lightDirection = mul(rotation, ObjSpaceLightDir(v.vertex));  
  206.                 //Transform the vertex to projection space  
  207.                 o.pos = mul( UNITY_MATRIX_MVP, v.vertex);   
  208.                 //Get the UV coordinates  
  209.                 o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);    
  210.                 o.uv2 = TRANSFORM_TEX (v.texcoord, _Bump);  
  211.                   
  212.                 // pass lighting information to pixel shader  
  213.                 TRANSFER_VERTEX_TO_FRAGMENT(o);  
  214.                   
  215.                 return o;  
  216.             }  
  217.               
  218.             float4 frag(v2f i) : COLOR    
  219.             {   
  220.                 //Get the color of the pixel from the texture  
  221.                 float4 c = tex2D (_MainTex, i.uv);    
  222.                 //Merge the colours  
  223.                 c.rgb = (floor(c.rgb*_Tooniness)/_Tooniness);  
  224.   
  225.                 //Get the normal from the bump map  
  226.                 float3 n =  UnpackNormal(tex2D (_Bump, i.uv2));   
  227.   
  228.                 //Based on the ambient light  
  229.                 float3 lightColor = float3(0);  
  230.   
  231.                 //Work out this distance of the light  
  232.                 float atten = LIGHT_ATTENUATION(i);  
  233.                 //Angle to the light  
  234.                 float diff = saturate (dot (n, normalize(i.lightDirection)));    
  235.                 //Perform our toon light mapping   
  236.                 diff = tex2D(_Ramp, float2(diff, 0.5));  
  237.                 //Update the colour  
  238.                 lightColor += _LightColor0.rgb * (diff * atten);   
  239.                 //Product the final color  
  240.                 c.rgb = lightColor * c.rgb * 2;  
  241.                 return c;   
  242.   
  243.             }   
  244.   
  245.             ENDCG  
  246.         }  
  247.     }  
  248.     FallBack "Diffuse"        
  249. }  



写在最后


上一篇里的代码我也更新过了,添加了有无法线的两种shader。和上一篇里的还有一点不一样,上一篇里的渐变纹理使用了假的双向反射分布函数,即除了漫反射方向还有视角方向进行采样。这一篇里仅仅使用了漫反射方向对渐变纹理进行采样,对应的渐变纹理也是一维的,如下:


读者有需要的可以自己添加上视角方向的采样,也就是说在v2f里添加一个新的变量viewDir,然后逐顶点计算后传递给frag函数。如果我后面有时间的话可能会回头添加上。当然,大家还是靠自己比较好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值