QT+OPenGL九之模型解码

因为本案例只是获取了模型的顶点,法线,uv,纹理。因为我们还没研究光照暂时先拿到这些去研究Phong Lighting Model着色已经足够,到研究漫反射和镜面反射贴图再来完善。

大型模型都是有很多个mesh对象构成如learnOpengl中说到一栋别墅,会有游泳池,石柱等这些小的模型组装出来的,而这些小的模型就叫做mesh对象,。因此先来写mesh对象
MyMesh .h

#pragma once
#include"vector"
#include "MyShader.h"
#include"Camera.h"
using namespace std;
struct Vertex
{
    QVector3D Position;
    QVector3D Normal;
    QVector2D TexCoords;
};
struct Texture {
    GLint id;
    QString type;
    QString path;
};
class MyMesh : protected QOpenGLFunctions_4_3_Core
{
public:
    vector<Vertex> vertices;//顶点属性向量
    vector<GLuint>indices;//索引向量
    vector<Texture>textures;//纹理属性向量
//一个网格对象需要顶点,索引和纹理,这是一般建模软件的结构建模网格的数据结构
    MyMesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> texture);
    void init(QOpenGLShaderProgram* shaderProgram);
    void draw(Camera camera);
    void setLocation(float x,float y,float z);
private:
    float locationX=0.0f, locationY=0.0f, locationZ=-7.0f;
    QMatrix4x4 mv;
    QOpenGLShaderProgram* shaderProgram;
    QOpenGLVertexArrayObject vao;
    QOpenGLBuffer vbo,ebo;
    GLuint vPosition,normal,uv, mv_loc;
};

MyMesh.cpp

#include "stdafx.h"
#include "MyMesh.h"
MyMesh::MyMesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures): ebo(QOpenGLBuffer::IndexBuffer)
{
    this->vertices = vertices;
    this->indices = indices;
    this->textures = textures;
}
void MyMesh::init(QOpenGLShaderProgram* shaderProgram)
{
    initializeOpenGLFunctions();
    this->shaderProgram = shaderProgram;
    shaderProgram->bind();
    vao.create();
    vbo.create();
    ebo.create();
    vao.bind();
    vbo.bind();
    vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    vbo.allocate(&vertices[0], this->vertices.size() * sizeof(Vertex));
    // 设置顶点坐标指针
    vPosition = shaderProgram->attributeLocation("vPosition");
    shaderProgram->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, sizeof(Vertex));
    glEnableVertexAttribArray(vPosition);
    // 设置法线指针
    normal= shaderProgram->attributeLocation("normal");
    shaderProgram->setAttributeBuffer("normal", GL_FLOAT, offsetof(Vertex, Normal), 3, sizeof(Vertex));//shader变量索引,参数类型,偏移量,元素大小,步长
    glEnableVertexAttribArray(normal);
    // 设置顶点的纹理坐标
    uv = shaderProgram->attributeLocation("uv");
    shaderProgram->setAttributeBuffer(uv, GL_FLOAT, offsetof(Vertex, TexCoords), 2, sizeof(Vertex));
    glEnableVertexAttribArray(uv);
    ebo.bind();
    ebo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    ebo.allocate(&this->indices[0], this->indices.size() * sizeof(GLuint));
    vao.release();
    ebo.release();
    vbo.release();
    shaderProgram->release();
    vertices.clear();
}
void MyMesh::draw(Camera camera) {
    shaderProgram->bind();
    mv_loc = shaderProgram->uniformLocation("mv_matrix");
    //构建视图矩阵
    QMatrix4x4 m;
    m.translate(locationX, locationY, locationZ);
    QMatrix4x4 v;
    v.lookAt(QVector3D(camera.location.x, camera.location.y, camera.location.z),
        QVector3D(camera.viewPoint.x, camera.viewPoint.y, camera.viewPoint.z),
        QVector3D(camera.worldY.x, camera.worldY.y, camera.worldY.z));
    mv = v * m;
    shaderProgram->setUniformValue(mv_loc, mv);
    vao.bind();
    glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
    vao.release();
    shaderProgram->release();
}
void MyMesh::setLocation(float x, float y, float z) {
    locationX = x;
    locationY = y;
    locationZ = z;
}

