OpenGL加载模型-网格

一、简介
个网格应该至少需要一组顶点,每个顶点包含一个位置向量,一个法线向量,一个纹理坐标向量。一个网格也应该包含一个索引绘制用的索引,以纹理(diffuse/specular map)形式表现的材质数据。

二、网格的使用
为了在OpenGL中定义一个顶点,现在我们设置有最少需求一个网格类:
struct Vertex {
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 TexCoords;
};
我们把每个需要的向量储存到一个叫做Vertex的结构体中,它被用来索引每个顶点属性。另外除了Vertex结构体外,我们也希望组织纹理数据,所以我们定义一个Texture结构体:
struct Texture {
GLuint id; String type;
};
我们储存纹理的id和它的类型,比如diffuse纹理或者specular纹理。
知道了顶点和纹理的实际表达,我们可以开始定义网格类的结构:
class Mesh {
Public:
vector vertices;
vector indices;
vector textures;
Mesh(vector vertices, vector indices, vector texture);
Void Draw(Shader shader);
private:
GLuint VAO, VBO, EBO;
void setupMesh();
}
构造方法里我们初始化网格所有必须数据。在setupMesh函数里初始化缓冲。最后通过Draw函数绘制网格。
在构造函数中也调用setupMesh函数:
Mesh(vector vertices, vector indices, vector textures) {
this->vertices = vertices;
this->indices = indices;
this->textures = textures;
this->setupMesh();
}
void setupMesh() {
glGenVertexArrays(1, &this->VAO);
glGenBuffers(1, &this->VBO);
glGenBuffers(1, &this->EBO);
glBindVertexArray(this->VAO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW); // 设置顶点坐标指针
glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0); // 设置法线指针
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal));
// 设置顶点的纹理坐标
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords));
glBindVertexArray(0);
}

我们需要为Mesh类定义的最后一个函数,是它的Draw函数。在真正渲染前我们希望绑定合适的纹理,然后调用glDrawElements。可因为我们从一开始不知道这个网格有多少纹理以及它们应该是什么类型的,所以这件事变得很困难。所以我们该怎样在着色器中设置纹理单元和采样器呢?
解决这个问题,我们需要假设一个特定的名称惯例:每个diffuse纹理被命名为texture_diffuseN,每个specular纹理应该被命名为texture_specularN。N是一个从1到纹理才抢其允许使用的最大值之间的数。可以说,在一个网格中我们有3个diffuse纹理和2个specular纹理,它们的纹理采样器应该这样被调用:
uniform sampler2D texture_diffuse1;
uniform sampler2D texture_diffuse2;
uniform sampler2D texture_diffuse3;
uniform sampler2D texture_specular1;
uniform sampler2D texture_specular2;
使用这样的惯例,我们能定义我们在着色器中需要的纹理采样器的数量。如果一个网格真的有(这么多)纹理,我们就知道它们的名字应该是什么。这个惯例也使我们能够处理一个网格上的任何数量的纹理,通过定义合适的采样器开发者可以自由使用希望使用的数量(虽然定义少的话就会有点浪费绑定和uniform调用了)。
像这样的问题有很多不同的解决方案,如果你不喜欢这个方案,你可以自己创造一个你自己的方案。 最后的绘制代码:
void Draw(Shader shader) {
GLuint diffuseNr = 1;
GLuint specularNr = 1;
for(GLuint i = 0; i < this->textures.size(); i++) {
glActiveTexture(GL_TEXTURE0 + i); // 在绑定纹理前需要激活适当的纹理单元 // 检索纹理序列号 (N in diffuse_textureN)
stringstream ss;
string number;
string name = this->textures[i].type;
if(name == “texture_diffuse”)
ss << diffuseNr++; // 将GLuin输入到string stream
else if(name == “texture_specular”)
ss << specularNr++; // 将GLuin输入到string stream
number = ss.str();
glUniform1f(glGetUniformLocation(shader.Program, (“material.” + name + number).c_str()), i); glBindTexture(GL_TEXTURE_2D, this->textures[i].id);
}
glActiveTexture(GL_TEXTURE0); // 绘制Mesh
glBindVertexArray(this->VAO);
glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0); glBindVertexArray(0);
}

三、总结
本篇文章我们主要介绍了OpenGL加载模型-网格相关内容,并附以大量的代码。相信大家能够共同进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值