引擎技术研究之实时阴影技术

 

阴影和纹理映射一样,通常是在渲染器中附加算法来实现,并被粘贴到场景中。在场景中每个光源产生的贴图称为阴影贴图(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;

 

 

   效果图如下: 

   

posted @ 2008-10-09 23:10  毛志谦  阅读(728)  评论(0编辑  收藏  举报