之所以选择结构体,是因为c++的结构体类型中的变量是连续的,因此很容易知道他的大小如:

struct Vertex
{
QVector3D Position;
QVector3D Normal;
QVector2D TexCoords;
};
他的大小就是sizeof(Vertex);
offsetof(Vertex, TexCoords)函数能自动运算出变量间的偏移量。第一个参数是结构体名,第二个参数是哪一个变量名,返回要便宜的量。

接着就可以获取模型数据了。

Model.h

#include "assimp/scene.h"
#include "assimp/postprocess.h"
#include "MyMesh.h"
#include"iostream"
using namespace std;
class Model
{
public:
    /*  成员函数   */
    Model();
    ~Model();
    void draw(Camera camera);
    void init(string path, QOpenGLShaderProgram* shaderProgram);
    void setModelLocation(QVector3D location);
private:
    ///*  模型数据  */
    vector<MyMesh*> meshes;
    QString directory;
    QOpenGLShaderProgram* shaderProgram;
    ///*  私有成员函数   */
    void loadModel(string path);
    void processNode(aiNode* node, const aiScene* scene);
    MyMesh* processMesh(aiMesh* mesh, const aiScene* scene);
   vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, QString typeName);
};

Model.cpp

#include "stdafx.h"
#include "Model.h"
Model::Model(){
   
}
Model::~Model() {
    meshes.clear();
}
void Model::init(string path, QOpenGLShaderProgram* shaderProgram) {
    this->shaderProgram = shaderProgram;
    loadModel(path);
}
void Model::setModelLocation(QVector3D location) {
    for (GLuint i = 0; i < this->meshes.size(); i++)
    {
        this->meshes[i]->setLocation(location.x(), location.y(), location.z());
    }
}
void Model::draw(Camera camera)
{
    for (GLuint i = 0; i < this->meshes.size(); i++)
    {
        this->meshes[i]->draw(camera);
    }
}
void Model::loadModel(string path)
{
    Assimp::Importer import;
    const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);

    if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
    {
        qDebug() << "ERROR::ASSIMP::" << import.GetErrorString() << endl;
        return;
    }
    this->directory =QString::fromStdString(path.substr(0, path.find_last_of('/')));
    //qDebug() <<"directory:"<< directory;//全路径
    this->processNode(scene->mRootNode, scene);
}
void Model::processNode(aiNode* node, const aiScene* scene)
{
    // 添加当前节点中的所有Mesh
    //qDebug() << "mNumMeshes:"<<node->mNumMeshes;
    for (GLuint i = 0; i < node->mNumMeshes; i++)
    {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        this->meshes.push_back(this->processMesh(mesh, scene));
    }
    // 递归处理该节点的子孙节点
    //qDebug() << "mNumChildren:" << node->mNumChildren;
    for (GLuint i = 0; i < node->mNumChildren; i++)
    {
        this->processNode(node->mChildren[i], scene);
    }
}
MyMesh* Model::processMesh(aiMesh* mesh, const aiScene* scene)
{
    vector<Vertex> vertices;
    vector<GLuint> indices;
    vector<Texture> textures;
    //qDebug() << "mNumVertices:" << mesh->mNumVertices;
    for (GLuint i = 0; i < mesh->mNumVertices; i++)
    {
        Vertex vertex;
        // 处理顶点坐标、法线和纹理坐标
        vertex.Position.setX(mesh->mVertices[i].x);
        vertex.Position.setY(mesh->mVertices[i].y);
        vertex.Position.setZ(mesh->mVertices[i].z);
        vertex.Normal.setX(mesh->mNormals[i].x);
        vertex.Normal.setY(mesh->mNormals[i].y);
        vertex.Normal.setZ(mesh->mNormals[i].z);
        if (mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates?
        {
            vertex.TexCoords.setX(mesh->mTextureCoords[0][i].x);
            vertex.TexCoords.setY(mesh->mTextureCoords[0][i].y);
        }
        else { vertex.TexCoords.setX(0.0);
        vertex.TexCoords.setY(0.0);}
        vertices.push_back(vertex);
    }

    // 处理顶点索引
    //qDebug() << "mNumFaces:" << mesh->mNumFaces;
    for (GLuint i = 0; i < mesh->mNumFaces; i++)
    {
        aiFace face = mesh->mFaces[i];
        
        for (GLuint j = 0; j < face.mNumIndices; j++)
        {
            indices.push_back(face.mIndices[j]);
        }
    }

     //处理材质
    if(mesh->mMaterialIndex >= 0)
    {
        aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
        vector<Texture> diffuseMats = this->loadMaterialTextures(material,
            aiTextureType_DIFFUSE, "texture_diffuse");
        textures.insert(textures.end(), diffuseMats.begin(), diffuseMats.end());//把区间[start,end]插入到迭代器的指定位置
        vector<Texture> specularMats = this->loadMaterialTextures(material,
            aiTextureType_SPECULAR, "texture_specular");
        textures.insert(textures.end(), specularMats.begin(), specularMats.end());
    }

    MyMesh* myMesh = new  MyMesh( vertices, indices, textures);
    myMesh->init(shaderProgram);
    return myMesh;
}

vector<Texture> Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, QString typeName)
{
    vector<Texture> textures;
    if (mat->GetTextureCount(type) == 0) {//没有纹理我们也创建一个空的,避免纹理不更新,被上次的覆盖。
        cout<< typeName <<":no find texture"<<endl;
        Texture texture;
        texture.id = 0;
        texture.type = typeName;
        texture.fileName = "no";
        textures.push_back(texture);
        return textures;
    }
    for (GLuint i = 0; i < mat->GetTextureCount(type); i++)
    {
        aiString folderPath;
        mat->GetTexture(type, i, &folderPath);
        qDebug() << "folderPath:" << folderPath.C_Str();
        GLboolean skip = false;
        for (GLuint j = 0; j < textures.size(); j++)
        {
            if (textures[j].path == folderPath.C_Str())
            {
                textures.push_back(textures[j]);
                skip = true;
                break;
            }
        }
        if (!skip)
        {   // 如果纹理没有被加载过,加载之
            Texture texture;
            QString fileName = this->directory;
            fileName += '/';
            fileName += folderPath.C_Str();
            qDebug() << fileName;
            QOpenGLTexture loadTexture(QImage(fileName).mirrored());
            texture.id = loadTexture.textureId();
            texture.type = typeName;
            texture.path = folderPath.C_Str();
            textures.push_back(texture);
        }
    }
    return textures;
}

