DirectX Tutorials www.andypike.com By Andy Pike 第九章:有纹理的球体、圆柱体和锥体
Introduction (序)
这一章我们将学习怎样创建有纹理的球体、圆柱体和锥体,我们也会为它们设置法线,这样它们就能产生正确的阴影了。而且,我们将会使所有的类都从一个基类CBase派生,这是一个能以HTML格式来生成记录文件的和有计算法线向量功能的类。我们这一章的例子将会含有两个圆柱体、两个圆锥体和一个简单的绕太阳旋转的地月模型。
How to make a cylinder (怎样生成圆柱体)
我们的圆柱体将会含有两个三角扇形,一个做顶,一个做底;它的周身将由一个三角带来完成。下图(9.1)演示了它的构成,顶点和三角形的分布;并且也演示了它的一个段上的几个顶点的法线是什么样子的。这里我们只用顶点缓冲而不用索引缓冲,原因是我们想要它的顶、底与周身相拼接的地方的一些重合顶点拥有不同的法线,如果用索引缓冲就不行了(因为它的实现是把几个重合的顶点看作一个)。X
Fig 9.1
要创建一个圆柱体我们需要指定它的分段数,在上面的图示中为8。当然,分段数越多,看起来就会越平滑(越接近圆)。我们还需要指定它的高和半径,这样它的顶点、法线、纹理坐标就都出来了。下面的代码片断能告诉你这一切是怎样完成的。 bool CCylinder::UpdateVertices() { CYLINDER_CUSTOMVERTEX* pVertex; WORD wVertexIndex = 0; int nCurrentSegment; //Lock the vertex buffer if(FAILED(m_pVertexBuffer->Lock(0, 0, (BYTE**)&pVertex, 0))) { LogError(\ return false; } float rDeltaSegAngle = (2.0f * D3DX_PI / m_nSegments); float rSegmentLength = 1.0f / (float)m_nSegments; - 1 -
DirectX Tutorials www.andypike.com By Andy Pike //Create the sides triangle strip for(nCurrentSegment = 0; nCurrentSegment <= m_nSegments; nCurrentSegment++) { float x0 = m_rRadius * sinf(nCurrentSegment * rDeltaSegAngle); float z0 = m_rRadius * cosf(nCurrentSegment * rDeltaSegAngle); pVertex->x = x0; pVertex->y = 0.0f + (m_rHeight / 2.0f); pVertex->z = z0; pVertex->nx = x0; pVertex->ny = 0.0f; pVertex->nz = z0; pVertex->tu = 1.0f - (rSegmentLength * (float)nCurrentSegment); pVertex->tv = 0.0f; pVertex++; pVertex->x = x0; pVertex->y = 0.0f - (m_rHeight / 2.0f); pVertex->z = z0; pVertex->nx = x0; pVertex->ny = 0.0f; pVertex->nz = z0; pVertex->tu = 1.0f - (rSegmentLength * (float)nCurrentSegment); pVertex->tv = 1.0f; pVertex++; } //Create the top triangle fan: Center pVertex->x = 0.0f; pVertex->y = 0.0f + (m_rHeight / 2.0f); pVertex->z = 0.0f; pVertex->nx = 0.0f; pVertex->ny = 1.0f; pVertex->nz = 0.0f; pVertex->tu = 0.5f; pVertex->tv = 0.5f; pVertex++; //Create the top triangle fan: Edges for(nCurrentSegment = 0; nCurrentSegment <= m_nSegments; nCurrentSegment++) { float x0 = m_rRadius * sinf(nCurrentSegment * rDeltaSegAngle); float z0 = m_rRadius * cosf(nCurrentSegment * rDeltaSegAngle); pVertex->x = x0; pVertex->y = 0.0f + (m_rHeight / 2.0f); pVertex->z = z0; pVertex->nx = 0.0f; pVertex->ny = 1.0f; pVertex->nz = 0.0f; float tu0 = (0.5f * sinf(nCurrentSegment * rDeltaSegAngle)) + 0.5f; float tv0 = (0.5f * cosf(nCurrentSegment * rDeltaSegAngle)) + 0.5f; pVertex->tu = tu0; pVertex->tv = tv0; pVertex++; } //Create the bottom triangle fan: Center pVertex->x = 0.0f; pVertex->y = 0.0f - (m_rHeight / 2.0f); pVertex->z = 0.0f; - 2 -
DirectX Tutorials www.andypike.com By Andy Pike pVertex->nx = 0.0f; pVertex->ny = -1.0f; pVertex->nz = 0.0f; pVertex->tu = 0.5f; pVertex->tv = 0.5f; pVertex++; //Create the bottom triangle fan: Edges for(nCurrentSegment = m_nSegments; nCurrentSegment >= 0; nCurrentSegment--) { float x0 = m_rRadius * sinf(nCurrentSegment * rDeltaSegAngle); float z0 = m_rRadius * cosf(nCurrentSegment * rDeltaSegAngle); pVertex->x = x0; pVertex->y = 0.0f - (m_rHeight / 2.0f); pVertex->z = z0; pVertex->nx = 0.0f; pVertex->ny = -1.0f; pVertex->nz = 0.0f; float tu0 = (0.5f * sinf(nCurrentSegment * rDeltaSegAngle)) + 0.5f; float tv0 = (0.5f * cosf(nCurrentSegment * rDeltaSegAngle)) + 0.5f; pVertex->tu = tu0; pVertex->tv = tv0; pVertex++; } if(FAILED(m_pVertexBuffer->Unlock())) { LogError(\ return false; } return true; }
那末然后呢?锁定顶点缓冲之后我们需要计算每个段的角度,也就是用分段数来平分2XPI个弧度,它将会被保存在rDeltaSegAngle中,在以后定义顶点位置时将会被用到。我们为它的周身设置纹理坐标时还得计算每段的长度,也就是用分段数来平分纹理坐标的最大值1.0。
然后我们还得定义它的周身。我们有一个循环,它会顺序的处理每一个分段,计算分段的顶和底的顶点的x和z值,它的y值很好计算,正的半个高度和负的半个高度就是了。我们用段的长度来计算当前段的纹理坐标。
然后,我们就能定义顶和底的三角扇形了。首先我们定义它们的中心点(圆心),然后用相同的计算方法得出边缘的顶点。完成后,顶点缓冲就应该满了,我们解锁它准备渲染。
How to make a cone (怎样生成圆锥体)
我们的锥体将由一个三角扇形来构成底,一个三角列来构成周身。像圆柱体一样,我们需要指定它的段数、高和半径。下图(9.2)演示了它的构成方法,图中的锥体为8个段,红线演示了其中一个段的法线。我们将同时使用索引缓冲和顶点缓冲。同时,我们也设置了正确的法线,得到了正确的阴影。
- 3 -
DirectX Tutorials www.andypike.com By Andy Pike Fig 9.2
我们需要指定锥体的段数,上图中的段数为8,当然,段数越多效果越好。我们还需要指定它的高和半径,这样它的顶点、法线、纹理坐标就都出来了。下面的代码片断能告诉你这一切是怎样完成的。 bool CCone::UpdateVertices() { CONE_CUSTOMVERTEX* pVertex; WORD* pIndices; WORD wVertexIndex = 0; int nCurrentSegment; //Lock the vertex buffer if(FAILED(m_pVertexBuffer->Lock(0, 0, (BYTE**)&pVertex, 0))) { LogError(\ return false; } //Lock the index buffer if(FAILED(m_pIndexBuffer->Lock(0, m_dwNumOfIndices, (BYTE**)&pIndices, 0))) { LogError(\ return false; } float rDeltaSegAngle = (2.0f * D3DX_PI / m_nSegments); float rSegmentLength = 1.0f / (float)m_nSegments; float ny0 = (90.0f - (float)D3DXToDegree(atan(m_rHeight / m_rRadius))) / 90.0f; //For each segment, add a triangle to the sides triangle list for(nCurrentSegment = 0; nCurrentSegment < m_nSegments; nCurrentSegment++) { float x0 = m_rRadius * sinf(nCurrentSegment * rDeltaSegAngle); float z0 = m_rRadius * cosf(nCurrentSegment * rDeltaSegAngle); pVertex->x = 0.0f; - 4 -
相关推荐: