Shader学习(七):非真实感绘制

发表于2017-09-05
评论0 3.8k浏览

下面和大家介绍的是一个学习shader着色器的系列,想使用好shader的开发人员可以学习下。下面介绍第七篇关于非真实感绘制的详细介绍。

边缘提取

边缘提取有很多种,这里用的是屏幕空间的做法,首先把要提取边缘的物品用纯色绘制到一张rt上,然后用一个一个边缘检测的滤波器在ps中扫一遍。

绘制纯色的的ps
  1. float4 ps_main( float4 Diff: COLOR0 ) : COLOR  
  2. {  
  3.     return 1;  
  4. }  


绘制完之后就像这样


边缘提取的滤波器称为Sobel filter。



边缘提取的ps如下
  1. sampler RT;  
  2. const float off = 1.0 / 256.0;  
  3. float4 ps_main( float2 TexCoord : TEXCOORD0 ) : COLOR  
  4. {  
  5.    // Sample the neighbor pixels  
  6.    float s00 = tex2D(RT, TexCoord   float2(-off, -off));  
  7.    float s01 = tex2D(RT, TexCoord   float2( 0,   -off));  
  8.    float s02 = tex2D(RT, TexCoord   float2( off, -off));  
  9.   
  10.    float s10 = tex2D(RT, TexCoord   float2(-off,  0));  
  11.    float s12 = tex2D(RT, TexCoord   float2( off,  0));  
  12.   
  13.    float s20 = tex2D(RT, TexCoord   float2(-off,  off));  
  14.    float s21 = tex2D(RT, TexCoord   float2( 0,    off));  
  15.    float s22 = tex2D(RT, TexCoord   float2( off,  off));  
  16.   
  17.    // Sobel filter in X and Ydirection  
  18.    float sobelX = s00   2 * s10   s20 - s02 - 2 * s12 - s22;  
  19.    float sobelY = s00   2 * s01   s02 - s20 - 2 * s21 - s22;  
  20.   
  21.    // Find edge     
  22.    float edgeSqr = (sobelX * sobelX   sobelY * sobelY);  
  23.    return 1.0-(edgeSqr > 0.07 * 0.07);  
  24. }  



在ps中,要计算两个方向x,y的分量,得到的结果是2d的vector,表示的是这个像素点的方向。

通过这个向量,求取它的长度,得到的就是这个边将要绘制的强度。

结果:




其他的边缘检测思路

边缘检测有很多方式,上面这种有一个很明显的问题就是它只能检测object最外面的轮廓,但是像下面这种情况


大象的鼻子就没法处理了。这时候一个处理方式是利用深度信息来处理。

渲染深度的vs如下
  1. float4x4 view_proj_matrix;  
  2. float depthScale;  
  3. struct VS_OUTPUT   
  4. {  
  5.    float4 Pos: POSITION;  
  6.    float texCoord: TEXCOORD;  
  7. };  
  8.   
  9. VS_OUTPUT main(float4 Pos: POSITION)  
  10. {  
  11.    VS_OUTPUT Out;  
  12.   
  13.    // Transform vertex position  
  14.    Out.Pos = mul(view_proj_matrix, Pos);  
  15.   
  16.    // Pass the scaled depth value as a texture coordinate  
  17.    Out.texCoord = depthScale * Out.Pos.z;  
  18.   
  19.    return Out;  
  20. }  


Ps中直接return depth。
  1. float4 main(float depth: TEXCOORD) : COLOR   
  2. {  
  3.    // Simply output the depth to the texture as a color  
  4.    return depth;  
  5. }  




判读边缘的条件变成视线和法线的夹角。


float edge = 1 - (dot(Normal,ViewVec)>0.07);


这样的方法在面数比较低的模型上表现不是很好。

还有一种方法就是渲两次,第一遍先将对模型进行缩放,第二遍像渲染模型。这样的方法不管是渲染内边缘还是外边缘都是可以的,但是你没办法保证边缘的宽度一致,因为绘制并不是屏幕空间的。


Toon Shading

完成了轮廓绘制了之后,下一步就是shading 了,这里的shading分为两个部分,一个是textureing,另一个是lighting。

卡通渲染的贴图特点是颜色数量很少,有两种处理的方式,一种是不用贴图,直接将颜色赋给顶点,另一种使用比较卡通的贴图。
将前面的边缘绘制和卡通风格的贴图整合一下,结果如下