前面一章说的比较清楚对于节点中的内容,这里就不解释了

使用

Model model;
MyShader shaderModel;
//注意我们前面写好的着色器类,添加新的着色器就ok了 
shaderModel.creatShader(":/QtGuiApplication1/Resources/config/shaderMode.vs",":/QtGuiApplication1/Resources/config/shaderMode.fs");
  QString fileName = QDir::currentPath();//当前工作路径
    fileName += "/Resources/model/Sphere.obj";
    mt.init(shaderModel.getShader());
    model.init(fileName.toStdString(), shaderModel.getShader());
    model.setModelLocation(QVector3D(-2.0,0.0,-4.0));
 model.draw(camera);

这时我们按照前面章节讲的添加一个新的着色器,且只包含顶点,和颜色(红色)。
效果:

image.png

出现了球体,因为模型里没有没有纹理贴图数据,所以我给了个红色在shader中。
球体模型,单独的纹理提取
链接:https://pan.baidu.com/s/1Z2Rft6otGfYLcr7bBt6KCw
提取码:jsdd
复制这段内容后打开百度网盘手机App,操作更方便哦
修改着色器代码:
shaderMode.vs

#version 430 
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
in vec3 vPosition;
in vec3 normal;
in vec2 uv;
out vec2 texcoord;
void main(void)
{
gl_Position=proj_matrix*mv_matrix*vec4(vPosition,1.0);
texcoord=uv;
}

shaderMode.fs

#version 430 
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform sampler2D textureID;
in vec2 texcoord;
void main(void) 
{
color =texture(textureID,texcoord) ;
}

