本文由zhangbaochong原創,轉載請注明出處http://www.cnblogs.com/zhangbaochong/p/5573970.html
前面實現簡單地形的教程,我們只是繪制了一個網格,這一次我們來學習一下幾種基本幾何體的繪制,包括平面網格、立方體、圓柱和球體等。
原來在GeometryGenerator類中只給出了CreateGrid一個方法來繪制網格,現在讓我們添加其他方法繪制一些幾何體。
為了方便繪制幾何體方法的調用,GeometryGenerator類我們使用了單例模式。很簡單,將構造函數設為private,添加一個GetInstance函數如下:
//單例模式 static GeometryGenerator* GetInstance() { static GeometryGenerator instance; return &instance; }
這只是實現單例模式的一種方法,還有幾種實現單例模式的方法就不一一說明了。
1.基本幾何體繪制方法
下面介紹幾種常見幾何體的繪制方法(代碼均參考dx11龍書)。
1.1網格
網格可以說是最常見同時也是最重要的,像實現地形水面等都離不開網格。生成一個網格首先要給出網格的寬和高,以及在寬和高上划分的格子數。
看龍書中給出的一張圖片就明白了:

由此,頂點的坐標就很容易生成了。
頂點索引的計算關鍵是推導出一個用於求構成第i行,第j列的頂點處右下方兩個三角形的頂點索引的通用公式。

