Shader学习(三):DOF(景深)

发表于2017-09-06
评论0 4.3k浏览

下面和大家介绍的是一个学习shader着色器的系列,想使用好shader的开发人员可以学习下。下面介绍第三篇关于DOF(景深)

DOF(景深)

最简单的现象就是当你用眼睛聚焦到前景的地方,后面就会变模糊, 当用眼睛聚焦到远景的地方,前就会变模糊,相机也会有同样的情况,通过调节光圈和焦距就很容易出现景深的效果。
如果要说景深形成的原理的话,可以用小孔成像来说一天,但是在图形学里面,要做的就一件事 Faking it!

镜头前的东西被模糊,远离镜头的东西比较清晰

Box filter 过于简单,高斯模糊需要两个pass,这里用一个稍微复杂的filter,一次pass就搞定,滤波器是这样的



具体的pixel shader里面是这样的

  1. float fInverseViewportWidth;  
  2. float fInverseViewportHeight;  
  3. sampler Texture0;  
  4.   
  5. const float4 samples[9] =   
  6. {  
  7. -1.0, -1.0, 0, 1.0/16.0,  
  8. -1.0, 1.0, 0, 1.0/16.0,  
  9. 1.0, -1.0, 0, 1.0/16.0,  
  10. 1.0, 1.0, 0, 1.0/16.0,  
  11. -1.0, 0.0, 0, 2.0/16.0,  
  12. 1.0, 0.0, 0, 2.0/16.0,  
  13. 0.0, -1.0, 0, 2.0/16.0,  
  14. 0.0, 1.0, 0, 2.0/16.0,  
  15. 0.0, 0.0, 0, 4.0/16.0  
  16. };  
  17.   
  18. float4 ps_main(float2 texCoord: TEXCOORD0) : COLOR  
  19. {  
  20.    float4 col = float4(0,0,0,0);  
  21. // Sample and output the averaged colors  
  22.    for(int i=0;i<9;i )  
  23.    col  = samples[i].w*tex2D(Texture0,texCoord   
  24.    float2(samples[i].x*fInverseViewportWidth,  
  25.    samples[i].y*fInverseViewportHeight));  
  26.    return col;  
  27. }  


基于Depth Impostor的DOF

基本的原理就是首先渲染一个模糊的RT,这个模糊的RT可能会用到很多种pass,比如之前的高斯模糊,还有上面所说的filter,进行多次叠加。
然后根据之前rt里的alpha通道的值进行blend。

在RenderMonkey中具体的做法首先添加一个下面几个变量



相机的几个参数



在绘制模型的时候,要把对应的深度存储到alpha中

 

VS

  1. float4x4 view_proj_matrix;  
  2. float far_clip;  
  3. struct VS_OUTPUT   
  4. {  
  5.    float4 Pos:     POSITION;  
  6.    float2 Txr1:    TEXCOORD0;  
  7.    float1 Depth:   TEXCOORD1;  
  8. };  
  9.   
  10. VS_OUTPUT vs_main(   
  11.    float4 inPos: POSITION,   
  12.    float2 Txr1: TEXCOORD0  
  13. )  
  14. {  
  15.    VS_OUTPUT Out;  
  16.    float4 OutPos;  
  17.    float4 offset;  
  18.    offset.x = 200;  
  19.    offset.y = 0;  
  20.    offset.z = 0;  
  21.    offset.w = 0;   
  22.    // Compute the position of the vertex  
  23.    Out.Pos = OutPos = mul(view_proj_matrix, inPos   offset);  
  24.    Out.Txr1 = Txr1;  
  25.   
  26.    // Send the depth to the pixel shader for encoding  
  27.    Out.Depth = OutPos.w/far_clip;  
  28.   
  29.    return Out;  
  30. }  

pixel shader
  1. float Near_Range;  
  2. float Far_Range;  
  3. float Near_Dist;  
  4. float Far_Dist;  
  5. sampler Texture0;  
  6. float4 ps_main(   
  7.    float4 inDiffuse: COLOR0,   
  8.    float2 inTxr1: TEXCOORD0,  
  9.    float1 Depth: TEXCOORD1  
  10. ) : COLOR0  
  11. {  
  12.   // Compute blur factor based on   near and far focus planes  
  13.   float Blur = max(clamp(0,1, 1 - (Depth-Near_Dist)/Near_Range),  
  14.                 clamp(0,1, (Depth-(Far_Dist-Far_Range))/Far_Range));  
  15.   
  16.   //  Output constant color:  
  17.   return float4(tex2D(Texture0,inTxr1).rgb,Blur);  
  18. }  

注意,Shader中所有的运算都是行主序!
注意,Shader中所有的运算都是行主序!
注意,Shader中所有的运算都是行主序!



具体来看下这个运算流程,
在VS中

  1. Out.Pos = OutPos = mul(view_proj_matrix, inPos   offset);  

经过这一步的计算,Out.Pos的w取值范围就是(0, far_clip)
  1. Out.Depth = OutPos.w/far_clip;  


这一步将深度映射到0到1.

 

再看ps

  1. float Blur = max(clamp(0,1, 1 - (Depth-Near_Dist)/Near_Range), clamp(0,1,(Depth-(Far_Dist-Far_Range))/Far_Range));  

这里是计算Blur值,也是后面进行blend的参数。Blend的取值范围如下



在nearRange之前和FarDist之后,取值都是1,中间部分是0,其余的部分是在0到1之前线性变化。根据NearRange,NearDis,FarRange,FarDist这几个值,就可以获得不同的景深效果。

模糊的rt处理这里就不说了,最好叠加个两三次。

最后在present的时候,只需要根据alpha值进行两张rt的Blend就可以了





看一下深度的Texture

取Blur值
  1. return float4(tex2D(Texture0,inTxr1).rgb,tex1D(Texture1,Depth).a);  

这样就省去了每次都去计算一遍算式的成本,特别是当blur的计算特别复杂的时候。

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

标签: