DX10 Shadow Volumn Sample Code的Bug修正
在DX10 Shadow Volumn Sample中的DetectAndProcessSilhouette这个函数意思是说边缘检测,但实际啥都没做,只是沿伸了顶点生成shadow vloumn。为了证明存在的BUG,换了个简单的模型来看一下shadow volumn的样子:
在这幅图中,很明显,反向面的顶点也做了不必要的沿伸。
下图是修正过的效果图:
明显shadow volumn 要更干净些了,而且结果完全正确。
再换回到原先的模型看一下效果。
以下两幅图是原来的代码结果:
以下是修正的结果:
可以看出,结果是没问题的。
下面分析代码是如何修改,修改过的代码段如下:
// // Helper to detect a silhouette edge and extrude a volume from it // void DetectAndProcessSilhouette( float3 N, // Un-normalized triangle normal GSShadowIn v1, // Shared vertex GSShadowIn v2, // Shared vertex GSShadowIn vAdj, // Adjacent triangle vertex inout TriangleStream<PSShadowIn> ShadowTriangleStream // triangle stream ) { float3 NAdj = cross( v2.pos - vAdj.pos, v1.pos - vAdj.pos ); float3 AdjDir = normalize(g_vLightPos - vAdj.pos); if(dot(AdjDir, NAdj) < 0.0f) { float3 outpos[4]; float3 extrude1 = normalize(v1.pos - g_vLightPos); float3 extrude2 = normalize(v2.pos - g_vLightPos); outpos[0] = v1.pos + g_fExtrudeBias*extrude1; outpos[1] = v1.pos + g_fExtrudeAmt*extrude1; outpos[2] = v2.pos + g_fExtrudeBias*extrude2; outpos[3] = v2.pos + g_fExtrudeAmt*extrude2; // Extrude silhouette to create two new triangles PSShadowIn Out; for(int v=0; v<4; v++) { Out.pos = mul( float4(outpos[v],1), g_mViewProj ); ShadowTriangleStream.Append( Out ); } ShadowTriangleStream.RestartStrip(); } }
这段代码添加了个条件:
float3 AdjDir = normalize(g_vLightPos - vAdj.pos); if(dot(AdjDir, NAdj) < 0.0f) {}
原理是这样的,如果一个三角形有任意两个顶点的法线分别和入射光线的的点乘结果符号不相同,则这个三角形一定和边缘相交。
下面是原理图,其中dot(v1,n1)>0, dot(v2,n2)<0
图片来源于ShaderX3,在章节1.5中也有这个方法的介绍。
在进入DetectAndProcessSilhouette之前,先执行的是这段代码:
// // GS for generating shadow volumes // //triangleadj assumes that every other vertex is an adjacent vertex [maxvertexcount(18)] void GSShadowmain( triangleadj GSShadowIn In[6], inout TriangleStream<PSShadowIn> ShadowTriangleStream ) { // Compute un-normalized triangle normal float3 N = cross( In[2].pos - In[0].pos, In[4].pos - In[0].pos ); // Compute direction from this triangle to the light float3 lightDir = g_vLightPos - In[0].pos; //if we're facing the light if( dot(N, lightDir) > 0.0f ) { // For each edge of the triangle, determine if it is a silhouette edge DetectAndProcessSilhouette( lightDir, In[0], In[2], In[1], ShadowTriangleStream ); DetectAndProcessSilhouette( lightDir, In[2], In[4], In[3], ShadowTriangleStream ); DetectAndProcessSilhouette( lightDir, In[4], In[0], In[5], ShadowTriangleStream ); //near cap using g_fExtrudeBias PSShadowIn Out; for(int v=0; v<6; v+=2) { float3 extrude = normalize(In[v].pos - g_vLightPos); float3 pos = In[v].pos + g_fExtrudeBias*extrude; Out.pos = mul( float4(pos,1), g_mViewProj ); ShadowTriangleStream.Append( Out ); } ShadowTriangleStream.RestartStrip(); //far cap (reverse the order - back face) using g_fExtrudeAmt for(int v=4; v>=0; v-=2) { float3 extrude = normalize(In[v].pos - g_vLightPos); float3 pos = In[v].pos + g_fExtrudeAmt*extrude; Out.pos = mul( float4(pos,1), g_mViewProj ); ShadowTriangleStream.Append( Out ); } ShadowTriangleStream.RestartStrip(); } }
其中,由这个条件所知if( dot(N, lightDir) > 0.0f ),我们一开始取的是正向面,这也是为什么之后要取if(dot(AdjDir, NAdj) < 0.0f)的原因了。triangleadj的顶点结构如下:
References:
[1] ShaderX3 Chaper 1.5
[2] http://msdn.microsoft.com/en-us/library/windows/desktop/bb509609(v=vs.85).aspx
posted on 2012-06-07 13:47 CGBeginner 阅读(493) 评论(0) 编辑 收藏 举报