惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

www.infosecurity-magazine.com
www.infosecurity-magazine.com
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
Cloudbric
Cloudbric
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
N
News and Events Feed by Topic
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Securelist
The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
D
DataBreaches.Net
S
Schneier on Security
L
LangChain Blog
Jina AI
Jina AI
M
MIT News - Artificial intelligence
Recent Announcements
Recent Announcements
T
Tenable Blog
B
Blog RSS Feed
V
Visual Studio Blog
Simon Willison's Weblog
Simon Willison's Weblog
G
Google Developers Blog
T
The Exploit Database - CXSecurity.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
WordPress大学
WordPress大学
W
WeLiveSecurity
I
InfoQ
The Hacker News
The Hacker News
雷峰网
雷峰网
月光博客
月光博客
P
Privacy & Cybersecurity Law Blog
O
OpenAI News
Hacker News: Ask HN
Hacker News: Ask HN
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
P
Privacy International News Feed
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
L
Lohrmann on Cybersecurity
人人都是产品经理
人人都是产品经理
V
V2EX
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Cybersecurity and Infrastructure Security Agency CISA
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Troy Hunt's Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
阮一峰的网络日志
阮一峰的网络日志
SecWiki News
SecWiki News
Microsoft Azure Blog
Microsoft Azure Blog

博客园 - 京山游侠

使用 Alacritty 替代 Gnome-Terminal 解决 Ubuntu 中终端的行间距问题 Linux 下的 OpenGL 之路(九):天空盒、反射和折射 Linux 下的 OpenGL 之路(八):贴图和材质 Linux 下的 OpenGL 之路(七):光照 新版 Ubuntu 中 gnome-terminal 可恶的行间距问题逼我退回了 Ubuntu 20.04 Linux 下的 OpenGL 之路(六):使用 GLFW 在 OpenGL 的场景中漫游 Linux 下的 OpenGL 之路(四):展示红宝书中自带的 3D 模型 Linux 下的 OpenGL 之路(三):向 3D 世界迈出一小步 Linux 下的 OpenGL 之路(二):为什么说使用 Linux 系统学习 OpenGL 更方便 Linux 下的 OpenGL 之路(一):安装 Ubuntu 20.10,高性能计算和 3D 视觉呈现第一步 测试一下我的电脑有多快 在4K屏下以超过VMWare默认的最高分辨率运行Linux系统 Linux 桌面玩家指南:20. 把 Linux 系统装入移动硬盘打包带走 Linux 桌面玩家指南:19. 深入理解 JavaScript,及其开发调试工具 Linux 桌面玩家指南:18. 使用 Docker 隔离自己的开发环境和部署环境 Linux 桌面玩家指南:17. 在 Ubuntu 中使用 deepin-wine,解决一些依赖 Windows 的痛点问题 Linux 桌面玩家指南:16. 使用 CUDA 发挥显卡的计算性能 Linux 桌面玩家指南:15. 深度学习可以这样玩 Linux 桌面玩家指南:14. 数值计算和符号计算
Linux 下的 OpenGL 之路(五):使用 Assimp 库加载 3D 模型
京山游侠 · 2021-08-07 · via 博客园 - 京山游侠

前言

要想让自己的 3D 之旅多一点乐趣,肯定得想办法找一些有意思一点的 3D 模型。3D 模型有各种各样的格式,obj的,stl的,fbx的等等不一而足。特别是 obj 格式的 3D 模型,完全是纯文本格式,网络上很多高手都喜欢自己写程序对 obj 格式的模型文件进行解析。我自己收集了一些 3D 模型,有几个 obj 格式的,也有几个 stl 格式的,为我以后的学习做准备。当我需要查看这些模型的时候,我首选是使用 Blender。在我的程序中使用的时候,我首选 Assimp 库。在我之前的随笔中,已经对 Assimp 库做了介绍。见 为什么说使用 Linux 系统学习 OpenGL 更方便

使用 Assimp 加载 3D 模型

