void CShadow::onTick(float fElapsedTime) { for (unsigned int i(0); i < m_vMesh.size(); i++) { D3DXVECTOR3 vSun(*m_pSun); D3DXMATRIX mInv; D3DXMatrixInverse(&mInv, NULL, m_vMat[i]); D3DXVec3TransformCoord(&vSun, &vSun, &mInv); ID3DXMesh* pMesh; m_vMesh[i]->CloneMeshFVF(m_vMesh[i]->GetOptions(), D3DFVF_XYZ, CDirect3D::getInstance()->GetD3D9Device(), &pMesh); D3DXVECTOR3 *pVertices, *pVolume; WORD* pIndices; pMesh->LockVertexBuffer(NULL, (void**) &pVertices); pMesh->LockIndexBuffer(NULL, (void**) &pIndices); DWORD dwFaces(pMesh->GetNumFaces()); WORD* pEdges = new WORD[dwFaces*3*2]; DWORD dwEdges(0); for (DWORD j(0); j < dwFaces; j++) { WORD wFace0 = pIndices[j*3]; WORD wFace1 = pIndices[j*3+1]; WORD wFace2 = pIndices[j*3+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, m_pSun) > 0.f) { AddEdge(pEdges, dwEdges, wFace0, wFace1); AddEdge(pEdges, dwEdges, wFace1, wFace2); AddEdge(pEdges, dwEdges, wFace2, wFace0); } } m_dwTriangles[i] = dwEdges*2; m_vVB[i]->Lock(0, sizeof(D3DXVECTOR3)*m_dwTriangles[i]*3, (void**) &pVolume, NULL); // some (solvable, but expensive) issues here: // (view port related) direction undetermined for segment v1 - v2, // so CULL_CW doesn't necessarily mean 'back side'. for (DWORD j(0); j < dwEdges; j++) { D3DXVECTOR3 v1 = pVertices[pEdges[2*j+0]]; D3DXVECTOR3 v2 = pVertices[pEdges[2*j+1]]; D3DXVECTOR3 v3 = (v1 - vSun); D3DXVECTOR3 v4 = (v2 - vSun); pVolume[j*6] = v3; pVolume[j*6+1] = v1; pVolume[j*6+2] = v4; // 1 2 pVolume[j*6+3] = v4; // |\| pVolume[j*6+4] = v1; // 3 4 pVolume[j*6+5] = v2; } m_vVB[i]->Unlock(); pMesh->UnlockVertexBuffer(); pMesh->UnlockIndexBuffer(); Safe_Release(pMesh); Safe_Delete_Array(pEdges); } }
//----------------------------------------------------------------------------- // Name: buildShadowVolume() // Desc: Takes a mesh as input, and uses it to build a shadow volume. The // technique used considers each triangle of the mesh, and adds it's // edges to a temporary list. The edge list is maintained, such that // only silohuette edges are kept. Finally, the silohuette edges are // extruded to make the shadow volume vertex list. //----------------------------------------------------------------------------- HRESULT CShadowVolume::BuildShadowVolume(LPD3DXMESH pMesh, D3DXVECTOR3 vLight) { // pMesh的顶点结构 struct MeshVertex { D3DXVECTOR3 p; D3DXVECTOR3 n; float u, v; }; MeshVertex *pVertices; WORD *pIndices; // 锁缓存 pMesh->LockVertexBuffer(0L, (LPVOID*)&pVertices); pMesh->LockIndexBuffer(0L, (LPVOID*)&pIndices); DWORD dwNumFaces = pMesh->GetNumFaces(); // 分配一个临时的索引数组 WORD *pEdges = new WORD[dwNumFaces * 6]; if (pEdges == NULL) { pMesh->UnlockVertexBuffer(); 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].p; D3DXVECTOR3 v1 = pVertices[wFace1].p; D3DXVECTOR3 v2 = pVertices[wFace2].p; // 计算法线是否向光 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); } } // pEdges中仅剩pMesh的边缘顶点,对每条边的两个顶点按光照的方向进行延伸 // 最终构建一个完整的阴影体 for (DWORD i = 0; i < dwNumEdges; ++i) { D3DXVECTOR3 v1 = pVertices[pEdges[2 * i + 0]].p; D3DXVECTOR3 v2 = pVertices[pEdges[2 * i + 1]].p; D3DXVECTOR3 v3 = v1 + vLight * 200; D3DXVECTOR3 v4 = v2 + vLight * 200; // 封边操作 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 the temporary edge list delete[] pEdges; // Unlock the geometry buffers pMesh->UnlockVertexBuffer(); pMesh->UnlockIndexBuffer(); return S_OK; }
void Sombra::ConstruirSombra(LPD3DXMESH pMesh, D3DXVECTOR3 vLight) { // Note: the MeshVertex format depends on the FVF of the mesh struct MeshVertex { D3DXVECTOR3 p, n; DWORD diffuse; float tu,tv; }; MeshVertex *pVertices; WORD *pIndices; // Lock the geometry buffers pMesh->LockVertexBuffer( 0L, (LPVOID*)&pVertices ); pMesh->LockIndexBuffer( 0L, (LPVOID*)&pIndices ); DWORD dwNumFaces = pMesh->GetNumFaces(); // Allocate a temporary edge list WORD *pEdges = new WORD[dwNumFaces*6]; if( pEdges == NULL ) { pMesh->UnlockVertexBuffer(); pMesh->UnlockIndexBuffer(); return ; } DWORD dwNumEdges = 0; // For each face 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].p; D3DXVECTOR3 v1 = pVertices[wFace1].p; D3DXVECTOR3 v2 = pVertices[wFace2].p; // Transform vertices or transform light? D3DXVECTOR3 vCross1(v2-v1); D3DXVECTOR3 vCross2(v1-v0); D3DXVECTOR3 vNormal; D3DXVec3Cross( &vNormal, &vCross1, &vCross2 ); if( D3DXVec3Dot( &vNormal, &vLight ) >= 0.0f ) { InsertarSegmento( pEdges, dwNumEdges, wFace0, wFace1 ); InsertarSegmento( pEdges, dwNumEdges, wFace1, wFace2 ); InsertarSegmento( pEdges, dwNumEdges, wFace2, wFace0 ); } } // Se construyen las caras de la sombra extrudando los segmentos en la dirección // de la luz y una longitud 10 veces la del vector luz. for( i = 0; i < dwNumEdges; ++i ) { D3DXVECTOR3 v1 = pVertices[pEdges[2*i+0]].p; D3DXVECTOR3 v2 = pVertices[pEdges[2*i+1]].p; D3DXVECTOR3 v3 = v1 - vLight/10; D3DXVECTOR3 v4 = v2 - vLight/10; // Add a quad (two triangles) to the vertex list 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 the temporary edge list delete[] pEdges; // Unlock the geometry buffers pMesh->UnlockVertexBuffer(); pMesh->UnlockIndexBuffer(); }