BX1002 31 杨汉标 D3D 课程设计报告.docx
- 文档编号:14843850
- 上传时间:2023-06-27
- 格式:DOCX
- 页数:25
- 大小:581.69KB
BX1002 31 杨汉标 D3D 课程设计报告.docx
《BX1002 31 杨汉标 D3D 课程设计报告.docx》由会员分享,可在线阅读,更多相关《BX1002 31 杨汉标 D3D 课程设计报告.docx(25页珍藏版)》请在冰点文库上搜索。
BX100231杨汉标D3D课程设计报告
《游戏工具开发技术课程设计报告书》
实时阴影
班级:
BX1002
姓名:
杨汉标
学号:
31
指导老师:
汪鑫
前言
在windows窗口程序中,用户通过键盘或者鼠标输入的消息并不是应用程序直接处理的,而是通过消息机制转发给windows操作系统,再通过回调应用程序的窗口过程函数进行相应的消息处理。
但在游戏程序中采用这样的处理方式就不会获得理想的执行效率,而direct3d,是使用directinput接口对象实现用户输入的。
Directinput直接和硬件驱动打交道,并能够较快地处理用户输入。
在3D游戏中,光照阴影通常是场景中的一个重要组成部分,它能够实现更逼真的自然效果,而游戏场景中的光照阴影越复杂,提供给用户的视觉真实感就越强。
最简单的阴影是平面阴影(PlannerShadow),这种阴影是物体在经过光照下投射到一个平面上产生的。
相对高级的实时阴影是体积阴影(VolumetricShadow)和阴影贴图(ShadowMapping)。
其中,阴影贴图实现起来比较容易,它可以发挥GPU可编程流水线的能力,但在处理动态光源物体是的开销较大。
而体积阴影弥补了阴影贴图的不足,因此应用比较广泛。
此次实训,只针对平面阴影和体积阴影进行介绍和演示。
目录
前言2
一、课程设计的目的和意义3
二、课程设计内容3
2.1问题描述3
2.2相关基本概念与理论知识3
2.2.1点光源平面阴影3
2.2.2方向光源平面阴影3
2.2.3方向平面阴影3
2.2.4体积阴影3
2.3解决方案3
2.3.1计算阴影矩阵3
2.3.2平面阴影的绘制3
2.3.3体积阴影的绘制3
2.3.4阴影贴图3
三、相关实例以及应用检测3
3.1平面阴影3
3.2体积阴影3
四、完整程序代码3
五、程序截图3
5.1平面阴影3
5.2体积阴影3
六、实训小结3
参考文献3
一、课程设计的目的和意义
通过课程设计,使我们掌握Directx3D图像编程的原理和技术,将理论与实际相结合,,规范、科学地完成一个3D图像的设计与实现,把理论课与实验课所学内容进行综合,并在此基础上强化我们的实践意识、提高其实际动手能力和创新能力。
二、课程设计内容
2.1问题描述
本次的实训中,假如在3D场景中只有一个重要的光源(即最强的光源),那么也只有该光源照射到物体时才产生明显的阴影。
因此,主要的工作就是计算出光源如何让物体将阴影投射到平面上。
2.2相关基本概念与理论知识
2.2.1点光源平面阴影
对于遮挡物上的任意一个顶点P,从点光源发出的光线通过任意点P的射线方程为r(t)=p+t(p-L)。
若经过射线r(t)与平面np+d=0的交点为S,那么S点就为遮挡物P点在平面np+d=0上投射的阴影点。
将r(t)代入平面方程np+d=0中,可以得到t,公式如下:
由于S点同样在射线r(t)上,所以将t代入到r(t)中,可以得到S,公式如下:
2.2.2方向光源平面阴影
假设平行光光源的光照方向用向量L表示,对于遮挡物上的任意一个顶点P,光线通过该点的射线方程为r(t)=p+tL,并且在平面np+d=0上投射出阴影点S,如图14.4所示。
将r(t)代入平面方程np+d=0中,可以得到t,公式如下:
将t代入到r(t)中,可以得到S,公式如下:
2.2.3方向平面阴影
将r(t)代入平面方程np+d=0中,可以得到t,公式如下:
将t代入到r(t)中,可以得到S,公式如下:
2.2.4体积阴影
想象一个物体挡住光时,在物体的后面会形成一个大的阴影锥(ShadowVolume)。
很明显地,若一个像素在“阴影锥”之中,那它就是在阴影之中。
其中使用了Z-Pass算法,D3DXMatrixShadow函数,UpdateShadowVolume和RenderShadowVolume函数更新阴影,Direct3DCleanup函数释放内存,ShadowMap渲染阴影,封装技术。
2.3解决方案
2.3.1计算阴影矩阵
假设将平面方程的各系数分别用一个4D齐次坐标(nx,ny,nzd)表示,并将点光源的位置或方向光的光照方向通过用一个4D向量L=(Lx,Ly,Lz,Lw)表示。
那么将它们代入相对应的计算公式中后,可以得到P点到其投影点S的阴影变换矩阵S,公式如下:
在程序中,并不需要手动计算平面的阴影变换矩阵,而可以调用D3DX库中的D3DXMatrixShadow函数,根据平行光的光照方向或点光源的位置创建平面的阴影变换矩阵。
该函数的声明如下:
D3DXMATRIX*D3DXMatrixShadow(
D3DXMATRIX*pOut,
CONSTD3DXVECTOR4*pLight,
CONSTD3DXPLANE*pPlane
);
例如,在下面的代码中,创建y=1平面、光照方向为(0.707,-0.707,0.707,0.0)的阴影矩阵。
D3DXVECTOR4vLight(0.707f,-0.707f,0.707f,0.0f);
D3DXPLANEplane(0.0f,-1.0f,0.0f,0.0f);
D3DXMATRIXmatShadow;
D3DXMatrixShadow(&matShadow,&vLight,&plane);
2.3.2平面阴影的绘制
首先将模板参考值D3DRS_STENCILREF设置为0,然后将模板比较函数设置为D3DCMP_EQUAL,这也就说明只有模板缓存中同样为0的像素(即这些像素能够通过模板测试)才能够被绘制到后台缓存中。
例如:
g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,true);
g_pd3dDevice->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_EQUAL);
g_pd3dDevice->SetRenderState(D3DRS_STENCILREF,0x0);
g_pd3dDevice->SetRenderState(D3DRS_STENCILMASK,0xffffffff);
g_pd3dDevice->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
g_pd3dDevice->SetRenderState(D3DRS_STENCILFAIL,D3DSTENCILOP_KEEP);
g_pd3dDevice->SetRenderState(D3DRS_STENCILZFAIL,D3DSTENCILOP_KEEP);
//计算阴影矩阵,绘制平面阴影
此时绘制的某些平面阴影有重叠的现象。
也就是说,当两个和多个物体的阴影有一部分重合时,将出现重合的区域的颜色比其他阴影区域的颜色深,如图
2.3.3体积阴影的绘制
1.Z-Pass算法
Z-Pass算法实现的基本思路及方式如下。
(1)关闭光源,绘制整个场景以获得场景中所有物体的深度值。
例如。
(2)关闭深度缓存,打开模板缓存,并渲染阴影体的正面。
若深度测试通过,则模板值加1。
例如:
/*渲染阴影体前面,深度测试总是通过,并对深度值加(Z-Pass)*/
m_pd3dDevice->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_ALWAYS);
m_pd3dDevice->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_INCR);
m_pd3dDevice->SetRenderState(D3DRS_SHADEMODE,D3DSHADE_FLAT);
m_pd3dDevice->SetFVF(D3DFVF_XYZ);
m_pd3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,m_dwVertices/3,m_pvVertices,sizeof(D3DXVECTOR3));
(3)渲染阴影体的背面,若深度测试通过,则模板值减1。
例如:
/*渲染阴影体背面,深度测试通过的像素的深度值减(Z-Pass)*/
m_pd3dDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CW);
m_pd3dDevice->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_DECR);
m_pd3dDevice->SetFVF(D3DFVF_XYZ);
m_pd3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,m_dwVertices/3,m_pvVertices,sizeof(D3DXVECTOR3));
(4)那些模板值不为0的面就位于阴影体中。
然后重新开启深度缓存,并根据每个像素的深度值绘制阴影。
由于物体位于阴影体的后面,那么当视线到达物体时,它进入阴影体的次数与离开阴影体的次数相等。
因此,模板值将等于0,那么所观察的物体将不具有阴影效果,如图14.10所示。
当物体位于阴影体内部时,视线在进入阴影体并到达物体时,它并没有离开相同个数的阴影体,那么模板值也就大于0,如图14.11所示。
Z-Pass算法有一个明显的缺陷,就是当视点位于阴影体之外时,使用Z-Pass算法可以很好的工作,但当视点进入阴影体后,使得视线失去了一次进入阴影体的机会,从而让本应该为1的模板值变成了0,如图14.12所示。
2.Z-Fail算法
Z-Fail算法实现的基本思路如下。
(1)关闭光源,绘制整个场景以获得场景中所有物体的深度值。
(2)关闭深度缓存,并打开模板缓存,并渲染阴影体的正面。
若深度测试失败,则模板值加1。
然后渲染阴影体的正面,若深度测试失败,则模板值减1。
(3)那些模板值不为0的面就位于阴影体中。
然后重新开启深度缓存,并根据每个像素的深度值绘制阴影。
2.3.4阴影贴图
阴影图实际上是一张2D纹理贴图,其中的每个像素都记录了从光源到遮挡物(遮挡物就是阴影生成物体)的每个“可见”像素的距离。
这里的“可见”像素是指以光源为观察点,光的方向为观察方向,设置观察矩阵并渲染所有遮挡物,而最终出现在渲染表面上的像素,如图14.15所示。
使用ShadowMap渲染阴影主要分两个过程:
生成阴影图和使用阴影图渲染。
(1)以光源为视点并对整个场景进行渲染,得到一幅所有物体相对于光源的深度图(即是ShadowMap)。
其中,每个像素的值代表着场景里面离光源最近的像素的深度值。
(2)将视点恢复到原来的正常位置渲染整个场景,并对每个像素计算它和光源的距离。
然后将这个值和深度图中相应的值比较,确定这个像素点是否处在阴影当中。
三、相关实例以及应用检测
3.1平面阴影
(1)分别创建圆环体和茶壶几何体对象,并设置它们的材质属性,然后按照平常绘制3D场景的方法绘制这些几何体。
(2)开启模板缓存,并按照前面介绍的方式设置模板测试函数、模板参考值和模板更新方式等。
代码如下:
/*开启模板缓存*/
g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,true);
g_pd3dDevice->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_EQUAL);
g_pd3dDevice->SetRenderState(D3DRS_STENCILREF,0x0);
g_pd3dDevice->SetRenderState(D3DRS_STENCILMASK,0xffffffff);
g_pd3dDevice->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
g_pd3dDevice->SetRenderState(D3DRS_STENCILZFAIL,D3DSTENCILOP_KEEP);
g_pd3dDevice->SetRenderState(D3DRS_STENCILFAIL,D3DSTENCILOP_KEEP);
g_pd3dDevice->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_INCR);
(3)调用D3DXMatrixShadow函数计算平面的阴影矩阵,将由4D向量表示光照方向,并设置为初始化D3DLIGHT9结构时的光照方向。
如果光源为点光源,那么将最后一个分量指定为1.0f,否则指定为0.0f。
这里创建的是方向光光源。
代码如下:
/*计算阴影矩阵*/
D3DXMATRIXmatShadow;
D3DXPLANEplaneY(0.0f,-2.0f,0.0f,0.0f);//方向光
D3DXVECTOR4vLightDir(0.707f,-0.707f,-0.707f,0.0f);
D3DXMatrixShadow(&matShadow,&vLightDir,&planeY);
(4)启用Alpha混合,以半透明显示平面阴影,然后绘制这些结合体。
3.2体积阴影
对于3D场景中某一个三角形面,光源照射到该三角形面时将产生一个三角阴影锥,并且阴影锥与需要产生阴影的平面的交集将会产生阴影,如图14.8所示。
列表中将只会得到所有正向光线的三角面的边信息,若将这些边沿光照方向进行延伸,然后完成侧面插补和前后封口,最终得到的将是光线经过物体模型而产生的阴影锥,如图14.9所示。
在添加由△V2V4V5构成三角面的边时,将不添加△V2V4V5中的V2V4边并删除列表中的V2V4边。
最后在经过侧面插补和前后封口后,将得到如图14.9(c)所示的阴影锥。
例如:
VOIDCShadowVolume:
:
AddFaceEdge(WORD*pEdges,DWORD&dwNumEdges,
WORDv0,WORDv1)
{
for(DWORDi=0;i { if((pEdges[2*i+0]==v0&&pEdges[2*i+1]==v1)|| (pEdges[2*i+0]==v1&&pEdges[2*i+1]==v0)) { pEdges[2*i+0]=pEdges[2*(dwNumEdges-1)+0];//存在,则移除这条边 pEdges[2*i+1]=pEdges[2*(dwNumEdges-1)+1]; dwNumEdges--; return; } } pEdges[2*dwNumEdges+0]=v0;//不存在,则添加到列表中 pEdges[2*dwNumEdges+1]=v1; dwNumEdges++; } 具体示例: 通过UpdateShadowVolume和RenderShadowVolume函数分别更新和绘制体积阴影,如图14.14所示。 (1)创建球体几何体,并从bigship.x文件中加载网格模型,通过顶点缓存创建包含纹理贴图的地面。 当然,还需要设置3D场景中的光源。 (2)在Direct3DRender函数中,对场景中球体、网格和地面进行绘制。 然后,调用CShadowVolume对象的UpdateShadowVolume函数更新阴影体。 此时,需要将光源位置从世界坐标系变换到本地坐标系下,即生成阴影体的遮挡物模型对象的本地坐标系下。 (3)在Direct3DCleanup函数中通过SAFE_DELETE宏释放CShadowVolume对象及其他对象所占用的内存。 代码如下: VOIDDirect3DCleanup() { SAFE_DELETE(g_pCamera); SAFE_DELETE(g_pShadows); SAFE_RELEASE(g_pMeshLight); SAFE_RELEASE(g_pMeshModel); SAFE_RELEASE(g_pFloorVBuffer); SAFE_RELEASE(g_pFloorTexture); for(DWORDi=0;i SAFE_RELEASE(g_pModelTexes[i]); SAFE_DELETE_ARRAY(g_pModelTexes); SAFE_DELETE_ARRAY(g_pModelMtrls); /*释放其他接口对象*/ } 四、完整程序代码 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}")="PlanerShadow","PlanerShadow.vcproj","{95BC4C46-F23D-4540-92ED-65BFD82840E5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms)=preSolution Debug|Win32=Debug|Win32 Release|Win32=Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms)=postSolution {95BC4C46-F23D-4540-92ED-65BFD82840E5}.Debug|Win32.ActiveCfg=Debug|Win32 {95BC4C46-F23D-4540-92ED-65BFD82840E5}.Debug|Win32.Build.0=Debug|Win32 {95BC4C46-F23D-4540-92ED-65BFD82840E5}.Release|Win32.ActiveCfg=Release|Win32 {95BC4C46-F23D-4540-92ED-65BFD82840E5}.Release|Win32.Build.0=Release|Win32 EndGlobalSection GlobalSection(SolutionProperties)=preSolution HideSolutionNode=FALSE EndGlobalSection EndGlobal /*半透明阴影*/ g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true); g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_ZENABLE,false); /*绘制圆环体阴影*/ D3DMATERIAL9mtrl; : : ZeroMemory(&mtrl,sizeof(mtrl)); mtrl.Ambient=D3DXCOLOR(0.2f,0.2f,0.2f,0.5f); mtrl.Diffuse=D3DXCOLOR(0.2f,0.2f,0.2f,0.5f); g_pd3dDevice->SetMaterial(&mtrl); matWorld=R*matTorus*matShadow; g_pd3dDevice->SetTransform(D3DTS_WORLD,&matWorld); g_pMeshTorus->DrawSubset(0); /*绘制茶壶阴影*/ matWorld=R*matTeapot*matShadow; g_pd3dDevice->SetTransform(D3DTS_WORLD,&matWorld); g_pMeshTeapot->DrawSubset(0); /*恢复渲染状态*/ g_pd3dDevice->SetRenderState(D3DRS_ZENABLE,true); g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,false); g_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,false); /*恢复渲染状态*/ m_pd3dDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW); m_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE,true); m_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,false); m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,false); /*关闭深度测试,启用Alpha混合*/ m_pd3dDevice->SetRenderState(D3DRS_ZENABLE,false); m_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE,true); m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); /*设置模板相关渲染状态*/ m_pd3dDevice->SetRenderState(D3DRS_STENCILENABLE,true); m_pd3dDevice->SetRenderState(D3DRS_STENCILREF,0x1); m_pd3dDevice->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_LESSEQUAL); m_pd3dDevice->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_KEEP); /*渲染一个灰色矩形,只有通过模板测试的像素才会被渲染到颜色缓冲区,表示阴影*/ m_pd3dDevice->SetFVF(SHADOWVERTEX: : FVF); m_pd3dDevice->SetStreamSource(0,m_pVertexBuf,0,sizeof(SHADOWVERTEX)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- BX1002 31 杨汉标 D3D 课程设计报告 课程设计 报告