Assimp 的使用是非常简单的,直接参考 Assimp 的文档,依葫芦画瓢即可。这里我只简单介绍一下 Assimp 中的数据组织结构。Assimp 读入模型后,先有一个 aiScene 对象,在这个对象中,有一个根节点,根节点又有子结点,子结点还可能有子结点,形成一个树状的结构。节点的类型是 aiNode。每一个节点包含一个或多个 aiMesh,而 aiMesh 又包含顶点数据和索引数据,索引数据是储存在 aiFace 类型中的,一般来说,我们的每一个 aiFace 都应该是一个三角形。每一个 aiMesh 又有对应的材质信息,因为我这里还没有涉及到光照和贴图,所以暂不考虑材质信息。只需要递归地进行解析,我们就可以很容易获得模型中的所有顶点信息和索引信息。

在这里,我创建了一个 Model 类,同时利用了之前的 Mesh 类,一个 Model 类的对象中可以包含多个 Mesh 类的对象。然后,在 Model 类的 loadModel() 方法中,使用 Assimp 读取模型数据,遍历各 aiNode, 再遍历 aiMesh,将 aiMesh 中的数据拷贝到 Mesh 中,然后调用所有 Mesh 对象的 setup() 方法和 render() 方法,即可完成模型的渲染。Model 类的内容如下:

#ifndef __MODEL_HPP__
#define __MODEL_HPP__

#include "mesh.hpp"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>

class Model
{
private:
    std::vector<Mesh> meshes;

public:
    void loadModel(std::string filename)
    {
        Assimp::Importer importer;
        const aiScene *scene = importer.ReadFile(filename,
                                                 aiProcess_Triangulate |
                                                     aiProcess_GenNormals |
                                                     aiProcess_FlipUVs);
        processNode(scene->mRootNode, scene);
    }

    void processNode(aiNode *node, const aiScene *scene)
    {
        for (int i = 0; i < node->mNumMeshes; i++)
        {
            aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
            Vertex tempVertex;
            std::vector<Vertex> tempVertices;
            std::vector<GLuint> tempIndices;
            //先读取顶点信息
            for (int j = 0; j < mesh->mNumVertices; j++)
            {
                if (mesh->HasPositions())
                {
                    tempVertex.position.x = mesh->mVertices[j].x;
                    tempVertex.position.y = mesh->mVertices[j].y;
                    tempVertex.position.z = mesh->mVertices[j].z;
                    tempVertex.position.w = 1.0f;
                }
                if (mesh->HasNormals())
                {
                    tempVertex.normal.x = mesh->mNormals[j].x;
                    tempVertex.normal.y = mesh->mNormals[j].y;
                    tempVertex.normal.z = mesh->mNormals[j].z;
                }
                if (mesh->HasTextureCoords(0))
                {
                    tempVertex.texCoord.x = mesh->mTextureCoords[0][j].x;
                    tempVertex.texCoord.y = mesh->mTextureCoords[0][j].y;
                }

                tempVertices.push_back(tempVertex);
            }
            //再读取索引信息
            for (int i = 0; i < mesh->mNumFaces; i++)
            {
                for (int j = 0; j < mesh->mFaces[i].mNumIndices; j++)
                {
                    tempIndices.push_back(mesh->mFaces[i].mIndices[j]);
                }
            }
            Mesh tempMesh;

            tempMesh.setVertices(std::move(tempVertices));
            tempMesh.setIndices(std::move(tempIndices));
            tempMesh.setup();
            this->meshes.push_back(std::move(tempMesh));
        }

        if (node->mNumChildren != 0)
        {
            for (int k = 0; k < node->mNumChildren; k++)
            {
                processNode(node->mChildren[k], scene);
            }
        }
        return;
    }

    void render()
    {
        for (auto i = meshes.begin(); i != meshes.end(); i++)
        {
            i->render();
        }
    }
};

#endif

然后来测试一下我之前收集的那些模型文件,其主文件 LoadModel.cpp 内容如下:

