阅读《计算机图形学编程(使用OpenGL和C++)》10 - 画环形

构建一个环

先在xy平面上画圆,以原点为圆心,外径为半径画圆,(可以看作将一个点从原点沿x轴方向平移外径长度的距离,然后绕Z轴旋转一周,得到了圆),然后,将这个圆沿x轴方向再平移内径的距离,如上左图,是环的切面,这样得到了环的第一个组成部分。

将这个圆各个顶点再沿Y轴旋转一周,就得到了环。如上右图,是圆绕Y轴旋转过程中形成的不同环面。

在构建这些顶点时,为每个顶点计算纹理坐标和法向量。还会额外为每个顶点生成与环面表面相切的向量(称为切向量)。

在顶点创建之后,逐个环地遍历所有顶点,并且对于每个顶点生成两个三角形。两个三角形的六个索引表条目的生成方式和之前的球体类似。

我们为剩余的环选择纹理坐标的策略,是将它们排列成使得纹理图像的S轴环绕环面的水平周边的一半,然后再对另一半重复。当我们绕Y轴旋转生成环时,我们指定一个从1开始并增加到指定精度的变量环(再次称为“prec”)。然后我们将S纹理坐标值设置为ring*2.0/prec,使S的取值范围介于0.0和2.0之间,然后每当纹理坐标大于1.0时减去1.0。这种方法的动机是避免纹理图像在水平方向上过度“拉伸”。反之,如果我们确实希望纹理完全围绕环面拉伸,我们只须从纹理坐标计算中删除“*2.0”乘数即可。

这里使用OpenGL索引,我们需要将索引本身加载到VBO中。指定VBO的类型为GL_ELEMENT_ARRAY_BUFFER(这会告诉OpenGL这个VBO包含索引)。

这里将glDrawArrays()调用替换为glDrawElements()调用,它告诉OpenGL利用索引VBO来查找要绘制的顶点。我们还使用glBindBuffer()启用包含索引的VBO,指定哪个VBO包含索引并且是GL_ELEMENT_ARRAY_BUFFER类型。

prec变量具有与球体类似的作用,对顶点数量和索引数量进行类似的计算。

计算了两个切向量(sTangent和tTangent,尽管通常称为“切向量(tangent)”和“副切向量(bitangent)”),它们的叉乘积形成法向量。

代码如下:

环面类 Torus