接下来加一下 光照
光照直接在vs中计算,只用diffuse就行 copy
  1. float4x4 view_proj_matrix;  
  2. float4 Light1_Position;  
  3. float4 Light1_Attenuation;  
  4. float4 Light1_Color;  
  5. struct VS_OUTPUT   
  6. {  
  7.    float4 Pos:       POSITION;  
  8.    float2 TexCoord:   TEXCOORD0;  
  9.    float2 Color:      COLOR0;  
  10. };  
  11.   
  12. float4 Light_PointDiffuse(float3 VertPos, float3 VertNorm, float3 LightPos,  
  13.                           float4 LightColor, float4 LightAttenuation)  
  14. {  
  15.    // Determine the distance from the light to the vertex and the direction  
  16.    float3 LightDir = LightPos - VertPos;  
  17.    float  Dist = length(LightDir);  
  18.    LightDir = LightDir / Dist;  
  19.   
  20.    // Compute distance based attenuation. This is defined as:  
  21.    // Attenuation = 1 / ( LA.x   LA.y*Dist   LA.z*Dist*Dist )  
  22.    float DistAttn = clamp(0,1, 1 / ( LightAttenuation.x    
  23.                                      LightAttenuation.y * Dist     
  24.                                      LightAttenuation.z * Dist * Dist ));  
  25.   
  26.    // Compute suface/light angle based attenuation defined as dot(N,L)  
  27.    // Note : This must be clamped as it may become negative.  
  28.    float AngleAttn = clamp(0, 1, dot(VertNorm, LightDir) );  
  29.    
  30.    // Compute final lighting  
  31.    return LightColor * DistAttn * AngleAttn;  
  32. }  
  33.   
  34.   
  35. VS_OUTPUT vs_main(float4 inPos: POSITION, float3 inNormal: NORMAL,float2 inTxr: TEXCOORD0)  
  36. {  
  37.    VS_OUTPUT Out;  
  38.   
  39.    // Compute the projected position and send out the texture coordinates  
  40.    Out.Pos = mul(view_proj_matrix, inPos);  
  41.    Out.TexCoord = inTxr;  
  42.   
  43.    // Output the ambient color  
  44.    float4 Color = float4(0.4,0.4,0.4,1);  
  45.   
  46.    // Compute light contribution  
  47.    Color  = Light_PointDiffuse(inPos, inNormal, Light1_Position,   
  48.                                Light1_Color, Light1_Attenuation);  
  49.   
  50.    // Output Final Color  
  51.    Out.Color = Color;  
  52.   
  53.    return Out;  
  54. }  



Ps利用取余操作对对diffuse进行分段。
  1. sampler Texture0;  
  2. float4 ps_main( float2 Tex: TEXCOORD0, float4 Diffuse:COLOR0) : COLOR  
  3. {  
  4.     // Clamp diffuse to a fixed set of values and modulate with  
  5.     // the texture color  
  6.     Diffuse = (int)(Diffuse * 4) / 4.0;  
  7.     return Diffuse*tex2D(Texture0, Tex);  
  8. }  


结果




手绘效果

思路非常简单:利用6张手绘贴图表示不同的光照强度,渲染物体的时候根据光照信息blend相应的贴图,问题就转化为求6张贴图的混合系数的问题。



在shader中用两个float3来记录每个贴图的blend值,其实函数根据diffuse的光强。


float hatchFactor = diffuse * 6.0;


