頂點著色器輪廓邊卡通效果演示程式
阿新 • • 發佈:2019-02-07
////////////////////////////////////////////////////////////////////////////////////////////////// // // File: silhouetteEdges.h // // Author: Frank Luna (C) All Rights Reserved // // System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 // // Desc: Generates the silhouette geometry of a mesh and renders it. Note // that we assume mesh vertex formats as described in MeshVertex. // ////////////////////////////////////////////////////////////////////////////////////////////////// #ifndef __silhouetteEdgesH__ #define __silhouetteEdgesH__ #include "d3dUtility.h" struct EdgeVertex //邊緣點,座標+法向量+連接面1+連接面2 { D3DXVECTOR3 position; D3DXVECTOR3 normal; D3DXVECTOR3 faceNormal1; D3DXVECTOR3 faceNormal2; }; struct MeshVertex { D3DXVECTOR3 position; D3DXVECTOR3 normal; static const DWORD FVF; //在C++的類定義中static const可以在宣告時給定值,而在類外定義時沒有給定值(也不能再次給定值) //具體使用見 silhouetteEdges.cpp 程式碼第二行 }; class SilhouetteEdges //輪廓邊類 { public: SilhouetteEdges( IDirect3DDevice9* device, ID3DXMesh* mesh, ID3DXBuffer* adjBuffer); ~SilhouetteEdges(); void render(); private: IDirect3DDevice9* _device; IDirect3DVertexBuffer9* _vb; IDirect3DIndexBuffer9* _ib; IDirect3DVertexDeclaration9* _decl; UINT _numVerts; UINT _numFaces; private: bool createVertexDeclaration(); void getFaceNormal( ID3DXMesh* mesh, DWORD faceIndex, D3DXVECTOR3* faceNormal); void getFaceNormals( ID3DXMesh* mesh, ID3DXBuffer* adjBuffer, D3DXVECTOR3* currentFaceNormal, D3DXVECTOR3 adjFaceNormals[3], DWORD faceIndex); void genEdgeVertices( ID3DXMesh* mesh, ID3DXBuffer* adjBuffer); void genEdgeIndices( ID3DXMesh* mesh); }; #endif // __silhouetteEdgesH__
////////////////////////////////////////////////////////////////////////// // //File; silhouetteEdges.cpp // //laserss , 2015.3.14 // ////////////////////////////////////////////////////////////////////////// #include "silhouetteEdges.h" const DWORD MeshVertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL; SilhouetteEdges::SilhouetteEdges(IDirect3DDevice9* device, ID3DXMesh* mesh, ID3DXBuffer* adjBuffer) { _device = device; _vb = 0; _ib = 0; _decl = 0; _numVerts = 0; _numFaces = 0; genEdgeVertices(mesh, adjBuffer); genEdgeIndices(mesh); createVertexDeclaration(); } SilhouetteEdges::~SilhouetteEdges(){ d3d::Release<IDirect3DVertexBuffer9*>(_vb); d3d::Release<IDirect3DIndexBuffer9*>(_ib); d3d::Release<IDirect3DVertexDeclaration9*>(_decl); } bool SilhouetteEdges::createVertexDeclaration(){ //建立SilhouetteEdges中的頂點宣告_decl (但還未啟用) HRESULT hr = 0; D3DVERTEXELEMENT9 decl[] ={ // 一個座標,三個法向量 // 資料流, 位元組偏移量, 資料型別, 網格化方法, 頂點分量的用途, 相同用法頂點分量的序號 {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0}, {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 1}, {0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 2}, D3DDECL_END() }; hr = _device->CreateVertexDeclaration(decl, &_decl); //第二個引數:一個指向所建立的IDirect3DVertexDeclaration*的指標 if(FAILED( hr )){ ::MessageBox(0, L"CreateVertexDeclaration() - FAILED", 0, 0); return false; } return true; //頂點宣告的啟用 //無需呼叫Device->SetFVF( fvf ) //而是呼叫Device->SetVertexDeclaration(_decl)即可 //其中_decl的型別為 IDirect3DVertexDeclaration* } void SilhouetteEdges::getFaceNormal(ID3DXMesh* mesh, DWORD faceIndex, D3DXVECTOR3* faceNormal) {//輸入網格介面指標mesh和想要求法向量的面的序號faceIndex,輸出法向量faceNormal MeshVertex* vertices = 0; //MeshVertex 座標+法向量+FVF mesh->LockVertexBuffer(0, (void**)&vertices); //LockVertexBuffer WORD* indices = 0; mesh->LockIndexBuffer(0, (void**)&indices); //Get the triangle`s indices WORD index0 = indices[faceIndex * 3]; //通過引索快取陣列得到組成第faceIndex個三角形的三個點在vertices陣列中的序號(下標) WORD index1 = indices[faceIndex * 3 + 1]; WORD index2 = indices[faceIndex * 3 + 2]; // Now extract the triangles vertices positions D3DXVECTOR3 v0 = vertices[index0].position; //儲存三個點的座標,用於計算三角形的法向量 D3DXVECTOR3 v1 = vertices[index1].position; D3DXVECTOR3 v2 = vertices[index2].position; //Compute face normal D3DXVECTOR3 edge0, edge1; edge0 = v1 - v0; edge1 = v2 - v0; D3DXVec3Cross(faceNormal, &edge0, &edge1); D3DXVec3Normalize(faceNormal, faceNormal); //計算法向量並儲存在faceNormal中 mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); } void SilhouetteEdges::getFaceNormals(ID3DXMesh* mesh, ID3DXBuffer* adjBuffer, D3DXVECTOR3* currentFaceNormal, D3DXVECTOR3 adjFaceNormals[3], DWORD faceIndex) {//給定網格mesh,鄰接引索介面adjBuffer,該平面序號faceIndex, 輸出該平面和其三個鄰接面的法向量儲存於currentFaceNormal,adjFaceNormal[3]中 MeshVertex* vertices = 0; mesh->LockVertexBuffer(0, (void**)&vertices); WORD* indices = 0; mesh->LockIndexBuffer(0, (void**)&indices); DWORD* adj = (DWORD*)adjBuffer->GetBufferPointer(); // // Get the face normal. getFaceNormal(mesh, faceIndex, currentFaceNormal); // // Get adjacent face indices // 一個三角形有三條邊,即有三個鄰接三角形 WORD faceIndex0 = adj[faceIndex * 3]; WORD faceIndex1 = adj[faceIndex * 3 + 1]; WORD faceIndex2 = adj[faceIndex * 3 + 2]; // Get adjacent face normals, if there is no adjacent face, // then set the adjacent face normal to the opposite of the // "currentFaceNormal". Recall we do this because edges that // don't have an adjacent triangle are automatically considered // silhouette edges. And in order to make that happen, we need // the current face normal and adjacent face normal to point // in the opposite direction. Also, recall that an entry // in the adjacency buffer equal to -1 denotes that the edge // doesn't have an adjacent triangle. //計算鄰接面法向量,若鄰接面不存在,將其法向量設定為 -currentFaceNormal. //若兩個相交面的法向量指向相反的方向,則認為他們共同的那條邊是輪廓邊 //如在一個三角形的某條邊上沒有鄰接面,認為該邊是輪廓邊,並假設存在一個 //法向量與之相反的面 //ps: faceIndex0/1/2 == -1 時,表示沒有鄰接面。 在indices陣列沒有下標為-1 // 的元素 D3DXVECTOR3 faceNormal0, faceNormal1, faceNormal2; if( faceIndex0 != USHRT_MAX ) // is there an adjacent triangle? //define USHRT_MAX 0xffff //有符號時為-1, 無符號時為65535 { WORD i0 = indices[faceIndex0 * 3]; WORD i1 = indices[faceIndex0 * 3 + 1]; WORD i2 = indices[faceIndex0 * 3 + 2]; D3DXVECTOR3 v0 = vertices[i0].position; D3DXVECTOR3 v1 = vertices[i1].position; D3DXVECTOR3 v2 = vertices[i2].position; D3DXVECTOR3 edge0 = v1 - v0; D3DXVECTOR3 edge1 = v2 - v0; D3DXVec3Cross(&faceNormal0, &edge0, &edge1); D3DXVec3Normalize(&faceNormal0, &faceNormal0); } else { faceNormal0 = -(*currentFaceNormal); } if( faceIndex1 != USHRT_MAX ) // is there an adjacent triangle? { WORD i0 = indices[faceIndex1 * 3]; WORD i1 = indices[faceIndex1 * 3 + 1]; WORD i2 = indices[faceIndex1 * 3 + 2]; D3DXVECTOR3 v0 = vertices[i0].position; D3DXVECTOR3 v1 = vertices[i1].position; D3DXVECTOR3 v2 = vertices[i2].position; D3DXVECTOR3 edge0 = v1 - v0; D3DXVECTOR3 edge1 = v2 - v0; D3DXVec3Cross(&faceNormal1, &edge0, &edge1); D3DXVec3Normalize(&faceNormal1, &faceNormal1); } else { faceNormal1 = -(*currentFaceNormal); } if( faceIndex2 != USHRT_MAX ) // is there an adjacent triangle? { WORD i0 = indices[faceIndex2 * 3]; WORD i1 = indices[faceIndex2 * 3 + 1]; WORD i2 = indices[faceIndex2 * 3 + 2]; D3DXVECTOR3 v0 = vertices[i0].position; D3DXVECTOR3 v1 = vertices[i1].position; D3DXVECTOR3 v2 = vertices[i2].position; D3DXVECTOR3 edge0 = v1 - v0; D3DXVECTOR3 edge1 = v2 - v0; D3DXVec3Cross(&faceNormal2, &edge0, &edge1); D3DXVec3Normalize(&faceNormal2, &faceNormal2); } else { faceNormal2 = -(*currentFaceNormal); } // save adjacent face normals adjFaceNormals[0] = faceNormal0; adjFaceNormals[1] = faceNormal1; adjFaceNormals[2] = faceNormal2; mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); } void SilhouetteEdges::genEdgeVertices(ID3DXMesh* mesh, ID3DXBuffer* adjBuffer) { // 3 edges per face and 4 vertices per edge _numVerts = mesh->GetNumFaces() * 3 * 4; //每條邊都有4個點,因為每條邊在特定的角度下都可能會成為輪廓邊 _device->CreateVertexBuffer( _numVerts * sizeof(EdgeVertex), D3DUSAGE_WRITEONLY, 0, // using vertex declaration D3DPOOL_MANAGED, &_vb, 0); MeshVertex* vertices = 0; mesh->LockVertexBuffer(0, (void**)&vertices); WORD* indices = 0; mesh->LockIndexBuffer(0, (void**)&indices); EdgeVertex* edgeVertices = 0; _vb->Lock(0, 0, (void**)&edgeVertices, 0); for(int i = 0; i < mesh->GetNumFaces(); i++) { D3DXVECTOR3 currentFaceNormal; D3DXVECTOR3 adjFaceNormals[3]; getFaceNormals(mesh, adjBuffer, ¤tFaceNormal, adjFaceNormals, i); // get the indices for this face WORD index0 = indices[i * 3]; WORD index1 = indices[i * 3 + 1]; WORD index2 = indices[i * 3 + 2]; // get the vertices for this face MeshVertex v0 = vertices[index0]; MeshVertex v1 = vertices[index1]; MeshVertex v2 = vertices[index2]; // A B // *--------* // | edge | // *--------* // C D // note, C and D are duplicates of A and B respectively, // such that the quad is degenerate. The vertex shader // will un-degenerate the quad if it is a silhouette edge. // compute edge0 v0->v1, note adjacent face // normal is faceNormal0 EdgeVertex A0, B0, C0, D0; A0.position = v0.position; A0.normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); A0.faceNormal1 = currentFaceNormal; A0.faceNormal2 = adjFaceNormals[0]; B0.position = v1.position; B0.normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); B0.faceNormal1 = currentFaceNormal; B0.faceNormal2 = adjFaceNormals[0]; C0 = A0; C0.normal = v0.normal; D0 = B0; D0.normal = v1.normal; *edgeVertices = A0; ++edgeVertices; *edgeVertices = B0; ++edgeVertices; *edgeVertices = C0; ++edgeVertices; *edgeVertices = D0; ++edgeVertices; // compute edge0 v1->v2, note adjacent face // normal is faceNormal1 EdgeVertex A1, B1, C1, D1; A1.position = v1.position; A1.normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); A1.faceNormal1 = currentFaceNormal; A1.faceNormal2 = adjFaceNormals[1]; B1.position = v2.position; B1.normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); B1.faceNormal1 = currentFaceNormal; B1.faceNormal2 = adjFaceNormals[1]; C1 = A1; C1.normal = v1.normal; D1 = B1; D1.normal = v2.normal; *edgeVertices = A1; ++edgeVertices; *edgeVertices = B1; ++edgeVertices; *edgeVertices = C1; ++edgeVertices; *edgeVertices = D1; ++edgeVertices; // compute edge0 v0->v2, note adjacent face // normal is faceNormal2 EdgeVertex A2, B2, C2, D2; A2.position = v0.position; A2.normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); A2.faceNormal1 = currentFaceNormal; A2.faceNormal2 = adjFaceNormals[2]; B2.position = v2.position; B2.normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); B2.faceNormal1 = currentFaceNormal; B2.faceNormal2 = adjFaceNormals[2]; C2 = A2; C2.normal = v0.normal; D2 = B2; D2.normal = v2.normal; *edgeVertices = A2; ++edgeVertices; *edgeVertices = B2; ++edgeVertices; *edgeVertices = C2; ++edgeVertices; *edgeVertices = D2; ++edgeVertices; } _vb->Unlock(); mesh->UnlockVertexBuffer(); mesh->UnlockIndexBuffer(); } void SilhouetteEdges::genEdgeIndices(ID3DXMesh* mesh) { DWORD numEdges = mesh->GetNumFaces() * 3; _numFaces = numEdges * 2; //每條邊上都有一個退化的四邊形,四邊形由兩個三角形組成(退化的四邊形看起來就是一條直線) _device->CreateIndexBuffer( numEdges * 6 * sizeof(WORD), // 2 triangles per edge D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &_ib, 0); WORD* indices = 0; _ib->Lock(0, 0, (void**)&indices, 0); // 0 1 // *--------* // | edge | // *--------* // 2 3 for(UINT i = 0; i < numEdges; i++) { // Six indices to define the triangles of the edge, // so every edge we skip six entries in the // index buffer. Four vertices to define the edge, // so every edge we skip four entries in the // vertex buffer. // 每條邊上都有一個退化的四邊形,即兩個三角形,即四個點六個引索 indices[i * 6] = i * 4 + 0; indices[i * 6 + 1] = i * 4 + 1; indices[i * 6 + 2] = i * 4 + 2; indices[i * 6 + 3] = i * 4 + 1; indices[i * 6 + 4] = i * 4 + 3; indices[i * 6 + 5] = i * 4 + 2; } _ib->Unlock(); } void SilhouetteEdges::render() { _device->SetVertexDeclaration(_decl); _device->SetStreamSource(0, _vb, 0, sizeof(EdgeVertex)); _device->SetIndices(_ib); _device->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, _numVerts, 0, _numFaces); }