Directx11学习笔记【十五】 基本几何体的绘制

本文由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 }
View Code

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

原文地址:https://www.cnblogs.com/zhangbaochong/p/5573970.html