// Projecti=
on matrix for the light<=
/span>D3DXMatrixPerspectiveFovLH(&matPro=
j, D3DXToRadian(30.0f),1.0f=, 1.0f, 1024.0f);
//ऩ=
4;际上作者在例程中使&=
#29992;的是D3DXMatrixOrthoLH( &matProj=
, 45.0f, 45.0f, 1.0f,
//1024.0f )。=
6825;个函数所构造的project矩=
8453;与D3DXMatrixPerspective=
FovLH()构造的=
span>//不同之处在=
20110;:它没有透视效果z=
90;即物体的大小与视点=
和物体的距离没有关=
1995;。显然例//程中模拟的=
26159;平行光源(direction light),而这里=
27169;拟的是聚光灯源(spot light不知翻译得=
23545;不对?)
=
// Concatenate the world matrix with the above// two to g=
et the required matrix<=
/span>matLightViewProj=3D matWorl=
d * matView * matProj;<=
/o:p>
下面是渲染场景=
深度的顶点渲染和像=
2032;渲染的代码:
=
// Shadow generation vertex shader<=
/span>structVSOUTPUT_SHADOW<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>{
=float4 vPosition: POSITION;floatfDepth =
;: TEXCOORD0;=
};<=
/o:p>VSOUTPUT_SHA=
DOW VS_Shadow( float4 inPosi=
tion : POSITION ){
=// Output struct<=
/span>VSOUTPUT_SHADOW OUT =3D (VSOUTPUT=
_SHADOW)0;// Output the transforme=
d positionOUT.vPosition =3D mul( inPosition, g_ma=
tLightViewProj );// Output the scene dept=
hOUT.fDepth =3D OUT.vPosition.z;return OUT;}
=
这里我们将顶点=
的位置与变换矩阵相=
0056;,并将变换后的z值作为深=
24230;。在像素渲染中将ę=
45;度值以颜色(color)的=
方式输出。
float4PS_Shadow( VSO=
UTPUT_SHADOW IN ) : COLOR0{
=// Output the scene dept=
hreturnfloat4( IN.fDepth, IN.fDepth, IN.fDepth, 1.0f );<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>}
=
瞧,=
我们完成了阴影映射=
2270;,下面就是以颜色ਬ=
1;式输出的阴影映射图&=
#65292;深蓝色部分表明较=
567;的深度值,浅蓝色部=
;分表明较大的深度值=
12290;
下面=
,我们要把场景的带=
8452;影的部分渲染到并ߎ=
1;立即显示的缓冲中,&=
#20351;我们可以进行模糊=
788;理,然后再将它投射回屏幕。ཛ=
8;先把场景的阴影部分&=
#28210;染到一幅屏幕大小=
340;定点纹理中。
=
// Create the screen-sized buffer map<=
/span>if(FAILED( g_pd3dDevice->CreateTexture( SCREEN_WIDTH,
<=
/o:p> =
;SCREEN_HEIGHT, 1, D3DUSAGE_RENDERTARGET,D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, =
;&g_pScreenMap, NULL ) ) ){=
MessageBox( g_hWnd, "Unable to create screen map!=
", =
;"Error", MB_OK | MB_ICONERROR );return E_FAIL;
=
o:p>}
=// Grab the=
texture's surface<=
/span>g_pScreenMap=
->GetSurfaceLevel( 0, & g_pScreenSurf );
<=
/span>
为了获得投=
24433;纹理坐标(projective texture coordinates),我们需要一=
010;纹理矩阵,作用是把=
;投影空间(projection space=
65289;中的位置变换到纹ĩ=
02;空间(texture space=
65289;中去。
=
// Generate the texture matrix<=
pre>floatfTexOffs =3D 0.5 + (0.5 / (float)SHADOW_MAP_SIZE);D3DXMATRIX <=
span
class=3DGramE>matTexAdj( 0.5f,&=
nbsp;0.0f,0.0f, 0.0f,&nb=
sp;0.0f,-0.5f,0.0f, 0.0f,<=
pre> =
; <=
/span>0.0f,0.0f,1.0f, 0.0f,<=
pre> =
; <=
/span>fTexOffs, fTexOffs, 0.0f, 1.0f );//这个矩阵是把projection space中范围为[-1,1]的x,y坐标值=
转换到纹理空间中=
//[0,1]的范围中去Ӎ=
0;注意y轴的方=
向改变了。那个(0.5 / (float)SHADOW_MAP_SIZE)
//的值有什么作用=
105;还不清楚,原文也没=
;有说明。<=
/span><=
/o:p>matTexture=3D matLigh=
tViewProj * matTexAdj;<=
/o:p>
我们像往常=
37027;样通过深度的比较Ĉ=
69;获得阴影因数,但随=
后并不是像平常那样=
6755;出整个照亮了的场ਾ=
3;,我们只输出阴影因&=
#25968;。下面的顶点渲染=
644;像素渲染完成这个工=
;作。
=
// Shadow mapping vertex shader<=
/span>structVSOUTPUT_UNLIT{
=float4 vPosition: POSITION;float4 vTexCoord: TEXCOORD0;floatfDepth =
;: TEXCOORD1;<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>};<=
/o:p>VSOUTPUT_UNL=
IT VS_Unlit( float4 inPosit=
ion : POSITION ){
=// Output struct<=
/span>VSOUTPUT_UNLIT OUT =3D (VSOUTPUT_=
UNLIT)0;<=
/pre>// Output the transforme=
d positionOUT.vPosition =3D mul( inPosition, g_ma=
tWorldViewProj );<=
/o:p>// Output the projective=
texture coordinatesOUT.vTexCoord =3D mul( inPosition, g_ma=
tTexture );<=
/o:p>// Output the scene dept=
hOUT.fDepth =3D mul( inPosition, g_matLightViewProj ).z;<=
/o:p>return OUT;}
=
我们采用percentage closer filtering (PCF)来平滑锯齿边缘Ӎ=
0;为了完成“PCF”,&=
#25105;们简单的对周围8个纹理点=
36827;行采样,并取得它Ê=
04;深度比较的平均值。=
=
// Shadow mapping pixel shader//=
span>注意这里采ஷ=
2;的是tex2Dproj()函数以&=
#21450;转换到纹理空间的=
521;量(x,y,z,w)对纹理进//行采样。这与d3d9sdk中shadowmapË=
63;子用tex2D()及向量(x,y)进行采样不同Ӎ=
0;具体//区别及原因很ê=
81;易从程序中看出,我=
就不再啰嗦了。<=
/span>float4PS_Unlit( VSOU=
TPUT_UNLIT IN ) : COLOR0{
=// G=
enerate the 9 texture co-ordinates for a 3x3 PCF kernelfloat4 vTexCoords[9];=// Texel sizefloat fTexelSize =3D =
1.0f / 1024.0f;<=
/pre>// G=
enerate the tecture co-ordinates for the specified depth-map size// 4 3 5=// 1 0 2=// 7 6 8<=
/span>VTexCoords[0] =3D IN.vTexCoord;vTexCoords[1] =3D IN.vTexCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.0f );vTexCoords[2] =3D IN.vTexCoord + float4(fTexelSiz=
e, 0.0f, 0.0f, 0.0f );vTexCoords[3] =3D IN.vTexCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );vTexCoords[6] =3D IN.vTexCoord + float4( 0.0f,fTexelSize, 0.0f, 0.0f );vTexCoords[4] =3D IN.vTexCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );vTexCoords[5] =3D IN.vTexCoord + float4(fTexelSiz=
e, -fTexelSize, 0.0f, 0.0f );vTexCoords[7] =3D IN.vTexCoord + float4( -fTexelSize,fTexelSize, 0.0f, 0.0f );vTexCoords[8] =3D IN.vTexCoord + float4(fTexelSiz=
e,fTexelSize, 0.0f, 0.0f );// Sample each of them c=
hecking whether the pixel under test is shadowed or not
<=
/o:p>float fShadowTerms[9]=
;float fShadowTerm =3D=
0.0f;for( int i =3D 0; i < 9; i++ )<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>{float A =3D tex2Dproj( ShadowSampler, vTexCoords[i] ).r;float B =3D (IN.fDept=
h - 0.1f);<=
/o:p>// Texel is shadowed<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>fShadowTerms[i] =3D A < B ? 0.0f: 1.0f;fShadowTerm&nbs=
p;+=3D fShadowTerms[i];}// Get the averagefShadowTerm /=3D 9.0f;return fShadowTerm;}
=
屏幕缓冲完=
25104;了,我们还需要进Ŝ=
92;模糊工作。
我们采用seperable
gaussian filter模糊=
屏幕缓冲。但我们也=
1487;以用Poisson filter。这=
次的render targets是<=
span
lang=3DEN-US>A8R8G8B8的纹理和相关的=
表面。我们需要两个render targets,一=
个进行水平阶段,一=
0010;进行垂直阶段。
=
// Create the blur maps<=
pre>for(int i =3D 0; i < 2; i++=
){
=if( FAILED( g_pd3dDev=
ice->CreateTexture( SCREEN_WIDTH, =
; &n=
bsp;SCREEN_HEIGHT, 1, D3DUSAGE_RENDERTARGET=
, =
; &n=
bsp;D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, =
; &n=
bsp;&g_pBlurMap[i], NULL ) ) ){MessageBox( g_hWnd, "Unable to create blur map!&q=
uot;, =
;"Error",=
MB_OK | MB_ICONERROR );return E_FAIL;
=
o:p>}// Grab the texture's su=
rfaceg_pBlurMap[i]->GetSurfaceLevel( 0, & g_pBlurSurf[i] );
=}
=
我们用下面=
30340;代码生成15个高斯偏移量(Gaussian offsets=
65289;及他们的权重(corresponding weights)。
floatGetGaussian=
Distribution( float x, float =
y, float rho ){
=float g =3D 1.0f / sqrt( 2.0f * 3.141592654f * rho * rho );return g * exp( -(x *=
x + y * y) / (2 * rho * rho) );}
=<=
/o:p>voidGetGaussianOffsets( bool bHorizontal, =
; &n=
bsp;D3DXVECTOR2 vViewportTexelSize,=
pre> =
; &n=
bsp;D3DXVECTOR2* vSampleOffsets, =
; &n=
bsp;float* fSampleWeights=
){
=// Get the center texel offset and weightfSampleWeights[0] =3D 1.0f * GetGaussianDistribution( 0, 0, 2.0f );<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>vSampleOffsets[0] =3D D3DXVECTOR2( 0.0f, 0.0f );// Get the offsets and w=
eights for the remaining tapsif( bHorizontal ){for( int i =3D 1; i < 15; i +=3D 2 )=
{vSampleOffsets[i + 0] =3D D3DXVECTOR2( i * vViewportTe=
xelSize.x, 0.0f );<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>vSampleOffsets[i + 1] =3D D3DXVECTOR2( -i * vViewportT=
exelSize.x, 0.0f );<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>fSampleWeights[i + 0] =3D 2.0f * GetGaussianDistribution( float(i + 0), 0.0f, 3.0f );fSampleWeights[i + 1] =3D 2.0f * GetGaussianDistribution( float(i + 1), 0.0f, 3.0f );}}else{for( int i =3D 1; i < 15; i +=3D 2 )=
{vSampleOffsets[i + 0] =3D D3DXVECTOR2( 0.0f, i * vViewportTexelSize.y );vSampleOffsets[i + 1] =3D D3DXVECTOR2( 0.0f, -i * vViewportTexelSize.y );fSampleWeights[i + 0] =3D 2.0f * GetGaussianDistribution( 0.0f, float=(i + 0), 3.0f );<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>fSampleWeights[i + 1] =3D 2.0f * GetGaussianDistribution( 0.0f, float=(i + 1), 3.0f );<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>}}
}
为了模糊=
屏幕缓冲,我们将模=
1946;映射图(blur map)作为render target,使=
用下面的顶点渲染和=
0687;素渲染代码渲染一ߑ=
0;与屏幕等大的方块。<=
/span>
// 作者在程序ߑ=
3;预先定义的屏幕大小&=
#26159;1024 * 768,而随后定È=
41;的与屏幕等大的方块=
为:
/=
/<=
/span>pVertices[0].p =3D D3DXVECTOR4( 0.0f, 0.0f,
0.0f, 1.0f );
//pVertices[1].p =3D
D3DXVECTOR4( 0.0f, 768 /
2, 0.0f, 1.0f );
//pVertices[2].p =3D
D3DXVECTOR4( 1024 / 2, 0.0f,
0.0f, 1.0f );
//pVertices[3].p =3D
D3DXVECTOR4( 1024 / 2, 768 / 2, 0.0f,
1.0f );
=
span>
//这种方法与d3dsdk中HDRLight中获得rend=
er
target的width and height然后再构造的//方法不×=
16;:<=
/p>
// svQuad[0].p =3D D3DXVECTOR4(-0.5f, -0.5f,
0.5f, 1.0f);
// svQuad[1].p =3D D3DXVECTOR4(Width-0.5f, -0.5f,
0.5f, 1.0f);
// svQuad[2].p =3D D3DXVECTOR4(-0.5f, Height-0.5f, 0.5f,
1.0f);
// svQuad[3].p =3D D3DXVECTOR4(Width-0.5f,fHeight-0.5f, 0.5f,
1.0f);
// 而一般定义的Ĺ=
83;口大小往往与从render target获得的width
and height不相同。=
o:p>
//而É=
08;者的fvf都是D3DFVF_XYZRHW。这两种方法有什么=
区别我一直没想通。
&=
nbsp;
=
// Gaussian filter vertex shader<=
/span>structVSOUTPUT_BLUR{
=float4 vPosition: POSITION;float2 vTexCoord: TEXCOORD0;};<=
/o:p>VSOUTPUT_BLU=
R VS_Blur( float4 inPositi=
on : POSITION, float2 inTexCoord : TEXCOORD0 ){
=// Output struct<=
/span>VSOUTPUT_BLUR OUT =3D (VSOUTPUT_B=
LUR)0;// Output the position=
span>OUT.vPosition =3D inPosition;// Output the texture co=
ordinatesOUT.vTexCoord =3D inTexCoord;return OUT;}
=// Horizont=
al blur pixel shader<=
/span>float4PS_BlurH( VSOUTPUT_BLUR IN ): COLOR0{
=// A=
ccumulated colorfloat4 vAccum =3D float4( 0.0f, 0.0f, 0.0f, 0.0f );// Sample the taps (g_vS=
ampleOffsets holds the texel offsets// and g_fSampleWeights holds the=
texel weights)<=
pre>for(int i =3D 0; i < 15; i++ )<=
span
lang=3DEN-US style=3D'font-family:"Courier New";color:#333333'>{vAccum +=3D tex2D( ScreenSampler, IN.vTexCoord + g_vSa=
mpleOffsets[i] ) * g_fSampleWeights[i];}return vAccum;
=
o:p>}
=<=
/o:p>// Vertical=
blur pixel shader<=
/span>float4PS_BlurV( VSOUTPUT_BLUR IN ): COLOR0{
=// A=
ccumulated colorfloat4 vAccum =3D float4( 0.0f, 0.0f, 0.0f, 0.0f );// Sample the taps (g_vS=
ampleOffsets holds the texel offsets and// g_fSampleWeights holds the texel weights)<=
pre>for( int i =3D 0; i < 15; i++ )=
{vAccum +=3D tex2D( BlurHSampler, IN.vTexCoord + g_vSam=
pleOffsets[i] ) * g_fSampleWeights[i];}return vAccum;
=
o:p>}
=
这里,模=
糊映射图已经完成了=
5292;为了增加阴影的模౺=
6;程度,增加了纹理上&=
#28857;的采样距离。最后=
968;步自然是将模糊后的=
;纹理图投射回屏幕空=
8;使其显示在屏幕上。<=
span
lang=3DEN-US>
After first Gaussian pass
After second Gaussian pass=
span>
为了=
将模糊后的纹理投射=
1040;屏幕上,我们像平६=
0;那样渲染场景,但投&=
#24433;模糊后的纹理时要=
351;用屏幕空间的坐标。=
;我们使用裁剪空间的=
22352;标和一些数学方法Ĉ=
69;产生屏幕空间的坐标=
。下面的顶点渲染和=
0687;素渲染将完成这个ॣ=
7;作:
structVSOUTPUT_SC=
ENE{
=float4 vPosition: POSITIO=
N;float2 vTexCoord: TEXCOOR=
D0;float4 vProjCoord: TEXCOORD1;float4 vScreenCoord: TEXCOORD2;
<=
/span>float3 vNormal: TEXCOORD3;float3 vLightVec: TEXCOOR=
D4;float3 vEyeVec: TEXCOORD5;};// Scene ve=
rtex shader<=
/span>VSOUTPUT_SCE=
NE VS_Scene( float4 inPosit=
ion : POSITION, =
; &n=
bsp;float3 inNormal : NORMAL, =
; &n=
bsp;float2 inTexCoord : T=
EXCOORD0 ){
=VSOUTPUT_SCENE OUT =3D (VSOUTPUT_=
SCENE)0;// Output the transforme=
d positionOUT.vPosition =3D mul( inPosition, g_ma=
tWorldViewProj );<=
/o:p>// Output the texture co=
ordinatesOUT.vTexCoord =3D inTexCoord;<=
/o:p>// Output the projective=
texture coordinates (we use this// to project the spot texture do=
wn onto the scene)//这个是用来ߝ=
5;生light map的纹理坐标=
0;。最终效果图中地面&=
#19978;光照效果就是用//这个坐标配ࡧ=
2;上一幅这样的light map实现的。=<=
/span>OUT.vProjCoord =3D mul( inPosition, g_ma=
tTexture );<=
/o:p>// Output the screen-spa=
ce texture coordinates//这个就=
是将裁剪空=
间的坐标转换到屏幕=
1354;间的坐标,方法和将裁剪空间的坐标$=
716;换//纹理空=
间的坐标的方法很相=
0284;。=OUT.vScreenCoord.x =3D ( OUT.vPosition.x * 0.5 + OUT.vPosition.w * 0.5 );OUT.vScreenCoord.y =3D ( OUT.vPosition.w * 0.5 - OUT.vPosition.y * 0.5 );OUT.vScreenCoord.z =3D OUT.vPosit=
ion.w;OUT.vScreenCoord.w =3D OUT.vPosit=
ion.w;<=
/o:p>// Get the world space v=
ertex positionfloat4 vWorldPos =3D mul( inPosition, g_matWorld );<=
/o:p>// Output the world spac=
e normalOUT.vNormal =3D mul( inNormal, g_matW=
orldIT );<=
/o:p>// Move the light vector=
into tangent spaceOUT.vLightVec =3D g_vLightPos.xyz=
- vWorldPos.xyz;<=
/o:p>// Move the eye vector i=
nto tangent spaceOUT.vEyeVec =3D g_vEyePos.xyz - v=
WorldPos.xyz;return OUT;}
=float4PS_Scene( VSOUTPUT_SCENE IN ) : COLOR0<=
/pre>{
=// N=
ormalize the normal, light and eye vectors
<=
/span>IN.vNormal=3D normalize( IN.vNormal=
);IN.vLightVec =3D normalize( IN.vLightV=
ec );IN.vEyeVec=3D normalize( IN.vEyeVec=
);// Sample the color and =
normal mapsfloat4 vColor=3D tex2D( ColorSampler, IN.vTexCoord );
<=
/span>// Compute the ambient, =
diffuse and specular lighting terms
float ambient=3D 0.0f;float diffuse=3D max( dot( IN.vN=
ormal, IN.vLightVec ), 0 );float specular =3D pow(max(dot( 2 * dot( I=
N.vNormal, IN.vLightVec ) * IN.vNormal =
; &n=
bsp;- I=
N.vLightVec, IN.vEyeVec ), 0 ), 8 );if( diffuse =3D=3D 0 =
) specular =3D 0;// Grab the shadow term<=
/span>float fShadowTerm =3D=
tex2Dproj( BlurVSampler, IN.vScreenCoord );
=// Grab the spot termfloat fSpotTerm =3D <=
span
class=3Dcodekeyword>tex2Dproj( SpotSampler, IN.vProjCoord );// Compute the final col=
orreturn (ambient * vCo=
lor) + =
;(diffuse * vColor * g_vLightColor * fShadowTerm * fSpot=
Term) + =
;(specular * vColor * g_vLightColor.a * fShadowTerm * fS=
potTerm);}
=
终于完成了。看=
上去不错。该技术的=
0248;点一是解决了锯齿=
2;题,二是在多光源,&=
#20302;内存下实现了软阴=
433;。另外该技术与阴影=
;生成方法无关,可以=
24456;容易的在shadow volumes技术=
中采用这项技术。缺=
8857;是由于进行了模糊ࣦ=
8;理而需要一些填充率&=
#12290;
下面是不同阶段=
的效果比较图:
谢谢你阅读这篇=
文章,如有疑问欢迎=
6469;信(这=
是原作者的信箱)
(汗,好多东西=
心里明白,但翻译出=
6469;就是不满意。各位ව=
1;是看不明白就找出原&=
#25991;研究吧。)
Hardware Shadow Mapping=
. Cass
Everitt, Ashu Rege and Cem Cebenoyan.
Hardware-accelerated
Rendering of Antialiased Shadows with Shadow Maps. Stefan Brabec and
Hans-Peter Seidel.