这个hatchFactor就是对diffuse进行了一些缩放,然后根据真个hatchFactor进行对应的判断。
  1. float4 Light_Direction;  
  2. float4x4 view_matrix;  
  3. float4x4 view_proj_matrix;  
  4. struct VS_OUTPUT  
  5. {  
  6.    float4 Pos           : POSITION0;  
  7.    float2 TexCoord      : TEXCOORD0;  
  8.    float3 HatchWeights0 : TEXCOORD1;  
  9.    float3 HatchWeights1 : TEXCOORD2;  
  10. };  
  11.   
  12. VS_OUTPUT vs_main( float4 inPos: POSITION0, float3 inNormal: NORMAL0,  
  13.                    float2 inTexCoord : TEXCOORD0 )  
  14. {  
  15.    VS_OUTPUT Out;   
  16.   
  17.    // Compute projected position and transfer texture  
  18.    // coordinates for the object  
  19.    Out.Pos = mul( view_proj_matrix, inPos );  
  20.    Out.TexCoord = inTexCoord;  
  21.   
  22.    // Determine a simple diffuse lighting component based  
  23.    // on a directional light in view space  
  24.    float3 pos_world    = mul( view_matrix, inPos );  
  25.    float3 normal_world = normalize(mul( (float3x3)view_matrix,   
  26.                          inNormal ));  
  27.    float  diffuse = min(1.0,max(0,dot(-Light_Direction,normal_world)));  
  28.    diffuse = diffuse * diffuse;  
  29.    diffuse = diffuse * diffuse;  
  30.   
  31.    float  hatchFactor = diffuse * 6.0;  
  32.    float3 weight0 = 0.0;  
  33.    float3 weight1 = 0.0;  
  34.   
  35.    // Determine the weights for the hatch textures based on the   
  36.    // hatch factor which is simply proportional to the diffuse  
  37.    // lighting. In other words, the more lit the object, the less  
  38.    // dense the hatching will be.  
  39.    if (hatchFactor>5.0) { weight0.x = 1.0; }  
  40.    else if (hatchFactor>4.0)   
  41.    {   
  42.       weight0.x = 1.0 - (5.0 - hatchFactor);  
  43.       weight0.y = 1.0 - weight0.x;  
  44.    }  
  45.    else if (hatchFactor>3.0)  
  46.    {  
  47.       weight0.y = 1.0 - (4.0 - hatchFactor);  
  48.       weight0.z = 1.0 - weight0.y;  
  49.    }  
  50.    else if (hatchFactor>2.0)  
  51.    {  
  52.       weight0.z = 1.0 - (3.0 - hatchFactor);  
  53.       weight1.x = 1.0 - weight0.z;  
  54.    }  
  55.    else if (hatchFactor>1.0)  
  56.    {  
  57.       weight1.x = 1.0 - (2.0 - hatchFactor);  
  58.       weight1.y = 1.0 - weight1.x;  
  59.    }  
  60.    else if (hatchFactor>0.0)  
  61.    {  
  62.       weight1.y = 1.0 - (1.0 - hatchFactor);  
  63.       weight1.z = 1.0 - weight1.y;  
  64.    }  
  65.    Out.HatchWeights0 = weight0;  
  66.    Out.HatchWeights1 = weight1;  
  67.   
  68.    return Out;  
  69. }  



Ps中就比较简单了
  1. sampler Hatch0;  
  2. sampler Hatch1;  
  3. sampler Hatch2;  
  4. sampler Hatch3;  
  5. sampler Hatch4;  
  6. sampler Hatch5;  
  7. sampler Base;  
  8. float4 ps_main( float2 TexCoord: TEXCOORD0,  
  9.                 float3 HatchWeights0: TEXCOORD1,  
  10.                 float3 HatchWeights1 : TEXCOORD2) : COLOR  
  11. {  
  12.     // Sample eatch hatch texture based on the object's texture  
  13.     // coordinates and weight the pattern based on the factor  
  14.     // determined from the lighting.   
  15.     float4 hatchTex0 = tex2D(Hatch0,TexCoord) * HatchWeights0.x;  
  16.     float4 hatchTex1 = tex2D(Hatch1,TexCoord) * HatchWeights0.y;  
  17.     float4 hatchTex2 = tex2D(Hatch2,TexCoord) * HatchWeights0.z;  
  18.     float4 hatchTex3 = tex2D(Hatch3,TexCoord) * HatchWeights1.x;  
  19.     float4 hatchTex4 = tex2D(Hatch4,TexCoord) * HatchWeights1.y;  
  20.     float4 hatchTex5 = tex2D(Hatch5,TexCoord) * HatchWeights1.z;  
  21.   
  22.     // Combine all patterns, the final color is simply the sum  
  23.     // of all hatch patterns.      
  24.     float4 hatchColor = hatchTex0    
  25.                         hatchTex1    
  26.                         hatchTex2    
  27.                         hatchTex3    
  28.                         hatchTex4    
  29.                         hatchTex5;  
  30.     return hatchColor;  
  31. }  



最终结果

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