练习题5 胶囊体
超4000字无法进行评论,所以把我的 “胶囊” 的代码放在这里^_^。
胶囊 = 半球壳 + 圆柱侧面 + 半球壳。
1 template<class VertexType, class IndexType> 2 inline MeshData<VertexType, IndexType> CreateCapsule(float radius, UINT levels, UINT slices, const DirectX::XMFLOAT4 & color) 3 { 4 using namespace DirectX; 5 6 MeshData<VertexType, IndexType> meshData; 7 UINT vertexCount = 2 + (levels ) * (slices + 1) + 2 * (slices + 1); // 计算好空间 8 UINT indexCount = 6 * (levels ) * slices + 6 * slices; 9 meshData.vertexVec.resize(vertexCount); 10 meshData.indexVec.resize(indexCount); 11 12 Internal::VertexData vertexData; 13 IndexType vIndex = 0, iIndex = 0; 14 15 float phi = 0.0f, theta = 0.0f; 16 float per_phi = XM_PI / levels; 17 float per_theta = XM_2PI / slices; 18 float x, y, z; 19 20 // 21 // 绘制顶部半球壳 放顶点-》放层级点-》放顶点索引-》放层级点索引 22 // 23 24 // 放入顶端点 25 vertexData = { XMFLOAT3(0.0f, radius + 1.0f, 0.0f), XMFLOAT3(0.0f, 1.0f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(0.0f, 0.0f) }; 26 Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData); 27 28 // 放入中间上层 顶点 29 for (UINT i = 1; i <= levels / 2; ++i)// 要多填一层顶点供使用 30 { 31 phi = per_phi * i; 32 // 需要slices + 1个顶点是因为 起点和终点需为同一点,但纹理坐标值不一致 33 for (UINT j = 0; j <= slices; ++j) 34 { 35 theta = per_theta * j; 36 x = radius * sinf(phi) * cosf(theta); 37 y = radius * cosf(phi) + 1.0f; 38 z = radius * sinf(phi) * sinf(theta); 39 // 计算出局部坐标、法向量、Tangent向量和纹理坐标 40 XMFLOAT3 pos = XMFLOAT3(x, y, z), normal; 41 XMStoreFloat3(&normal, XMVector3Normalize(XMLoadFloat3(&pos))); 42 43 vertexData = { pos, normal, XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(theta / XM_2PI, phi / XM_PI) }; 44 Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData); 45 } 46 } 47 48 // 放入顶点三角形索引 49 if (levels > 1) 50 { 51 for (UINT j = 1; j <= slices; ++j) 52 { 53 meshData.indexVec[iIndex++] = 0; 54 meshData.indexVec[iIndex++] = j % (slices + 1) + 1; 55 meshData.indexVec[iIndex++] = j; 56 } 57 } 58 59 // 放入中间上层三角形索引 60 for (UINT i = 1; i < levels / 2; ++i)// 处理索引则要少一层,cause 上一层顶点负责绘制本层三角面片 61 { 62 for (UINT j = 1; j <= slices; ++j) 63 { 64 meshData.indexVec[iIndex++] = (i - 1) * (slices + 1) + j; 65 meshData.indexVec[iIndex++] = (i - 1) * (slices + 1) + j % (slices + 1) + 1; 66 meshData.indexVec[iIndex++] = i * (slices + 1) + j % (slices + 1) + 1; 67 68 meshData.indexVec[iIndex++] = i * (slices + 1) + j % (slices + 1) + 1; 69 meshData.indexVec[iIndex++] = i * (slices + 1) + j; 70 meshData.indexVec[iIndex++] = (i - 1) * (slices + 1) + j; 71 } 72 } 73 74 // 75 // 绘制中部圆柱侧面 放上面圆顶点-》放下面圆顶点-》放索引点 76 // 77 78 UINT vIndex_cur = vIndex; // 当前顶点数 79 float height = 2.0f, h2 = height / 2; 80 // 放入侧面顶端点 81 for (UINT i = 0; i <= slices; ++i) 82 { 83 theta = i * per_theta; 84 vertexData = { XMFLOAT3(radius * cosf(theta), h2, radius * sinf(theta)), XMFLOAT3(cosf(theta), 0.0f, sinf(theta)), 85 XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(theta / XM_2PI, 0.0f) }; 86 Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData); 87 } 88 // 放入侧面底端点 89 for (UINT i = 0; i <= slices; ++i) 90 { 91 theta = i * per_theta; 92 vertexData = { XMFLOAT3(radius * cosf(theta), -h2, radius * sinf(theta)), XMFLOAT3(cosf(theta), 0.0f, sinf(theta)), 93 XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(theta / XM_2PI, 1.0f) }; 94 Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData); 95 } 96 // 放入索引,注意更新顶点索引值 97 for (UINT i = vIndex_cur; i < vIndex_cur + slices; ++i) 98 { 99 meshData.indexVec[iIndex++] = i; 100 meshData.indexVec[iIndex++] = i + 1; 101 meshData.indexVec[iIndex++] = (slices + 1) + i + 1; 102 103 meshData.indexVec[iIndex++] = (slices + 1) + i + 1; 104 meshData.indexVec[iIndex++] = (slices + 1) + i; 105 meshData.indexVec[iIndex++] = i; 106 } 107 108 // 109 // 绘制底部半球壳 放层级点-》放底部顶点-》放层级索引-》放底部顶点索引 110 // 111 112 UINT add = 2 * (slices + 1); // 圆柱顶点数,用于确定下半球壳顶点索引 113 114 // 放入中间下层 层级点 115 for (UINT i = levels/2+1; i < levels ; ++i)// 要多填一层顶点供使用 116 { 117 phi = per_phi * i; 118 // 需要slices + 1个顶点是因为 起点和终点需为同一点,但纹理坐标值不一致 119 for (UINT j = 0; j <= slices; ++j) 120 { 121 theta = per_theta * j; 122 x = radius * sinf(phi) * cosf(theta); 123 y = radius * cosf(phi) - 1.0f; 124 z = radius * sinf(phi) * sinf(theta); 125 // 计算出局部坐标、法向量、Tangent向量和纹理坐标 126 XMFLOAT3 pos = XMFLOAT3(x, y, z), normal; 127 XMStoreFloat3(&normal, XMVector3Normalize(XMLoadFloat3(&pos))); 128 129 vertexData = { pos, normal, XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(theta / XM_2PI, phi / XM_PI) }; 130 Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData); 131 } 132 } 133 134 // 放入底端点 135 vertexData = { XMFLOAT3(0.0f, -radius-1.0f, 0.0f), XMFLOAT3(0.0f, -1.0f, 0.0f), 136 XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(0.0f, 1.0f) }; 137 Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData); 138 139 // 放入中间下层索引,注意顶点索引值 140 for (UINT i = levels / 2; i < levels - 1; ++i)// 处理索引则要少一层,cause 上一层顶点负责绘制本层三角面片 141 { 142 for (UINT j = 1; j <= slices; ++j) 143 { 144 meshData.indexVec[iIndex++] = add + (i - 1) * (slices + 1) + j; 145 meshData.indexVec[iIndex++] = add + (i - 1) * (slices + 1) + j % (slices + 1) + 1; 146 meshData.indexVec[iIndex++] = add + i * (slices + 1) + j % (slices + 1) + 1; 147 148 meshData.indexVec[iIndex++] = add + i * (slices + 1) + j % (slices + 1) + 1; 149 meshData.indexVec[iIndex++] = add + i * (slices + 1) + j; 150 meshData.indexVec[iIndex++] = add + (i - 1) * (slices + 1) + j; 151 } 152 } 153 154 // 放入底端顶点索引,注意顶点索引值(最下面一层) 155 if (levels > 1) 156 { 157 for (UINT j = 1; j <= slices; ++j) 158 { 159 meshData.indexVec[iIndex++] = add + (levels - 2) * (slices + 1) + j; 160 meshData.indexVec[iIndex++] = add + (levels - 2) * (slices + 1) + j % (slices + 1) + 1; 161 meshData.indexVec[iIndex++] = add + (levels - 1) * (slices + 1) + 1; 162 } 163 } 164 165 return meshData; 166 }
效果:


浙公网安备 33010602011771号