C++/OpenGL 入门(17):生成圆环并贴纹理图

  1. 来源:《Computer Graphics Programming in OpenGL Using C++ 》by V Scott
    Gordon John L Clevenger
  2. 内容:程序6.1 Sphere,书P141页,PDF160/403

生成:

在这里插入图片描述

笔记

  1. OpenGL 创建一个圆环 torus
    ,生成圆环的思路是,原点右侧有一个点,点绕着原点绕着z轴旋转一周,在每个点处有点绕着该点绕着y轴旋转一周。

在这里插入图片描述

  1. 纹理上S轴的长度对应的是圆环一半的周长,另一半周长重复纹理在S轴上的内容。将纹理在S轴坐标设置为ring*2.0/prec,纹理在S轴上的范围是0-2,其中s轴坐标大于1时会执行S-1,避免纹理被拉伸。当然,需要纹理被拉伸时,不需要减去1.
  2. 在球体和圆环模型中,都需要一个整数索引的数列指向顶点列。在球体中,我们列出一系列目录创建一整套顶点,把他们加载进VBO中,我们也可以生成圆环用同样的方法将顶点,法向量等信息存储进缓存器中。
  3. 这次采用另一种方法 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);

  1. 在 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);

  1. 用于球的着色器shader用在圆环上是没有更改的,
    半径 Inner和outer的定义如下

在这里插入图片描述

  1. 代码中,初始化里规定了视觉视角和模型的初始位置
	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();
};
  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值