#include "../include/app.hpp"
#include "../include/shader.hpp"
#include "../include/model.hpp"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

class MyApp : public App {
    private:
        const GLfloat clearColor[4] = {0.2f, 0.3f, 0.3f, 1.0f};
        Model allis;
        Model buma;
        Model saber;
        Model cottage;
        Model bugatti;
        Shader* simpleShader;

    public:
        void init(){
            
            ShaderInfo shaders[] = {
                {GL_VERTEX_SHADER, "simpleShader.vert"},
                {GL_FRAGMENT_SHADER, "simpleShader.frag"},
                {GL_NONE, ""}
            };
            simpleShader = new Shader(shaders);
            allis.loadModel( "allis.stl");
            buma.loadModel("buma.stl");
            saber.loadModel( "saber.stl");
            bugatti.loadModel("bugatti/bugatti.obj");
          
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LEQUAL);

            glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
        }

        void display(){
            glClearBufferfv(GL_COLOR, 0, clearColor);
            glClear(GL_DEPTH_BUFFER_BIT);

            glm::mat4 I(1.0f);
            glm::vec3 X(1.0f, 0.0f, 0.0f);
            glm::vec3 Y(0.0f, 1.0f, 0.0f);
            glm::vec3 Z(0.0f, 0.0f, 1.0f);
            float t = (float)glfwGetTime();

            glm::mat4 view_matrix = glm::translate(I, glm::vec3(0.0f, 0.0f, -10.0f))
                                        * glm::rotate(I, t, Y);

            glm::mat4 projection_matrix = glm::perspective(glm::radians(45.0f), aspect, 1.0f, 100.0f);

            glm::mat4 allis_model_matrix = glm::translate(I, glm::vec3(-3.0f, -1.5f, 0.0f)) 
                                            * glm::scale(I, glm::vec3(1.0f, 1.0f, 1.0f)) * glm::rotate(I, glm::radians(-90.0f), X);
            
            simpleShader->setModelMatrix(allis_model_matrix);
            simpleShader->setViewMatrix(view_matrix);
            simpleShader->setProjectionMatrix(projection_matrix);
            simpleShader->setCurrent();
            allis.render();

            glm::mat4 buma_model_matrix = glm::translate(I, glm::vec3(-1.0f, -1.5f, 0.0f)) 
                                             * glm::scale(I, glm::vec3(0.03f, 0.03f, 0.03f)) * glm::rotate(I, glm::radians(0.0f), X);
            simpleShader->setModelMatrix(buma_model_matrix);
            buma.render();

            glm::mat4 saber_model_matrix = glm::translate(I, glm::vec3(7.5f, 0.6f, 1.0f)) 
                                            * glm::scale(I, glm::vec3(0.03f, 0.03f, 0.03f)) * glm::rotate(I, glm::radians(-90.0f), X);
            simpleShader->setModelMatrix(saber_model_matrix);
            saber.render();

            glm::mat4 bugatti_model_matrix = glm::translate(I, glm::vec3(5.0f, -1.0f, 0.0f)) 
                                                 * glm::scale(I, glm::vec3(0.2f, 0.2f, 0.2f)) * glm::rotate(I, glm::radians(0.0f), X);
            simpleShader->setModelMatrix(bugatti_model_matrix);
            bugatti.render();

        }

        ~MyApp(){
            if(simpleShader != NULL){
                delete simpleShader;
            }
        }

};


DECLARE_MAIN(MyApp)

效果如下:

我这里只展示了三个美女和一个汽车,从左侧的文件列表中可以看出,我还收集了不少其它的模型,以后再逐渐与大家见面。

目前看起来还只是白茫茫一片,那是因为模型太精细了,所以看不清楚网格。下一步,我将在我的程序框架中添加改变窗口大小、在场景中漫游、线框模型和面模型之间切换等功能。今天就到这里吧。

版权申明

该随笔由京山游侠在2021年08月07日发布于博客园,引用请注明出处,转载或出版请联系博主。QQ邮箱:1841079@qq.com