1 #pragma once
  2 #include <vector>
  3 #include <glm\glm.hpp>
  4 #include <cmath>
  5 class Torus
  6 {
  7 private:
  8     int numVertices;
  9     int numIndices;
 10     int prec;
 11     float inner;
 12     float outer;
 13     std::vector<int> indices;
 14     std::vector<glm::vec3> vertices;
 15     std::vector<glm::vec2> texCoords;
 16     std::vector<glm::vec3> normals;
 17     std::vector<glm::vec3> sTangents;
 18     std::vector<glm::vec3> tTangents;
 19     void init();
 20     float toRadians(float degrees);
 21 
 22 public:
 23     Torus();
 24     Torus(float innerRadius, float outerRadius, int prec);
 25     ~Torus();
 26     int getNumVertices();
 27     int getNumIndices();
 28     std::vector<int> getIndices();
 29     std::vector<glm::vec3> getVertices();
 30     std::vector<glm::vec2> getTexCoords();
 31     std::vector<glm::vec3> getNormals();
 32     std::vector<glm::vec3> getStangents();
 33     std::vector<glm::vec3> getTtangents();
 34 };
 35 
 36 #include "Torus.h"
 37 #include <iostream>
 38 using namespace std;
 39 #include "glm/gtc/matrix_transform.hpp"
 40 void Torus::init()
 41 {
 42     numVertices = (prec + 1) * (prec + 1);
 43     numIndices = prec * prec * 6;
 44     for (int i = 0; i < numVertices; i++)
 45     {
 46         vertices.push_back(glm::vec3());
 47         texCoords.push_back(glm::vec2());
 48         normals.push_back(glm::vec3());
 49         sTangents.push_back(glm::vec3());
 50         tTangents.push_back(glm::vec3());
 51     }
 52     for (int i = 0; i < numIndices; i++)
 53     {
 54         indices.push_back(0);
 55     }
 56 
 57     // 计算第一个环
 58     for (int i = 0; i < prec + 1; i++)
 59     {
 60         float amt = toRadians(i * 360.0f / prec);
 61         // 绕原点旋转点,形成环,然后将它们向外移动
 62         glm::mat4 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 0.0f, 1.0f));
 63         glm::vec3 initPos(rMat * glm::vec4(outer, 0.0f, 0.0f, 1.0f));
 64         vertices[i] = glm::vec3(initPos + glm::vec3(inner, 0.0f, 0.0f));
 65 
 66         // 为环上的每个顶点计算纹理坐标
 67         texCoords[i] = glm::vec2(0.0f, ((float)i / (float)prec));
 68 
 69         // 计算切向量和法向量,第一个切向量是绕Z轴旋转的Y轴
 70         rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 0.0f, 1.0f));
 71         tTangents[i] = glm::vec3(rMat * glm::vec4(0.0f, -1.0f, 0.0f, 1.0f));
 72         sTangents[i] = glm::vec3(glm::vec3(0.0f, 0.0f, -1.0f));
 73         // 第二个切向量是-Z轴
 74         normals[i] = glm::cross(tTangents[i], sTangents[i]);
 75         // 它们的叉乘积就是法向量
 76 
 77         // 绕Y轴旋转最初的那个环,形成其他的环
 78         for (int ring = 1; ring < prec + 1; ring++)
 79         {
 80             for (int vert = 0; vert < prec + 1; vert++)
 81             {
 82                 // 绕Y轴旋转最初那个环的顶点坐标
 83                 float amt = (float)(toRadians(ring * 360.0f / prec));
 84                 glm::mat4 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
 85                 vertices[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(vertices[i], 1.0f));
 86 
 87                 // 计算新环顶点的纹理坐标
 88                 texCoords[ring * (prec + 1) + vert] = 
 89                     glm::vec2((float)ring/** 2.0f*//(float)prec, texCoords[vert].t);
 90                 if (texCoords[ring * (prec + 1) + i].s > 1.0)
 91                 {
 92                     texCoords[ring * (prec + 1) + i].s -= 1.0f;
 93                 }
 94 
 95                 // 绕Y轴旋转切向量和副切向量
 96                 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
 97                 sTangents[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(sTangents[i], 1.0f));
 98                 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
 99                 tTangents[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(tTangents[i], 1.0f));
100 
101                 // 绕Y轴旋转法向量
102                 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
103                 normals[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(normals[i], 1.0f));
104             }
105         }
106     }
107     // 按照逐个顶点的两个三角形,计算三角形索引
108     for (int ring = 0; ring < prec; ring++)
109     {
110         for (int vert = 0; vert < prec; vert++)
111         {
112             indices[((ring * prec + vert) * 2) * 3 + 0] = ring * (prec + 1) + vert;
113             indices[((ring * prec + vert) * 2) * 3 + 1] = (ring + 1) * (prec + 1) + vert;
114             indices[((ring * prec + vert) * 2) * 3 + 2] = ring * (prec + 1) + vert + 1;
115             indices[((ring * prec + vert) * 2 + 1) * 3 + 0] = ring * (prec + 1) + vert + 1;
116             indices[((ring * prec + vert) * 2 + 1) * 3 + 1] = (ring + 1) * (prec + 1) + vert;
117             indices[((ring * prec + vert) * 2 + 1) * 3 + 2] = (ring + 1) * (prec + 1) + vert + 1;
118         }
119     }
120 }
121 
122 float Torus::toRadians(float degrees)
123 {
124     return (degrees * 2.0f * 3.14159f) / 360.0f;
125 }
126 
127 Torus::Torus()
128 {
129     prec = 48;
130     inner = 0.5f;
131     outer = 0.2f;
132     init();
133 }
134 
135 Torus::Torus(float innerRadius, float outerRadius, int precIn)
136 {
137     prec = precIn;
138     inner = innerRadius;
139     outer = outerRadius;
140     init();
141 }
142 
143 Torus::~Torus()
144 {
145 }
146 
147 int Torus::getNumVertices()
148 {
149     return numVertices;
150 }
151 
152 int Torus::getNumIndices()
153 {
154     return numIndices;
155 }
156 
157 std::vector<int> Torus::getIndices()
158 {
159     return indices;
160 }
161 
162 std::vector<glm::vec3> Torus::getVertices()
163 {
164     return vertices;
165 }
166 
167 std::vector<glm::vec2> Torus::getTexCoords()
168 {
169     return texCoords;
170 }
171 
172 std::vector<glm::vec3> Torus::getNormals()
173 {
174     return normals;
175 }
176 
177 std::vector<glm::vec3> Torus::getStangents()
178 {
179     return sTangents;
180 }
181 
182 std::vector<glm::vec3> Torus::getTtangents()
183 {
184     return tTangents;
185 }

main.cpp

 1 ...
 2 #include "Torus.h"
 3 ...
 4 Torus myTorus(0.5f, 0.2f, 48);
 5 ...
 6 void setupVertices(void)
 7 {    
 8     std::vector<int> ind = myTorus.getIndices();         //索引
 9     std::vector<glm::vec3> vert = myTorus.getVertices(); //顶点
10     std::vector<glm::vec2> tex = myTorus.getTexCoords(); //纹理
11     std::vector<glm::vec3> norm = myTorus.getNormals();  //法向量
12 
13     std::vector<float> pvalues;   //顶点位置
14     vector<float> tvalues;        //纹理坐标
15     vector<float> nvalues;        //法向量
16 
17     int numVertices = myTorus.getNumVertices();
18     for (int i = 0; i < numVertices; i++)
19     {
20         pvalues.push_back((vert[i]).x);
21         pvalues.push_back((vert[i]).y);
22         pvalues.push_back((vert[i]).z);
23 
24         tvalues.push_back((tex[i]).s);
25         tvalues.push_back((tex[i]).t);
26 
27         nvalues.push_back((norm[i]).x);
28         nvalues.push_back((norm[i]).y);
29         nvalues.push_back((norm[i]).z);
30     }
31 
32     glGenVertexArrays(1, vao); // 创建一个vao,并返回它的整数型ID存进数组vao中
33     glBindVertexArray(vao[0]); // 激活vao
34     glGenBuffers(numVBOs, vbo);// 创建4个vbo,并返回它们的整数型ID存进数组vbo中
35 
36     // 把顶点放入缓冲区 #0
37     glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 
38     glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW); 
39     // 把纹理坐标放入缓冲区 #1
40     glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); 
41     glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW); 
42     // 把法向量放入缓冲区 #2
43     glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
44     glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
45     // 把索引放入缓冲区 #3
46     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
47     glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * 4, &ind[0], GL_STATIC_DRAW);
48 }
49 ...
50 void display(GLFWwindow* window, double currentTime)
51 {
52     ...
53     
54     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
55     glDrawElements(GL_TRIANGLES, myTorus.getNumIndices(), GL_UNSIGNED_INT, 0);
56 }

效果如图所示:

猜你喜欢

转载自blog.csdn.net/ttod/article/details/135346477