對頂點緩存中的任意一點A,如果該點位於地形中的第i行、第j列的話,那么該點在頂點緩存中所對應的位置應該就是i*m+j(m為每行的頂點數)。如果A點在索引緩存中的位置為k的話,那么A點為起始點構成的三角形ABC中,B、C頂點在頂點緩存中的位置就為(i+1)x m+j和i x m+(j+1)。且B點索引值為k+1,C點索引值為k+2.這樣。這樣,公式就可以推導為如下:
三角形ABC=【i*每行頂點數+j,i*每行頂點數+(j+1),(i+1)*行頂點數+j】
三角形CBD=【(i+1)*每行頂點數+j,i*每行頂點數+(j+1),(i+1)*行頂點數+(j+1)】
1 void GeometryGenerator::CreateGrid(float width, float height, UINT m, UINT n, MeshData &mesh) 2 { 3 mesh.vertices.clear(); 4 mesh.indices.clear(); 5 //每行頂點數、每列頂點數 6 UINT nVertsRow = m + 1; 7 UINT nVertsCol = n + 1; 8 //起始x、z坐標 9 float oX = -width * 0.5f; 10 float oZ = height * 0.5f; 11 //每一格坐標變化 12 float dx = width / m; 13 float dz = height / n; 14 15 //頂點總數量:nVertsRow * nVertsCol 16 mesh.vertices.resize(nVertsRow * nVertsCol); 17 18 //逐個添加頂點 19 for (UINT i = 0; i < nVertsCol; ++i) 20 { 21 float tmpZ = oZ - dz * i; 22 for (UINT j = 0; j < nVertsRow; ++j) 23 { 24 UINT index = nVertsRow * i + j; 25 mesh.vertices[index].pos.x = oX + dx * j; 26 mesh.vertices[index].pos.y = 0.f; 27 mesh.vertices[index].pos.z = tmpZ; 28 29 mesh.vertices[index].normal = XMFLOAT3(0.f, 1.f, 0.f); 30 mesh.vertices[index].tangent = XMFLOAT3(1.f, 0.f, 0.f); 31 32 mesh.vertices[index].tex = XMFLOAT2(dx*i, dx*j); 33 } 34 } 35 36 //總格子數量:m * n 37 //因此總索引數量: 6 * m * n 38 UINT nIndices = m * n * 6; 39 mesh.indices.resize(nIndices); 40 UINT tmp = 0; 41 for (UINT i = 0; i < n; ++i) 42 { 43 for (UINT j = 0; j < m; ++j) 44 { 45 mesh.indices[tmp] = i * nVertsRow + j; 46 mesh.indices[tmp + 1] = i * nVertsRow + j + 1; 47 mesh.indices[tmp + 2] = (i + 1) * nVertsRow + j; 48 mesh.indices[tmp + 3] = i * nVertsRow + j + 1; 49 mesh.indices[tmp + 4] = (i + 1) * nVertsRow + j + 1; 50 mesh.indices[tmp + 5] = (i + 1) * nVertsRow + j; 51 52 tmp += 6; 53 } 54 } 55 }
1.2立方體
立方體的繪制就很簡單了,一個立方體只需要提供三維方向上的長度。有一點與之前繪制彩色立方體時不一樣的是,我們這里創建立方體用到24個頂點(每個面4個),而之前彩色立方體只用到了8個頂點(每個頂點被3個面共享)。這是因為在后面學習過程中我們需要頂點的法線坐標,而一個頂點相對於其連接的3個面來說,法線完全不同,因此無法共享頂點。
1 void GeometryGenerator::CreateBox(float width, float height, float depth, MeshData &mesh) 2 { 3 mesh.vertices.clear(); 4 mesh.indices.clear(); 5 6 //一共24個頂點(每面4個) 7 mesh.vertices.resize(24); 8 //一共36個索引(每面6個) 9 mesh.indices.resize(36); 10 11 float halfW = width * 0.5f; 12 float halfH = height * 0.5f; 13 float halfD = depth * 0.5f; 14 15 //眼睛面向z軸正方向 16 //構建頂點 17 //前面 18 mesh.vertices[0].pos = XMFLOAT3(-halfW, -halfH, -halfD); 19 mesh.vertices[0].normal = XMFLOAT3(0.f, 0.f, -1.f); 20 mesh.vertices[0].tangent = XMFLOAT3(1.f, 0.f, 0.f); 21 mesh.vertices[0].tex = XMFLOAT2(0.f, 1.f); 22 mesh.vertices[1].pos = XMFLOAT3(-halfW, halfH, -halfD); 23 mesh.vertices[1].normal = XMFLOAT3(0.f, 0.f, -1.f); 24 mesh.vertices[1].tangent = XMFLOAT3(1.f, 0.f, 0.f); 25 mesh.vertices[1].tex = XMFLOAT2(0.f, 0.f); 26 mesh.vertices[2].pos = XMFLOAT3(halfW, halfH, -halfD); 27 mesh.vertices[2].normal = XMFLOAT3(0.f, 0.f, -1.f); 28 mesh.vertices[2].tangent = XMFLOAT3(1.f, 0.f, 0.f); 29 mesh.vertices[2].tex = XMFLOAT2(1.f, 0.f); 30 mesh.vertices[3].pos = XMFLOAT3(halfW, -halfH, -halfD); 31 mesh.vertices[3].normal = XMFLOAT3(0.f, 0.f, -1.f); 32 mesh.vertices[3].tangent = XMFLOAT3(1.f, 0.f, 0.f); 33 mesh.vertices[3].tex = XMFLOAT2(1.f, 1.f); 34 //左側面 35 mesh.vertices[4].pos = XMFLOAT3(-halfW, -halfH, halfD); 36 mesh.vertices[4].normal = XMFLOAT3(-1.f, 0.f, 0.f); 37 mesh.vertices[4].tangent = XMFLOAT3(0.f, 0.f, -1.f); 38 mesh.vertices[4].tex = XMFLOAT2(0.f, 1.f); 39 mesh.vertices[5].pos = XMFLOAT3(-halfW, halfH, halfD); 40 mesh.vertices[5].normal = XMFLOAT3(-1.f, 0.f, 0.f); 41 mesh.vertices[5].tangent = XMFLOAT3(0.f, 0.f, -1.f); 42 mesh.vertices[5].tex = XMFLOAT2(0.f, 0.f); 43 mesh.vertices[6].pos = XMFLOAT3(-halfW, halfH, -halfD); 44 mesh.vertices[6].normal = XMFLOAT3(-1.f, 0.f, 0.f); 45 mesh.vertices[6].tangent = XMFLOAT3(0.f, 0.f, -1.f); 46 mesh.vertices[6].tex = XMFLOAT2(1.f, 0.f); 47 mesh.vertices[7].pos = XMFLOAT3(-halfW, -halfH, -halfD); 48 mesh.vertices[7].normal = XMFLOAT3(-1.f, 0.f, 0.f); 49 mesh.vertices[7].tangent = XMFLOAT3(0.f, 0.f, -1.f); 50 mesh.vertices[7].tex = XMFLOAT2(1.f, 1.f); 51 //背面 52 mesh.vertices[8].pos = XMFLOAT3(halfW, -halfH, halfD); 53 mesh.vertices[8].normal = XMFLOAT3(0.f, 0.f, 1.f); 54 mesh.vertices[8].tangent = XMFLOAT3(-1.f, 0.f, 0.f); 55 mesh.vertices[8].tex = XMFLOAT2(0.f, 1.f); 56 mesh.vertices[9].pos = XMFLOAT3(halfW, halfH, halfD); 57 mesh.vertices[9].normal = XMFLOAT3(0.f, 0.f, 1.f); 58 mesh.vertices[9].tangent = XMFLOAT3(-1.f, 0.f, 0.f); 59 mesh.vertices[9].tex = XMFLOAT2(0.f, 0.f); 60 mesh.vertices[10].pos = XMFLOAT3(-halfW, halfH, halfD); 61 mesh.vertices[10].normal = XMFLOAT3(0.f, 0.f, 1.f); 62 mesh.vertices[10].tangent = XMFLOAT3(-1.f, 0.f, 0.f); 63 mesh.vertices[10].tex = XMFLOAT2(1.f, 0.f); 64 mesh.vertices[11].pos = XMFLOAT3(-halfW, -halfH, halfD); 65 mesh.vertices[11].normal = XMFLOAT3(0.f, 0.f, 1.f); 66 mesh.vertices[11].tangent = XMFLOAT3(-1.f, 0.f, 0.f); 67 mesh.vertices[11].tex = XMFLOAT2(1.f, 1.f); 68 //右側面 69 mesh.vertices[12].pos = XMFLOAT3(halfW, -halfH, -halfD); 70 mesh.vertices[12].normal = XMFLOAT3(1.f, 0.f, 0.f); 71 mesh.vertices[12].tangent = XMFLOAT3(0.f, 0.f, 1.f); 72 mesh.vertices[12].tex = XMFLOAT2(0.f, 1.f); 73 mesh.vertices[13].pos = XMFLOAT3(halfW, halfH, -halfD); 74 mesh.vertices[13].normal = XMFLOAT3(1.f, 0.f, 0.f); 75 mesh.vertices[13].tangent = XMFLOAT3(0.f, 0.f, 1.f); 76 mesh.vertices[13].tex = XMFLOAT2(0.f, 0.f); 77 mesh.vertices[14].pos = XMFLOAT3(halfW, halfH, halfD); 78 mesh.vertices[14].normal = XMFLOAT3(1.f, 0.f, 0.f); 79 mesh.vertices[14].tangent = XMFLOAT3(0.f, 0.f, 1.f); 80 mesh.vertices[14].tex = XMFLOAT2(1.f, 0.f); 81 mesh.vertices[15].pos = XMFLOAT3(halfW, -halfH, halfD); 82 mesh.vertices[15].normal = XMFLOAT3(1.f, 0.f, 0.f); 83 mesh.vertices[15].tangent = XMFLOAT3(0.f, 0.f, 1.f); 84 mesh.vertices[15].tex = XMFLOAT2(1.f, 1.f); 85 //上面 86 mesh.vertices[16].pos = XMFLOAT3(-halfW, halfH, -halfD); 87 mesh.vertices[16].normal = XMFLOAT3(0.f, 1.f, 0.f); 88 mesh.vertices[16].tangent = XMFLOAT3(1.f, 0.f, 0.f); 89 mesh.vertices[16].tex = XMFLOAT2(0.f, 1.f); 90 mesh.vertices[17].pos = XMFLOAT3(-halfW, halfH, halfD); 91 mesh.vertices[17].normal = XMFLOAT3(0.f, 1.f, 0.f); 92 mesh.vertices[17].tangent = XMFLOAT3(1.f, 0.f, 0.f); 93 mesh.vertices[17].tex = XMFLOAT2(0.f, 0.f); 94 mesh.vertices[18].pos = XMFLOAT3(halfW, halfH, halfD); 95 mesh.vertices[18].normal = XMFLOAT3(0.f, 1.f, 0.f); 96 mesh.vertices[18].tangent = XMFLOAT3(1.f, 0.f, 0.f); 97 mesh.vertices[18].tex = XMFLOAT2(1.f, 0.f); 98 mesh.vertices[19].pos = XMFLOAT3(halfW, halfH, -halfD); 99 mesh.vertices[19].normal = XMFLOAT3(0.f, 1.f, 0.f); 100 mesh.vertices[19].tangent = XMFLOAT3(1.f, 0.f, 0.f); 101 mesh.vertices[19].tex = XMFLOAT2(1.f, 1.f); 102 //底面 103 mesh.vertices[20].pos = XMFLOAT3(-halfW, -halfH, halfD); 104 mesh.vertices[20].normal = XMFLOAT3(0.f, -1.f, 0.f); 105 mesh.vertices[20].tangent = XMFLOAT3(1.f, 0.f, 0.f); 106 mesh.vertices[20].tex = XMFLOAT2(0.f, 1.f); 107 mesh.vertices[21].pos = XMFLOAT3(-halfW, -halfH, -halfD); 108 mesh.vertices[21].normal = XMFLOAT3(0.f, -1.f, 0.f); 109 mesh.vertices[21].tangent = XMFLOAT3(1.f, 0.f, 0.f); 110 mesh.vertices[21].tex = XMFLOAT2(0.f, 0.f); 111 mesh.vertices[22].pos = XMFLOAT3(halfW, -halfH, -halfD); 112 mesh.vertices[22].normal = XMFLOAT3(0.f, -1.f, 0.f); 113 mesh.vertices[22].tangent = XMFLOAT3(1.f, 0.f, 0.f); 114 mesh.vertices[22].tex = XMFLOAT2(1.f, 0.f); 115 mesh.vertices[23].pos = XMFLOAT3(halfW, -halfH, halfD); 116 mesh.vertices[23].normal = XMFLOAT3(0.f, -1.f, 0.f); 117 mesh.vertices[23].tangent = XMFLOAT3(1.f, 0.f, 0.f); 118 mesh.vertices[23].tex = XMFLOAT2(1.f, 1.f); 119 120 //構建索引 121 mesh.indices[0] = 0; 122 mesh.indices[1] = 1; 123 mesh.indices[2] = 2; 124 mesh.indices[3] = 0; 125 mesh.indices[4] = 2; 126 mesh.indices[5] = 3; 127 128 mesh.indices[6] = 4; 129 mesh.indices[7] = 5; 130 mesh.indices[8] = 6; 131 mesh.indices[9] = 4; 132 mesh.indices[10] = 6; 133 mesh.indices[11] = 7; 134 135 mesh.indices[12] = 8; 136 mesh.indices[13] = 9; 137 mesh.indices[14] = 10; 138 mesh.indices[15] = 8; 139 mesh.indices[16] = 10; 140 mesh.indices[17] = 11; 141 142 mesh.indices[18] = 12; 143 mesh.indices[19] = 13; 144 mesh.indices[20] = 14; 145 mesh.indices[21] = 12; 146 mesh.indices[22] = 14; 147 mesh.indices[23] = 15; 148 149 mesh.indices[24] = 16; 150 mesh.indices[25] = 17; 151 mesh.indices[26] = 18; 152 mesh.indices[27] = 16; 153 mesh.indices[28] = 18; 154 mesh.indices[29] = 19; 155 156 mesh.indices[30] = 20; 157 mesh.indices[31] = 21; 158 mesh.indices[32] = 22; 159 mesh.indices[33] = 20; 160 mesh.indices[34] = 22; 161 mesh.indices[35] = 23; 162 }
1.3圓柱
為了構建一個圓柱,需要提供如下信息:圓柱的上口半徑(topRadius),下口半徑(bottomRadius),高度(height)。此外,為了指定圓柱的精細度,還需要指定兩個參數,一個為沒高度方向上平均划分的個數(stack),另一個為沿圓周方向等分的個數(slice)。
可以根據龍書中給出的圖理解一下:

1 void GeometryGenerator::CreateCylinder(float topRadius, float bottomRadius, float height, int slice, int stack, MeshData &mesh) 2 { 3 mesh.vertices.clear(); 4 mesh.indices.clear(); 5 6 //從上到下每個stack半徑變化量:dRadius 7 float dRadius = (bottomRadius - topRadius) / stack; 8 //每個stack高度:dHeight 9 float dHeight = height / stack; 10 11 //每個圓周上頂點數量:slice+1 12 int vertsPerRow = slice + 1; 13 //頂點行數:stack+1 14 int nRows = stack + 1; 15 16 //總頂點數 17 int nVerts = vertsPerRow * nRows; 18 //總索引數 19 int nIndices = slice * stack * 6; 20 21 mesh.vertices.resize(nVerts); 22 mesh.indices.resize(nIndices); 23 24 //頂部Y坐標 25 float topY = height * 0.5f; 26 27 for (int i = 0; i < nRows; ++i) 28 { 29 float tmpY = topY - dHeight * i; 30 float tmpRadius = topRadius + i * dRadius; 31 32 for (int j = 0; j < vertsPerRow; ++j) 33 { 34 float theta = XM_2PI * j / slice; 35 int index = i * vertsPerRow + j; 36 mesh.vertices[index].pos = XMFLOAT3(tmpRadius*cos(theta), tmpY, tmpRadius*sin(theta)); 37 } 38 } 39 40 UINT tmp(0); 41 for (int i = 0; i < stack; ++i) 42 { 43 for (int j = 0; j < slice; ++j) 44 { 45 mesh.indices[tmp] = i * vertsPerRow + j; 46 mesh.indices[tmp + 1] = (i + 1) * vertsPerRow + j + 1; 47 mesh.indices[tmp + 2] = (i + 1) * vertsPerRow + j; 48 mesh.indices[tmp + 3] = i * vertsPerRow + j; 49 mesh.indices[tmp + 4] = i * vertsPerRow + j + 1; 50 mesh.indices[tmp + 5] = (i + 1) * vertsPerRow + j + 1; 51 52 tmp += 6; 53 } 54 } 55 }
1.4球
繪制球基本參數只有一個半徑,但是同圓柱一樣為了指定精細程度也要給出stack和slice兩個參數,這里slice是從上極點沿球面到下極點的180度角等分。具體繪制可以看代碼理解:
1 void GeometryGenerator::CreateSphere(float radius, int slice, int stack, MeshData &mesh) 2 { 3 4 mesh.vertices.clear(); 5 mesh.indices.clear(); 6 7 int vertsPerRow = slice + 1; 8 int nRows = stack - 1; 9 10 int nVerts = vertsPerRow * nRows + 2; 11 int nIndices = (nRows - 1)*slice * 6 + slice * 6; 12 13 mesh.vertices.resize(nVerts); 14 mesh.indices.resize(nIndices); 15 16 for (int i = 1; i <= nRows; ++i) 17 { 18 float phy = XM_PI * i / stack; 19 float tmpRadius = radius * sin(phy); 20 for (int j = 0; j < vertsPerRow; ++j) 21 { 22 float theta = XM_2PI * j / slice; 23 UINT index = (i - 1)*vertsPerRow + j; 24 25 float x = tmpRadius*cos(theta); 26 float y = radius*cos(phy); 27 float z = tmpRadius*sin(theta); 28 29 //位置坐標 30 mesh.vertices[index].pos = XMFLOAT3(x, y, z); 31 //法線 32 XMVECTOR N = XMVectorSet(x, y, z, 0.f); 33 XMStoreFloat3(&mesh.vertices[index].normal, XMVector3Normalize(N)); 34 //切線 35 XMVECTOR T = XMVectorSet(-sin(theta), 0.f, cos(theta), 0.f); 36 XMStoreFloat3(&mesh.vertices[index].tangent, XMVector3Normalize(T)); 37 //紋理坐標 38 mesh.vertices[index].tex = XMFLOAT2(j*1.f / slice, i*1.f / stack); 39 } 40 } 41 42 int size = vertsPerRow * nRows; 43 //添加頂部和底部兩個頂點信息 44 mesh.vertices[size].pos = XMFLOAT3(0.f, radius, 0.f); 45 mesh.vertices[size].normal = XMFLOAT3(0.f, 1.f, 0.f); 46 mesh.vertices[size].tangent = XMFLOAT3(1.f, 0.f, 0.f); 47 mesh.vertices[size].tex = XMFLOAT2(0.f, 0.f); 48 49 mesh.vertices[size + 1].pos = XMFLOAT3(0.f, -radius, 0.f); 50 mesh.vertices[size + 1].normal = XMFLOAT3(0.f, -1.f, 0.f); 51 mesh.vertices[size + 1].tangent = XMFLOAT3(1.f, 0.f, 0.f); 52 mesh.vertices[size + 1].tex = XMFLOAT2(0.f, 1.f); 53 54 UINT tmp(0); 55 int start1 = 0; 56 int start2 = mesh.vertices.size() - vertsPerRow - 2; 57 int top = size; 58 int bottom = size + 1; 59 for (int i = 0; i < slice; ++i) 60 { 61 mesh.indices[tmp] = top; 62 mesh.indices[tmp + 1] = start1 + i + 1; 63 mesh.indices[tmp + 2] = start1 + i; 64 65 tmp += 3; 66 } 67 68 for (int i = 0; i < slice; ++i) 69 { 70 mesh.indices[tmp] = bottom; 71 mesh.indices[tmp + 1] = start2 + i; 72 mesh.indices[tmp + 2] = start2 + i + 1; 73 74 tmp += 3; 75 } 76 77 for (int i = 0; i < nRows - 1; ++i) 78 { 79 for (int j = 0; j < slice; ++j) 80 { 81 mesh.indices[tmp] = i * vertsPerRow + j; 82 mesh.indices[tmp + 1] = (i + 1) * vertsPerRow + j + 1; 83 mesh.indices[tmp + 2] = (i + 1) * vertsPerRow + j; 84 mesh.indices[tmp + 3] = i * vertsPerRow + j; 85 mesh.indices[tmp + 4] = i * vertsPerRow + j + 1; 86 mesh.indices[tmp + 5] = (i + 1) * vertsPerRow + j + 1; 87 88 tmp += 6; 89 } 90 } 91 }
2.場景繪制
2.1最終效果

2.2多個幾何體共享頂點索引緩沖區
我們一共繪制了四種幾何體:網格、立方體、球和圓柱,它們公用了一個頂點和索引緩沖區。這樣我們就需要在其中找出每個幾何體對應的位置。
為了在頂點、索引緩沖區中找到一個物體對應的位置,我們使用三個參數:該物體在頂點緩沖區中的起始位置(VStart),索引緩沖區中的起始位置(IStart),以及索引總數(totalIndices)。看龍書中的一幅圖就很容易理解了:

2.3設為線框模式繪制
在Render函數中創建一個柵格化狀態ID3D11RasterizerState ,狀態描述中FillMode設為D3D11_FILL_WIREFRAME即可
1 //設置為線框繪制模式 2 D3D11_RASTERIZER_DESC rsDesc; 3 ZeroMemory(&rsDesc, sizeof(rsDesc)); 4 rsDesc.CullMode = D3D11_CULL_BACK; 5 rsDesc.DepthClipEnable = true; 6 //D3D11_FILL_WIREFRAME以線框模式繪制,D3D11_FILL_SOLID是以實體模式繪制 7 rsDesc.FillMode = D3D11_FILL_WIREFRAME; 8 rsDesc.FrontCounterClockwise = false; 9 ID3D11RasterizerState *rsState(nullptr); 10 m_pd3dDevice->CreateRasterizerState(&rsDesc, &rsState); 11 m_pImmediateContext->RSSetState(rsState);
源碼下載:http://files.cnblogs.com/files/zhangbaochong/GeoDrawDemo.zip
