📄 game_shadow.cpp
字号:
//=============================================================================
// Desc: ShadowVolume.cpp
//=============================================================================
#include "Game_User.h"
//-----------------------------------------------------------------------------
// Desc: 创建阴影体
//-----------------------------------------------------------------------------
HRESULT CShadowVolume::Create(LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXMESH pMesh)
{
//克隆与阴影体关联的网格模型
pMesh->CloneMeshFVF(pMesh->GetOptions(), D3DFVF_XYZ, pd3dDevice, &m_pMesh);
//为阴影体顶点分配存储空间
DWORD dwNumFaces = m_pMesh->GetNumFaces();
m_pVertices = new D3DXVECTOR3[dwNumFaces*3*6];
return S_OK;
}
//-----------------------------------------------------------------------------
// Desc: 根据网格模型和光源位置构造阴影体
// 首先检查网格模型的每个三角形, 将网格模型的轮廓边保存到一个临时列表中.
// 然后根据轮廓边和光源位置构造阴影体
//-----------------------------------------------------------------------------
HRESULT CShadowVolume::UpdateShadowVolume( D3DXVECTOR3 vLight )
{
D3DXVECTOR3* pVertices;
WORD* pIndices;
//锁定网格模型顶点缓冲区和索引缓冲区
m_pMesh->LockVertexBuffer( 0L, (LPVOID*)&pVertices );
m_pMesh->LockIndexBuffer( 0L, (LPVOID*)&pIndices );
//获取网格模型面的数量
DWORD dwNumFaces = m_pMesh->GetNumFaces();
//为临时边列表分配内存空间
WORD* pEdges = new WORD[dwNumFaces*6];
if( pEdges == NULL )
{
m_pMesh->UnlockVertexBuffer();
m_pMesh->UnlockIndexBuffer();
return E_OUTOFMEMORY;
}
DWORD dwNumEdges = 0;
//遍历网格模型的每个面(三角形), 将网格模型的轮廓边添加到临时边列表中
for( DWORD i=0; i<dwNumFaces; i++ )
{
//当前面三个顶点的索引
WORD wFace0 = pIndices[3*i+0];
WORD wFace1 = pIndices[3*i+1];
WORD wFace2 = pIndices[3*i+2];
//根据顶点索引获取当前面三个顶点的坐标
D3DXVECTOR3 v0 = pVertices[wFace0];
D3DXVECTOR3 v1 = pVertices[wFace1];
D3DXVECTOR3 v2 = pVertices[wFace2];
//计算当前面的法线
D3DXVECTOR3 vCross1(v2-v1);
D3DXVECTOR3 vCross2(v1-v0);
D3DXVECTOR3 vNormal;
D3DXVec3Cross( &vNormal, &vCross1, &vCross2 );
//如果当前面是背光面, 则将当前面的三条边"添加"到边列表
if( D3DXVec3Dot( &vNormal, &vLight ) >= 0.0f )
{
AddEdge( pEdges, dwNumEdges, wFace0, wFace1 );
AddEdge( pEdges, dwNumEdges, wFace1, wFace2 );
AddEdge( pEdges, dwNumEdges, wFace2, wFace0 );
}
}
//针对保存的每条轮廓边, 添加一个矩形, 所有的矩形构成阴影体
m_dwNumVertices = 0;
for(int i=0; i<dwNumEdges; i++ )
{
//计算矩形的四个的顶点
D3DXVECTOR3 v1 = pVertices[pEdges[2*i+0]];
D3DXVECTOR3 v2 = pVertices[pEdges[2*i+1]];
D3DXVECTOR3 v3 = v1 - vLight*100;
D3DXVECTOR3 v4 = v2 - vLight*100;
//添加矩形
m_pVertices[m_dwNumVertices++] = v1;
m_pVertices[m_dwNumVertices++] = v2;
m_pVertices[m_dwNumVertices++] = v3;
m_pVertices[m_dwNumVertices++] = v2;
m_pVertices[m_dwNumVertices++] = v4;
m_pVertices[m_dwNumVertices++] = v3;
}
//删除临时的边列表
delete[] pEdges;
//解锁网格模型顶点缓冲区和索引缓冲区
m_pMesh->UnlockVertexBuffer();
m_pMesh->UnlockIndexBuffer();
return S_OK;
}
//-----------------------------------------------------------------------------
// Desc: 将边添加到列表中
//-----------------------------------------------------------------------------
VOID CShadowVolume::AddEdge( WORD* pEdges, DWORD& dwNumEdges, WORD v0, WORD v1 )
{
//首先删除内部的边(在边列表中出现两次的边)
for( DWORD i=0; i < dwNumEdges; i++ )
{
if( ( pEdges[2*i+0] == v0 && pEdges[2*i+1] == v1 ) ||
( pEdges[2*i+0] == v1 && pEdges[2*i+1] == v0 ) )
{
if( dwNumEdges > 1 )
{
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++;
}
//-----------------------------------------------------------------------------
// Desc: 渲染阴影体, 只更新模板缓冲区, 不更新颜色缓冲区
//-----------------------------------------------------------------------------
HRESULT CShadowVolume::Render( LPDIRECT3DDEVICE9 pd3dDevice,
D3DXMATRIX* pmatWorld, bool bRenderVolume)
{
//禁用z缓冲区写操作, 并启用模板缓冲区
pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
pd3dDevice->SetRenderState( D3DRS_STENCILENABLE, TRUE );
//使用平面着色模式(不进行插值)
pd3dDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );
//如果渲染阴影体到颜色缓冲区
if(bRenderVolume)
{
pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
//为阴影体设置材质
D3DMATERIAL9 mtrl;
ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );
mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f;
mtrl.Diffuse.a = mtrl.Ambient.a = 0.5f;
pd3dDevice->SetMaterial( &mtrl );
}
else //只更新深度模板缓冲区
{
pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true );
pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO );
pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
}
//渲染阴影体前面
pd3dDevice->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
pd3dDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
pd3dDevice->SetTransform( D3DTS_WORLD, pmatWorld );
pd3dDevice->SetFVF( D3DFVF_XYZ );
pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, m_dwNumVertices/3,
m_pVertices, sizeof(D3DXVECTOR3) );
//渲染阴影体背面
pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );
pd3dDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_DECR );
pd3dDevice->SetTransform( D3DTS_WORLD, pmatWorld );
pd3dDevice->SetFVF( D3DFVF_XYZ );
pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, m_dwNumVertices/3,
m_pVertices, sizeof(D3DXVECTOR3) );
//恢复渲染状态
pd3dDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
pd3dDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );
pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
return S_OK;
}
//-----------------------------------------------------------------------------
// Desc: 销毁阴影体
//-----------------------------------------------------------------------------
HRESULT CShadowVolume::Destroy( )
{
if(m_pMesh != NULL)
{
m_pMesh->Release();
m_pMesh = NULL;
}
delete[] m_pVertices;
return S_OK;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -