阴影和纹理映射一样,通常是在渲染器中附加算法来实现,并被粘贴到场景中。在场景中每个光源产生的贴图称为阴影贴图(shadermap)。在渲染的过程中对该贴图进行存取,以找出某个像素是否位于阴影中。
阴影贴图的生成原理如下图所示:
可把光源模拟成有特定方向的发光点,其发出的光线与长方体相交与A点,若射穿长方体则与地面相交于B点,我们记录光线首次与物体相交的点的信息,并保存光源到该点的距离,称为该点在灯光空间的深度Depth。如此被长方体遮盖住的部分地面里的点则没有相交信息,则保存为同一光源线上首次与光源相交的点的信息为该点的Depth,如图,A点和B点保存的都是A点到光源的距离。如此场景中每个点均有Depth。我们可比较点与光源的实际距离P和点的Depth:若P>Depth,则P点在某一物体的阴影中,由此可计算场景中物体的每个点是否在阴影中来生成阴影贴图,最后在渲染时将阴影贴图贴在场景中。
具体实现如下:
1:定义一监听器m_pShadowRenderer和相应的渲染目标m_pShadowTarget(具体步骤可参考“引擎技术之渲染到目标“)
Execute函数的实现:
先获取光源的位置lightPos和方向lightPos。
计算lightlookat=lightPos+lightDir;
定义光源的顶方向up(0,1,0);执行:
D3DXMatrixLookAtLH( &lightview, &lightPos, &lightlookat,&up);
获取视图矩阵。接着根据 shader文件获取相应的baseshader变量。
设置shader里的视图矩阵变量: baseShader->SetMatrix("LightView",&lightview);
baseShader->CommitChanges();
渲染场景中的物体:
执行IDirect3DDevice9接口的BeginScene(),若返回真,遍历场景中的物体。设置相应的
shader里的技术后再渲染,渲染后再设置回才场景默认的shader技术,最后执行
IDirect3DDevice9接口的EndScene()函数。
2: Shader的实现
实现阴影效果要用到两个技术: techScene 和techShadowMap
techShadowMap用于渲染到阴影贴图shadermap,而techScene作最终的渲染。
techShadowMap的 VS 中主要进行三个变换矩阵的操作,并保存变换后顶点与光源的距离
在 PS 中主要计算 depth= IN.depth.z/IN.depth.w;return float4(depth,1,1,1);
techScene中除了一般的矩阵变换后,还需要进行特殊设置:
VS:
//计算shadow相关的属性
float4 lightworldPos = mul( inPosition, World );
lightworldPos = mul( lightworldPos, LightView );
lightworldPos = mul( lightworldPos, Projection );
//float w=lightworldPos.w;
//计算本点在灯光空间的深度
OUT.depth = lightworldPos;
//计算本点在shadowmap里的纹理坐标
OUT.vShadowTexcoord.x = ( lightworldPos.x * 0.5 + lightworldPos.w * 0.5 );
OUT.vShadowTexcoord.y = ( lightworldPos.w * 0.5 - lightworldPos.y * 0.5 );
OUT.vShadowTexcoord.z = lightworldPos.z/lightworldPos.w;
OUT.vShadowTexcoord.w = lightworldPos.w;
PS:
float shadowvalue=1.0f;
float4 smcolor=tex2Dproj(ShadowSampler,IN.vShadowTexcoord/IN.vShadowTexcoord.w);
float smDepth=smcolor.r;
float realDepth=IN.depth.z/IN.depth.w;
if(realDepth*0.9997f>smDepth)//比较该点对于光源的实际距离和其在灯光空间的深度
{ shadowvalue=0; }//设置该点在阴影区域内
IN.vShadowTexcoord/=IN.vShadowTexcoord.w;
if( IN.vShadowTexcoord.x<0 || IN.vShadowTexcoord.x>1
|| IN.vShadowTexcoord.y<0 || IN.vShadowTexcoord.y>1)
{ shadowvalue=1; }
float diff=(shadowvalue*IN.diff+ambient)/(1+ambient);
color.rgb=color.rgb*diff;
效果图如下: