Directx11教程二十五之ZBuffer(深度缓存,DepthBuffer)

好文,感谢博主,原文链接:http://blog.csdn.net/qq_29523119/article/details/52894710

这节教程的结构如下:




一,DepthBuffer(深度缓存)

深度缓存(Z缓存值)是用来记录视截体的每个像素的深度,其值范围是[0,1],在D3D11中 默认近裁剪面(Near_Z)上面的像素深度为0,而远裁剪面上的像素的像素深度为1, 注意stencilBuffer一般都是跟DepthBuffer绑定在一起的,创建DepthBuffer 时,指定数据格式 DXGI_FORMAT_D24_UNORM_S8_UINT.,   
其中深度缓存值中有24位用来存储真正的深度缓存值(Z值),有8位用来存储模板缓存值。

如图所示:



默认情况下   对于背后缓存的同一个位置的像素,Z缓存值小的将代替Z缓存值大的像素,即所谓的Z缓存测试剔除。

那么Z缓存值怎么求出来呢?

我一步一步的说出来,首先来看看D3D11渲染流水线:







首先说明一点,真正意义上的透视投影过程由两步组成:(1)乘以投影矩阵   (2)透视除法

第一步:乘以投影矩阵 ( Projection matrix ),变换到齐次裁剪空间 ( Homogeneous clip space ),并进行CVV裁剪
假设在相机空间(View Coordinate System) 一个顶点的坐标为(x,y,z,1),则该变乘以投影矩阵( Projection matrix)后,变换到齐次裁剪空间,
此时坐标变为(Xp,Yp,Zp,Wp),此时W不为1,因此称作为齐次坐标,给出此时坐标分值Xp,Yp,Zp的范围, 
-Wp=<Xp<=Wp   -Wp=<Yp<=Wp     0=<Zp<=Wp

这时将进行CVV裁剪算法( 其实这裁剪算法 也不能算CVV裁剪,下面会说原因)

第二步,进行透视除法,变换到NDC空间(Normalized Device Coordinate)

有上面可以知道齐次裁剪空间内 顶点 (Xp,Yp,Zp,Wp)  -Wp=<Xp<=Wp   -Wp=<Yp<=Wp     0=<Zp<=Wp,

进行透视除法,就是将顶点(Xp,Yp,Zp,Wp)每个分值除以Wp,得到(Xn,Yn,Zn,1),其中
  (Xn,Yn,Zn,1)

规则观察体(Canonical View Volume)称为CVV,人们称之为CVV,感觉大量人误认为是单位立方体(或者长方体),这里我跟着他们一起错,规定CVV也是单位立方体,而不是规则观察体。(其实按单词字面意思,立方体或者长方体不管是不是单位的都应该算是CVV,毕竟都是规则的,但是这里我跟着他们一起错吧,就认为是单位的)
上面说了这些也就是想表达一个意思:真正的透视投影过程不仅仅是乘以投影矩阵就行了,还需要进行接下来的透视除法。
当然很多人以为是在NDC空间进行裁剪的,也就是(Xn,Yn,Zn,1),-1=<Xn<=1,-1=<Yn<=1,0=<Zn<=1这样的空间进行裁剪,这样是错误的想法







这里给出两篇大牛的文章,深入论述了““透视投影变换的””的来龙去脉。





这里放出文章部分截图 证明我的观点:(这是关于opengl的投影变换原理说明,-1=< Z(opengl)<=1,   0= < Z (d3d11 )<1.0,但无伤大雅,照样可以理解)
不过这作者感觉有些论述自相矛盾,原因是最上面3D渲染流水线图也是该作者给出的,文章在这里   http://blog.csdn.net/popy007/article/details/5120158




然而我们在Shader里指定系统签名“”SV_POSITION” 之后 裁剪算法和透视除法是显卡自动自动进行.,不用我们操心,而如果使用我们自己定义的签名“”POSITION“”,透视除法并不会自动进行,也就是乘以投影矩阵后(投影变换后)得到的坐标(Xp,Yp,Zp,Wp)   -Wp=<Xp<=Wp   -Wp=<Yp<=Wp     0=<Zp<=Wp

       那么在齐次裁剪空间     0.0=<Zp/Wp<=1.0      Z缓存值=Zp/Wp;


当然我这里主要是关于D3D11渲染管线的,opengl的我不清楚,嘎嘎。大笑


下面放出我的Shader代码:
[cpp]  view plain  copy
  1. //VertexShader  
  2. cbuffer CBMatrix:register(b0)  
  3. {  
  4.     matrix World;  
  5.     matrix View;  
  6.     matrix Proj;  
  7. };  
  8.   
  9. struct VertexIn  
  10. {  
  11.     float4 Pos:POSITION;  
  12. };  
  13.   
  14.   
  15. struct VertexOut  
  16. {  
  17.     float4 Pos:SV_POSITION;  
  18.     float4 Pos_Depth:POSITION;  
  19. };  
  20.   
  21.   
  22. VertexOut VS(VertexIn ina)  
  23. {  
  24.     VertexOut outa;  
  25.   
  26.     //------齐次裁剪空间-----(x,y,z,w)  -w=<x<=w -w=<y<=w  0=<z<=w  由于标记“SV_POSITION”,貌似之后会进行CVV裁剪,       // 这时透视除法和CVV裁剪是显卡自动进行的  
  27.     outa.Pos = mul(ina.Pos, World);  
  28.     outa.Pos = mul(outa.Pos, View);  
  29.     outa.Pos = mul(outa.Pos, Proj);  
  30.   
  31.     //顶点坐标,由于没标记“SV_POSITION”,不会自动进透视除法,一直停留在齐次裁剪空间  
  32.     outa.Pos_Depth = outa.Pos;  
  33.     return outa;  
  34. }  
  35.   
  36. float4 PS(VertexOut outa) : SV_Target  
  37. {  
  38.     float4 color;  
  39.     float Z;  
  40.     //求出每个像素的Z缓存值  0.0=<(Z缓存值=Zp/Wp)<=1.0  
  41.     Z = outa.Pos_Depth.z / outa.Pos_Depth.w;  
  42.   
  43.     //当Z缓存值小于0.9f时,显示为红色,占据了大部分的屏幕,Z越小,越接近屏幕  
  44.     if (Z < 0.9f)  
  45.     {  
  46.         color = float4(1.0, 0.0f, 0.0f, 1.0f);  
  47.     }  
  48.   
  49.     //当Z缓存值大于0.9f而小于0.95f时为绿色  
  50.     if (Z > 0.9f)  
  51.     {  
  52.         color = float4(0.0, 1.0f, 0.0f, 1.0f);  
  53.     }  
  54.   
  55.     //当Z缓存值大于0.925f为蓝色  
  56.     if (Z > 0.925f)  
  57.     {  
  58.         color = float4(0.0, 0.0f, 1.0f, 1.0f);  
  59.     }  
  60.   
  61.     return color;  
  62.       
  63. }  



放出我的程序运行图:



最后来了这样在PixelShader求出Z缓存值有什么作用呢?我们可以根据Z缓存的大小来决定进不进行某些步骤的渲染,比如在游戏中离屏幕远的(比如Z缓存值大于0.8f)物体,可以不使用NormalMap和SpecularMap等等效果,可以不进行阴影的渲染等等。

下面我的源代码链接:

猜你喜欢

转载自blog.csdn.net/a359877454/article/details/78328038
今日推荐