代码改变世界

延迟渲染之由深度重建世界位置

2014-12-08 23:23  风恋残雪  阅读(1167)  评论(0编辑  收藏  举报

前面一篇文章介绍了混合方式的延迟渲染(Hybrid deferred shading),本文来讲解一下其中的一个细节,由深度构建世界位置,我们这里假定使用D3D9以及左手坐标系。

我在这里介绍两种方式,这两种方式也就跟实现方式有一定的关系。

方法1:

如果我们在第一遍中写深度时使用的是经过透视除法的z值

 1 // 第一遍时输出深度
 2 float4x4 matViewProjection;
 3 struct VS_INPUT 
 4 {
 5    float4 Position : POSITION0;
 6    
 7 };
 8 struct VS_OUTPUT 
 9 {
10    float4 Position : POSITION0;
11    float2 Depth    : TEXCOORD0;   
12 };
13 
14 VS_OUTPUT vs_main( VS_INPUT Input )
15 {
16    VS_OUTPUT Output;
17    Output.Position = mul( Input.Position, matViewProjection );
18    // 除w为非线性运算,需要放到ps除
19    Output.Depth = Output.Position.zw; 
20    return( Output );  
21 }
22 
23 float4 ps_main(float2 iDepth : TEXCOORD0) : COLOR0
24 {   
25     // 输出除w后的值
26    return iDepth.x / iDepth.y;
27 }
28 
29 // 在计算光照时获取世界位置,此处仅为示例代码
30 struct VS_OUTPUT
31 {
32    float4 Pos: POSITION;
33    float4 TexCoord: TEXCOORD0;
34 };
35 VS_OUTPUT vs_main( float4 Pos: POSITION )
36 {
37    VS_OUTPUT Out = (VS_OUTPUT) 0; 
38    Out.Pos = float4(Pos.xy, 0.0, 1.0);
39    Out.TexCoord.x = Out.Pos.x * 0.5 + 0.5;
40    Out.TexCoord.y = 1.0 - (Out.Pos.y * 0.5 + 0.5);
41    Out.TexCoord.zw = Pos.xy;
42    return Out;
43 }
44 
45 float4x4 fInvMatViewProj;
46 sampler2D depthTexture;        // 第一遍保存的深度纹理
47 float4 ps_main(float4 iUV : TEXCOORD0) : COLOR0
48 {   
49    float fDepth = tex2D(depthTexture, iUV.xy).x;
50    float4 vPos =  mul (float4(iUV.zw, fDepth, 1.0), fInvMatViewProj);
51    // 需要除以w得到世界位置
52    vPos = vPos / vPos.w;
53    return vPos; 
54 }

如果是在d3d10或者更高版本的dx中,我们可以直接读取深度纹理,这样就可以减少一张RT,所以用这种 方法会比较省。D3D9下通过一些方法也可以得到,比如INTZ或者驱动暴露给D3D9的接口[1]。

第二种方法为直接保存摄像机空间中的z除以远裁剪面后的值。

 1 // 第一遍时输出摄像机空间的z值除以远裁剪面的值
 2 float4x4 matViewProjection;
 3 float fFarClipPlane;
 4 struct VS_INPUT 
 5 {
 6    float4 Position : POSITION0;
 7    
 8 };
 9 struct VS_OUTPUT 
10 {
11    float4 Position : POSITION0;
12    float Depth    : TEXCOORD0;
13    
14 };
15 
16 VS_OUTPUT vs_main( VS_INPUT Input )
17 {
18    VS_OUTPUT Output;
19 
20    Output.Position = mul( Input.Position, matViewProjection );
21    // z值除以远裁剪面的值,此处为线性运算,帮可以放在vs中做除法,利用硬件插值传给ps使用
22    Output.Depth = Output.Position.w / fFarClipPlane ; 
23    return( Output );  
24 }
25 
26 float4 ps_main(float iDepth : TEXCOORD0) : COLOR0
27 {   
28    return iDepth;  
29 }
30 
31 
32 
33 // 在计算光照时获取世界位置,利用相似三角形来求得。此处仅为示例代码
34 struct VS_OUTPUT
35 {
36    float4 Pos         : POSITION;
37    float2 UV          : TEXCOORD0;
38    // 此处为世界空间内的摄像机远裁剪面四个点的值分别减去摄像机位置得到的向量
39    float4 vFarCorner  : TEXCOORD1;        
40 };
41 VS_OUTPUT vs_main( float4 Pos: POSITION , float4 vFarCorner : TEXCOORD0)
42 {
43    VS_OUTPUT Out = (VS_OUTPUT) 0; 
44    Out.Pos = float4(Pos.xy, 0.0, 1.0);
45    Out.vFarCorner = vFarCorner;
46    Out.UV.x = Out.Pos.x * 0.5 + 0.5;
47    Out.UV.y = 1.0 - (Out.Pos.y * 0.5 + 0.5);
48    return Out;
49 }
50     
51 float3 vCamPos;
52 sampler2D depthTexture; // 第一遍保存的深度纹理
53 float4 ps_main(float2 iUV : TEXCOORD0, float4 vFarCorner : TEXCOORD1) : COLOR0
54 {   
55    float fDepth = tex2D(depthTexture, iUV.xy).x;
56    // 根据相似三角形,反算世界位置。
57    float4 vPos = float4(vCamPos + fDepth * vFarCorner, 1.0f);
58    return vPos;
59 }

  我们目前使用的是第二种方法,效率也还好,不过如果我们不考虑更大范围的支持老硬件,可能会使用参考文献[1]中的方法直接读取深度,这样第一种方法就比较合适了,当然还有其它一些方法,我也没有仔细去研究,读者如果有兴趣可以自己google,也可以参考[2]。相似三角形原理如下图所示:

其中x.z/fFarDist即为上面代码中的 1 Output.Depth = Output.Position.w / fFarClipPlane ;  ,乘以fFarVec这样就得到了一个向量,再加上世界空间中的摄像机位置就得到了最终的世界位置 。

  最近看到自己的文章被别的网站盗用了,地址在这,转载也不写,文章最后的地址也是他们自己的,好像文章是他们写的一样--!对这种无耻的行为感到有点无奈,希望以后如果觉得文章还可以,转载文章请说明下,并注明出处,这也是对别人最基本的尊重吧。

参考文章:

[1] http://aras-p.info/texts/D3D9GPUHacks.html

[2] http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/