第三十二章 投光物和多光源总结

本文介绍了如何使用Assimp库导入3D模型,重点讲解了模型的顶点、纹理和网格数据的解析过程。涉及到Assimp的后期处理选项,如aiProcess_Triangulate和aiProcess_FlipUVs,以及如何将导入的数据转化为OpenGL理解的格式。在处理网格时,详细阐述了顶点结构体、纹理数据和网格类的实现,包括顶点位置、法向量、纹理坐标等,并展示了如何遍历场景节点,构建Mesh对象。最后讨论了纹理加载的优化,避免重复加载相同的纹理,提高了性能。

Assimp
将模型导入到程序中。需要掌握:解析导出的模型文件以及提取所有有用的信息,存储为OpenGL能够理解的格式。
模型的文件格式不同,每一种都有自己的方式来导出模型数据。有专门的库可以直接用,不需要每种文件格式写一个导入器。
使用较多的模型导入库是:Assimp,是Open Asset Import Library(开放的资产导入库)的缩写。
能够导入不同的模型文件格式,也能够导出部分的格式,会将所有的模型数据加载到通用数据结构中。

导入一个模型后,会将整个模型加载到一个场景对象,包含导入的模型、场景中的所有数据。会给场景载入一系列节点,每个节点包含了场景对象中所存储数据
的索引,每个节点都有任意数量的子节点。
1.根节点:Scene对象,包含了所有的场景数据
2.场景的根节点可能包含其他子节点:有一系列指向场景对象中mMeshes数组中存储的网格数据的索引。而根节点Scene下的mMeshes数组存储了真正的Mesh对象,
场景的节点中的mMeshes数组保存的只是场景中的网格数组的索引。
3.Mesh对象包含渲染所需要的所有的相关数据,举例:顶点位置,法向量,纹理坐标,面,物体材质
4.一个网格包含多个面。Face代表物体的渲染图元(三角形,方形,点)。一个面包含了组成图元的顶点的索引。
由于顶点和索引是分开的,可以使用索引缓冲来进行渲染。
5.一个网格也包含一个Material对象,有一些函数可以获取物体的材质属性,比如颜色和纹理贴图,漫反射,镜面光贴图。

做法:将一个物体加载到Scene对象,遍历节点,可以获取对应的Mesh对象,处理每个对象可以获取顶点数据,索引和它的材质属性。最终会把一系列网格数据包含在
一个Model对象中。
补充:每个模型都是由好几个子模型组成的,每个单独的形状叫做一个网格。
一个网格是OpenGL中绘制物体中所需的最小单位,包含顶点数据,索引和材质属性。

使用Assimp的话,需要创建Model和Mesh类来加载并使用刚才介绍的结构存储导入后的模型。要绘制一个模型,不需要将整个模型渲染为一个整体,只需要渲染组成模型的
每个独立的网格就可以了。可以使用camke工具编译Assimp库。

下面主要是针对每个网格的实现:
需要定义一个网格类,网格最少需要一系列顶点,每个顶点包含一个位置向量,一个法向量,一个纹理坐标向量。还应该包括用于索引绘制的索引,纹理形式的
材质数据(漫反色、镜面光贴图)
首先定义一个顶点:
struct Vertex {
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 TexCoords;
};
将所有的向量存储到Vertex结构体中,用其来索引每个顶点属性。还需要将纹理数据整合到Texture结构体中:
struct Vertex {
// position
glm::vec3 Position;
// normal
glm::vec3 Normal;
// texCoords
glm::vec2 TexCoords;
// tangent
glm::vec3 Tangent;
// bitangent
glm::vec3 Bitangent;
//bone indexes which will influence this vertex
int m_BoneIDs[MAX_BONE_INFLUENCE];
//weights from each bone
float m_Weights[MAX_BONE_INFLUENCE];
};

struct Texture {
unsigned int id;
string type;
};–存储纹理的ID和它的类型,举例:漫反射贴图或者是镜面光贴图
上述定义了顶点和纹理的实现,下面定义网格类:
class Mesh {
public:
/* 网格数据 /
vector vertices;
vector indices;
vector textures;
/
函数 /
Mesh(vector vertices, vector indices, vector textures);
void Draw(Shader shader);
private:
/
渲染数据 /
unsigned int VAO, VBO, EBO;
/
函数 */
void setupMesh();
};
上面的Draw函数用来绘制网格,其中是将一个着色器传入的Draw函数,再将着色器传入网格类中可以让我们绘制之前设置uniform
构造器实现如下:
Mesh(vector vertices, vector indices, vector textures)
{
this->vertices = vertices;
this->indices = indices;
this->textures = textures;

setupMesh();

}
上面的setupMesh函数是用来初始化的。需要在渲染网格数据之前配置正确的缓冲,通过顶点属性指针定义顶点着色器的布局:
void setupMesh()
{
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);

glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);  

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), 
             &indices[0], GL_STATIC_DRAW);

// 顶点位置
glEnableVertexAttribArray(0);   
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
// 顶点法线
glEnableVertexAttribArray(1);   
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
// 顶点纹理坐标
glEnableVertexAttribArray(2);   
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));

glBindVertexArray(0);

}
上述代码中用到C++的一个特性,因为Vertex结构体的内存布局是连续的。将结构体作为一个数据数组使用,将会以顺序排列结构体的变量,这会直接转换为
在数组缓冲中所需的字节大小。我们能够直接传入一大列的Vertex结构体的指针作为缓冲的数据,它们将会完美地转换为glBufferData所能用的参数:
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
等同于:
Vertex vertex;
vertex.Position = glm::vec3(0.2f, 0.4f, 0.6f);
vertex.Normal = glm::vec3(0.0f, 1.0f, 0.0f);
vertex.TexCoords = glm::vec2(1.0f, 0.0f);
// = [0.2f, 0.4f, 0.6f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f];
结构体的另外一个很好的用途是它的预处理指令offsetof(s, m),它的第一个参数是一个结构体,第二个参数是这个结构体中变量的名字。这个宏会返回那个变量距结构体头部的字节偏移量(Byte Offset)。

最后是需要对Mesh类定义渲染函数Draw,在渲染网格之前,需要调用glDrawElements函数之前先绑定相应的纹理。
但是,当前不值得网格有多少纹理,纹理类型,无法在着色器中设置纹理单元和采样器。
需要设定一个命名标准用来解决该问题:每个漫反射纹理被命名为texture_diffuseN,每个镜面光反射被命名为texure_specularN。(N代表1-纹理采样器最大数字)
举例:uniform sampler2D texture_dfffuseN;
渲染代码如下:
void Draw(Shader shader)
{
unsigned int diffuseNr = 1;
unsigned int specular

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值