然后我们自己添加一张纹理:
myMesh.h添加

QOpenGLTexture texture;

.cpp中启用这张纹理:(注意初始化在构造函数列表中了)

#include "stdafx.h"
#include "MyMesh.h"
MyMesh::MyMesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures): ebo(QOpenGLBuffer::IndexBuffer), texture(QImage("./Resources/model/earth.bmp"))
{
    this->vertices = vertices;
    this->indices = indices;
    this->textures = textures;
}
void MyMesh::init(QOpenGLShaderProgram* shaderProgram)
{
    initializeOpenGLFunctions();
    this->shaderProgram = shaderProgram;
    shaderProgram->bind();
    vao.create();
    vbo.create();
    ebo.create();
    vao.bind();
    vbo.bind();
    vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    vbo.allocate(&vertices[0], this->vertices.size() * sizeof(Vertex));
    // 设置顶点坐标指针
    vPosition = shaderProgram->attributeLocation("vPosition");
    shaderProgram->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, sizeof(Vertex));
    glEnableVertexAttribArray(vPosition);
    // 设置法线指针
    normal= shaderProgram->attributeLocation("normal");
    shaderProgram->setAttributeBuffer("normal", GL_FLOAT, offsetof(Vertex, Normal), 3, sizeof(Vertex));//shader变量索引,参数类型,偏移量,元素大小,步长
    glEnableVertexAttribArray(normal);
    // 设置顶点的纹理坐标
    uv = shaderProgram->attributeLocation("uv");
    shaderProgram->setAttributeBuffer(uv, GL_FLOAT, offsetof(Vertex, TexCoords), 2, sizeof(Vertex));
    glEnableVertexAttribArray(uv);
    ebo.bind();
    ebo.setUsagePattern(QOpenGLBuffer::StaticDraw);
    ebo.allocate(&this->indices[0], this->indices.size() * sizeof(GLuint));
    vao.release();
    ebo.release();
    vbo.release();
    shaderProgram->release();
    vertices.clear();
}
void MyMesh::draw(Camera camera) {
    shaderProgram->bind();
    texture.bind(texture.textureId());
    shaderProgram->setUniformValue("textureID", texture.textureId());
    mv_loc = shaderProgram->uniformLocation("mv_matrix");
    //构建视图矩阵
    QMatrix4x4 m;
    m.translate(locationX, locationY, locationZ);
    QMatrix4x4 v;
    v.lookAt(QVector3D(camera.location.x, camera.location.y, camera.location.z),
        QVector3D(camera.viewPoint.x, camera.viewPoint.y, camera.viewPoint.z),
        QVector3D(camera.worldY.x, camera.worldY.y, camera.worldY.z));
    mv = v * m;
    shaderProgram->setUniformValue(mv_loc, mv);
    vao.bind();
    glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
    vao.release();
    texture.release();
    shaderProgram->release();
}
void MyMesh::setLocation(float x, float y, float z) {
    locationX = x;
    locationY = y;
    locationZ = z;
}

效果:


image.png

这时我们shader中有了法线就可以研究冯氏(Phong)光照模型了。

目录

VSC++2019+QT+OpenGL
QT+OpenGL一之绘制立方体(三角形图元)
QT+OpenGL二之纹理贴图
QT+OpenGL三之矩阵简解
QT+OpenGL四之相机的移动和旋转
QT+OpenGL五之绘制不同的模型(vao,vbo机制)
QT+OpenGL六之天空盒
QT+OpenGL七之使用EBO
QT+OPenGL八之模型准备
QT+OPenGL九之模型解码
QT+OPenGL十之光照模型
QT+OPenGL十一之漫反射和镜面反射贴图
QT+OPenGL十二之定向光
QT+OPenGL十三之真正的点光源和聚光灯
QT+OPenGL十四之多光源混合的问题
QT+OPenGL十五之深度缓冲区
QT+OPenGL十六之模板缓冲区
QT+OPenGL十七帧缓冲区(离屏渲染)
QT+OPenGL十八抗锯齿
QT+OPenGL十九镜面反射效率调整
QT+OPenGL二十Gamma校正

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容