- 来源:《Computer Graphics Programming in OpenGL Using C++ 》by V Scott
Gordon John L Clevenger - 内容:程序6.1 Sphere,书P141页,PDF160/403
生成:
笔记
- OpenGL 创建一个圆环 torus
,生成圆环的思路是,原点右侧有一个点,点绕着原点绕着z轴旋转一周,在每个点处有点绕着该点绕着y轴旋转一周。
- 纹理上S轴的长度对应的是圆环一半的周长,另一半周长重复纹理在S轴上的内容。将纹理在S轴坐标设置为ring*2.0/prec,纹理在S轴上的范围是0-2,其中s轴坐标大于1时会执行S-1,避免纹理被拉伸。当然,需要纹理被拉伸时,不需要减去1.
- 在球体和圆环模型中,都需要一个整数索引的数列指向顶点列。在球体中,我们列出一系列目录创建一整套顶点,把他们加载进VBO中,我们也可以生成圆环用同样的方法将顶点,法向量等信息存储进缓存器中。
- 这次采用另一种方法 OpenGL’s indexing
需要加载indice 到 VBO中,需要生成另一个VBO来存储indice。因为每一个索引值都是一个整数,所以将索引数列复制进c++整数的 vector 中,用glBufferData()
加载vector 到 生成的VBO中,注意 VBO类型是GL_ELEMENT_ARRAY_BUFFER
(告诉OpenGL,这个VBO是用来存储indice),这段代码需要写在setupVertices()
函数中:
std::vector ind = myTorus.getIndices(); // torus index accessor returns indices as an int vector
. . .
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]); // vbo #3 is the additional added vbo
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size()*4, &ind[0], GL_STATIC_DRAW);
- 在 display 函数中,用
glDrawElements()
,取代glDrawArrays()
,glDrawElements()
函数是让OpenGL利用VBO索引来寻找要画图的顶点。用glBindBuffer()
和GL_ELEMENT_ARRAY_BUFFER
关键字来确定VBO包含indice:
numTorusIndices = myTorus.getNumIndices();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glDrawElements(GL_TRIANGLES, numTorusIndices, GL_UNSIGNED_INT, 0);
- 用于球的着色器shader用在圆环上是没有更改的,
半径 Inner和outer的定义如下
- 代码中,初始化里规定了视觉视角和模型的初始位置
cameraX = 0.0f; cameraY = 1.0f; cameraZ = 3.0f;
pyrLocX = 0.0f; pyrLocY = 0.5f; pyrLocZ = 0.5f; // shift down Y to reveal perspective
cameraX, cameraY, cameraZ
的坐标系与上同,但是变化方向与上图相反。就是,当cameraX增加,物体向左移动
完整代码:
一共有 个文件,分别是:
220301 6.2 Torus.cpp
6.2 Torus.cpp
6.2 Torus.h
5.2 Utils.cpp
5.2 Utils.h
5.2 vertShader.glsl
5.2 fragShader.glsl
brick1.jpg
其中 5.2 Utils.cpp,5.2 Utils.h,5.2 vertShader.glsl,5.2 fragShader.glsl,brick1.jpg 这5个文件在C++/OpenGL 入门(16):生成球体并贴纹理图中有完整内容,以下只展示前三个文件内容。
220301 6.2 Torus.cpp
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include "glm\glm.hpp"
#include "glm\gtc\type_ptr.hpp"
#include "glm\gtc\matrix_transform.hpp"
#include "Utils\6.2 Torus.h"
#include "Utils\5.2 Utils.h"
using namespace std;
#define numVAOs 1
#define numVBOs 4
float cameraX, cameraY, cameraZ;
float pyrLocX, pyrLocY, pyrLocZ;
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
GLuint brickTexture; // 纹理图片ID
// allocate variables used in display() function,
// so that they won’t need to be allocated during rendering
GLuint mvLoc, projLoc;
int width, height;
float aspect;
glm::mat4 pMat, vMat, mMat, mvMat;
Torus myTorus(0.5f, 0.2f, 48); // 声明一个圆环的class
void setupVertices(void) {
std::vector<int> ind = myTorus.getIndices();
std::vector<glm::vec3> vert = myTorus.getVertices();
std::vector<glm::vec2> tex = myTorus.getTexCoords();
std::vector<glm::vec3> norm = myTorus.getNormals();
std::vector<float> pvalues;
std::vector<float> tvalues;
std::vector<float> nvalues;
int numVertices = myTorus.getNumVertices();
for (int i = 0; i < numVertices; i++) {
pvalues.push_back(vert[i].x);
pvalues.push_back(vert[i].y);
pvalues.push_back(vert[i].z);
tvalues.push_back(tex[i].s);
tvalues.push_back(tex[i].t);
nvalues.push_back(norm[i].x);
nvalues.push_back(norm[i].y);
nvalues.push_back(norm[i].z);
}
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
glGenBuffers(numVBOs, vbo); // generate VBOs as before, plus one for indices
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); // vertex positions
glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); // texture coordinates
glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]); // normal vectors
glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]); // indices
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * 4, &ind[0], GL_STATIC_DRAW);
}
void init(GLFWwindow* window) {
renderingProgram = createShaderProgram("add/5.2 vertShader.glsl", "add/5.2 fragShader.glsl");
cameraX = 0.0f; cameraY = 1.0f; cameraZ = 3.0f;
pyrLocX = 0.0f; pyrLocY = 0.5f; pyrLocZ = 0.5f; // shift down Y to reveal perspective
setupVertices();
brickTexture = loadTexture("add/brick1.jpg"); // 加载纹理的图片
}
void display(GLFWwindow* window, double currentTime) {
glClear(GL_DEPTH_BUFFER_BIT);
glUseProgram(renderingProgram);
// get the uniform variables for the MV and projection matrices
mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
// build perspective matrix
glfwGetFramebufferSize(window, &width, &height);
aspect = (float)width / (float)height;
pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f); // 1.0472 radians = 60 degrees
// build view matrix, model matrix, and model-view matrix
vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
// vbo[0]
mMat = glm::translate(glm::mat4(1.0f), glm::vec3(pyrLocX, pyrLocY, pyrLocZ));
mvMat = vMat * mMat;
// copy perspective and MV matrices to corresponding uniform variables
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
// associate VBO with the corresponding vertex attribute in the vertex shader
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
// 纹理
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, brickTexture);
// adjust OpenGL settings and draw model
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glFrontFace(GL_CCW);// 锥体的三角形是逆时针的面认为是正方向
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glDrawElements(GL_TRIANGLES, myTorus.getNumIndices(), GL_UNSIGNED_INT, 0);
}
int main(void) { // main() is unchanged from before
if (!glfwInit()) { exit(EXIT_FAILURE); }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(600, 600, "Chapter 6 - program 2", NULL, NULL);
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1);
init(window);
while (!glfwWindowShouldClose(window)) {
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
6.2 Torus.cpp
#include <cmath>
#include <vector>
#include <iostream>
#include "glm\glm.hpp"
#include "glm\gtc\type_ptr.hpp"
#include "glm\gtc\matrix_transform.hpp"
#include "6.2 Torus.h"
using namespace std;
Torus::Torus() {
prec = 48;
inner = 0.5f;
outer = 0.2f;
init();
}
Torus::Torus(float innerRadius, float outerRadius, int precIn) {
prec = precIn;
inner = innerRadius;
outer = outerRadius;
init();
}
float Torus::toRadians(float degrees) { return (degrees * 2.0f * 3.14159f) / 360.0f; }
void Torus::init() {
numVertices = (prec + 1) * (prec + 1);
numIndices = prec * prec * 6;
for (int i = 0; i < numVertices; i++) { vertices.push_back(glm::vec3()); }
for (int i = 0; i < numVertices; i++) { texCoords.push_back(glm::vec2()); }
for (int i = 0; i < numVertices; i++) { normals.push_back(glm::vec3()); }
for (int i = 0; i < numVertices; i++) { sTangents.push_back(glm::vec3()); }
for (int i = 0; i < numVertices; i++) { tTangents.push_back(glm::vec3()); }
for (int i = 0; i < numIndices; i++) { indices.push_back(0); }
// calculate first ring
for (int i = 0; i < prec + 1; i++) {
float amt = toRadians(i*360.0f / prec);
// build the ring by rotating points around the origin, then moving them outward
glm::mat4 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 0.0f, 1.0f));
glm::vec3 initPos(rMat * glm::vec4(outer, 0.0f, 0.0f, 1.0f));
vertices[i] = glm::vec3(initPos + glm::vec3(inner, 0.0f, 0.0f));
// compute texture coordinates for each vertex on the ring
texCoords[i] = glm::vec2(0.0f, ((float)i / (float)prec));
// compute tangents and normals -- first tangent is Y-axis rotated around Z
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 0.0f, 1.0f));
tTangents[i] = glm::vec3(rMat * glm::vec4(0.0f, -1.0f, 0.0f, 1.0f));
sTangents[i] = glm::vec3(glm::vec3(0.0f, 0.0f, -1.0f)); // second tangent is -Z.
normals[i] = glm::cross(tTangents[i], sTangents[i]); // their X-product is the normal.
}
// rotate the first ring about Y to get the other rings
for (int ring = 1; ring < prec + 1; ring++) {
for (int vert = 0; vert < prec + 1; vert++) {
// rotate the vertex positions of the original ring around the Y axis
float amt = (float)(toRadians(ring * 360.0f / prec));
glm::mat4 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
vertices[ring*(prec + 1) + vert] = glm::vec3(rMat * glm::vec4(vertices[vert], 1.0f));
// compute the texture coordinates for the vertices in the new rings
texCoords[ring*(prec + 1) + vert] = glm::vec2((float)ring*2.0f / (float)prec, texCoords[vert].t);
if (texCoords[ring*(prec + 1) + vert].s > 1.0) texCoords[ring*(prec + 1) + vert].s -= 1.0f;
// rotate the tangent and bitangent vectors around the Y axis
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
sTangents[ring*(prec + 1) + vert] = glm::vec3(rMat * glm::vec4(sTangents[vert], 1.0f));
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
tTangents[ring*(prec + 1) + vert] = glm::vec3(rMat * glm::vec4(tTangents[vert], 1.0f));
// rotate the normal vector around the Y axis
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
normals[ring*(prec + 1) + vert] = glm::vec3(rMat * glm::vec4(normals[vert], 1.0f));
}
}
// calculate triangle indices corresponding to the two triangles built per vertex
for (int ring = 0; ring < prec; ring++) {
for (int vert = 0; vert < prec; vert++) {
indices[((ring*prec + vert) * 2) * 3 + 0] = ring * (prec + 1) + vert;
indices[((ring*prec + vert) * 2) * 3 + 1] = (ring + 1)*(prec + 1) + vert;
indices[((ring*prec + vert) * 2) * 3 + 2] = ring * (prec + 1) + vert + 1;
indices[((ring*prec + vert) * 2 + 1) * 3 + 0] = ring * (prec + 1) + vert + 1;
indices[((ring*prec + vert) * 2 + 1) * 3 + 1] = (ring + 1)*(prec + 1) + vert;
indices[((ring*prec + vert) * 2 + 1) * 3 + 2] = (ring + 1)*(prec + 1) + vert + 1;
}
}
}
// accessors for the torus indices and vertices
int Torus::getNumVertices() { return numVertices; }
int Torus::getNumIndices() { return numIndices; }
std::vector<int> Torus::getIndices() { return indices; }
std::vector<glm::vec3> Torus::getVertices() { return vertices; }
std::vector<glm::vec2> Torus::getTexCoords() { return texCoords; }
std::vector<glm::vec3> Torus::getNormals() { return normals; }
std::vector<glm::vec3> Torus::getStangents() { return sTangents; }
std::vector<glm::vec3> Torus::getTtangents() { return tTangents; }
6.2 Torus.h
#pragma once
#include <cmath>
#include <vector>
#include <glm\glm.hpp>
class Torus
{
private:
int numVertices;
int numIndices;
int prec;
float inner;
float outer;
std::vector<int> indices;
std::vector<glm::vec3> vertices;
std::vector<glm::vec2> texCoords;
std::vector<glm::vec3> normals;
std::vector<glm::vec3> sTangents;
std::vector<glm::vec3> tTangents;
void init();
float toRadians(float degrees);
public:
Torus();
Torus(float innerRadius, float outerRadius, int prec);
int getNumVertices();
int getNumIndices();
std::vector<int> getIndices();
std::vector<glm::vec3> getVertices();
std::vector<glm::vec2> getTexCoords();
std::vector<glm::vec3> getNormals();
std::vector<glm::vec3> getStangents();
std::vector<glm::vec3> getTtangents();
};