assimp库
这是一个可以可以把obj文件传入我们系统的库。
mesh 网格
我们新建一个mesh类用来表示我们之前的那些模型数据,网格是那么需要一系列的顶点vertex的,而顶点需要有哪些数据呢,位置、向量、纹理坐标。
把这些信息归到顶点类中。
struct Vertex {
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 TexCoords;
};
然后还需要用于索引绘制的索引以及纹理材质(漫反射贴图,镜面贴图),纹理类
struct Texture {
unsigned int id;//确定是哪副材质图
std::string type;//确定是用于漫反射还是镜面反射
std::string path;//材质路径
};
class Mesh
{
public:
Mesh(float vertices[]);
//网格数据
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
std::vector<Texture> textures;
//函数
Mesh(std::vector<Vertex> vertices,std::vector<unsigned int> indices,std::vector<Texture> textures);
void Draw(Shader* shader);
private:
//渲染数据
unsigned int VAO, VBO, EBO;
void setupMesh();
};
在class里面有一个setup函数,是用来初始化缓冲的,也就是把之前那些VAO初始化和设置顶点数据的工作放到这个里面。
然后用draw来绘制网格,也就是之前在render loop中最后draw的那块。draw()是传入了一个shader着色器。
void Mesh::setupMesh(){
//VAO 生成绑定
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
//VBO 生成绑定填充数据
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*vertices.size(), &vertices[0], GL_STATIC_DRAW);
//EBO 生成绑定填充数据
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*indices.size(), &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);
};
有两个和之前的不同:
一个是在glbufferdata中,第二个参数,顶点数据的大小,这里是vertices.size() * sizeof(Vertex),顶点个数*每个顶点的字节大小(每个顶点32字节=8个float*4byte)
第二个是glVertexAttribPointer的最后一个参数,这里面用了个offsetof来定义偏移量,它的第一个参数是一个结构体,第二个参数是这个结构体中变量的名字。这个宏会返回那个变量距结构体头部的字节偏移量(Byte Offset)。这样我们就不用自己去算偏移量了。直接给名字就可以
model模型
在前面已经把mesh网格做完了,现在要接着去做model,一个模型里面会有多个mesh,更大一点的说,在一个场景中有多个模型,每个模型又有多个mesh。
#pragma once
#include"Mesh.h"
#include<string>
#include<vector>
class Model
{
public:
//函数
Model(std::string path);
void Draw(Shader* shader);
private:
//模型数据
std::vector<Mesh> meshes;
std::string directory;
//函数
void loadModel(std::string path);
void processNode(aiNode* node,const aiScene*scene);
Mesh processMesh(aiMesh* mesh, const aiScene* scene);
std::vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type,
std::string typeName);
};
首先看一下.h,这里有个mesh的数组,会通过loadmodel来加载文件,draw就很简单,遍历网格然后画出来
void Model::Draw(Shader* shader)
{
for (unsigned int i = 0; i < meshes.size(); i++) {
meshes[i].Draw(shader);
}
}
导入3d模型导opengl
首先我们要知道一个在assimp中的数据结构scene
有了这个场景我们就可以去访问里面的模型了。通过这样的声明就可以去获取场景对象了。
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
void Model::loadModel(std::string path)
{
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cout << "Assimp error" << std::endl;
return;
}
directory = path.substr(0, path.find_last_of('\\'));
//std::cout <<"success!"<< directory << std::endl;
processNode(scene->mRootNode, scene);
}
directory = path.substr(0, path.find_last_of('\\'));这条函数很有趣,是用来定位文件路径的,直到组后一个\,然后把我们需要的那些模型文件放到exe同名文件下。可以试着输出一下directory。
得到文件目录了之后就是要去遍历场景中的所有节点了,就是scene的那个数据机构中的所有节点了。
这里就和链表中的深度优先算法一样,不断处理节点的所有子节点,然后递归后返回。
void Model::processNode(aiNode* node, const aiScene* scene)
{
//std::cout << node->mName.data << std::endl;
//处理节点的所有mesh
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh*curMesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(curMesh, scene));
}
//然后处理节点
for (unsigned int i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene);
}
}
这里理清一下思路,对于processNode传入两个参数,当前的node和整个场景。
先处理当前的node,先获得当前节点的mesh然后用processMesh来把当前的网格参数存到meshes数组中。然后在对他的子节点做同样处理。
然后就是processMesh函数
他的功能是把传进来的网格参数变成我们需要的网格参数,就是在mesh中的三个(顶点属性、索引、纹理)