本文转自:扒几个 3D 模型备用 - 京山游侠 - 博客园 (cnblogs.com)

前言

在上一篇中,我展示了 OpenGL 开发的基本过程,算是向 3D 世界迈出的一小步吧。对于简单的 3D 物体,比如立方体、球体、圆环等等,我们只需要简单的计算就可以得到他们的顶点的坐标。但是仅仅这样,还不是太过瘾,我们需要找一些复杂一点的 3D 模型,以便于我们体会 3D 世界的魅力。

在我学习 OpenGL 的过程中,我收集了不少的 3D 模型,主要是从 Free3D 下载的,都是 Obj 格式的文件,有的带纹理贴图,有的不带纹理贴图。比如,有一个小木屋的模型,带纹理贴图和法线贴图,是我学习贴图和光照的好素材。还有一个地球的模型,还有几辆汽车的模型。还有我从著名的 OpenGL 网络教程 LearnOpenGL 中下载得有一套 nanosuit 的模型。对于这些有着规范格式的 3D 模型,我觉得使用 Assimp 库加载是比较好的选择,至于 Assimp 库,以后再介绍。

另外,茶壶也是一个经典的模型,不过是以贝塞尔曲面的方式定义的。贝塞尔曲面其实不难,使用 16 个控制点可以描述一个曲面,并且可以根据我们需要的光滑程度选择不同的细分级别,关于贝塞尔曲面的内容留待以后再专讲,而且我觉得和曲面细分着色器一起学习效果更佳。那么这个茶壶模型的数据在哪里可以找到呢?FreeGlut 中有,可以在 github 中找到。除此之外,红宝书的源代码中也有一个茶壶的数据。这里不赘述。

我这里要扒的几个模型来自红宝书的源代码,它们分别是 armadillo.vbm、 bunny.vbm 和 ninja.vbm。这里,作者使用了他自创的 vbm 模型格式。作者还写了从 obj 格式到 vbm 格式转换的工具以及从 Maya 导出 vbm 格式的工具。但毕竟 vbm 格式不是标准的通用格式,我并不是很喜欢。但是为了把这三个模型显示出来看看,我还是认真研究了作者的源代码。

VBM 模型文件的具体细节

我是通过阅读红宝书源代码中的 vbm.h 和 vbm.cpp 文件来了解 vbm 模型文件的细节的。这是一个二进制的模型文件,一开始是个 VBM_HEADER 结构,在作者的设计中,该文件分为新版和旧版,旧版的头部结构为 VBM_HEADER_OLD,但是从我扒出的数据来看,根本就不需要考虑旧版。

在 VBM_HEADER 之后,是若干个 VBM_ATTRIB_HEADER 结构,该结构用来说明每个顶点包含哪些属性,每个属性又包含哪些分量。从我扒出的数据来看,以上三个模型,都是包含三个属性的,分别是顶点坐标,包含 4 个 GLfloat 分量,顶点法向量,包含 3 个 GLfloat 分量,纹理贴图坐标,包含两个 GLfloat 分量。这和我上一篇中对顶点格式的设计简直一模一样。

在 VBM_ATTRIB_HEADER 之后,是若干个 VBM_FRAME_HEADER,看来该作者设计该格式是可以支持动画的。不过以我扒出的数据来看,以上三个模型文件都只包含一帧。

在 VBM_FRAME_HEADER 之后就是顶点数据。从头文件中可以得到顶点的个数,以及每个顶点包含哪些属性,以及每个属性包含几个分量,就很容易算出顶点数据的长度。

顶点数据之后,就是索引数据。我读源代码,同时还发现顶点数据之后是材质信息。这两组数据是有点混淆的。好在,以我扒出的数据来看,以上三个模型文件既没有使用索引,也没有包含任何材质,那倒是让我省事了不少。

编写我自己的 VbmObject 类

参考我之前写的 Mesh 类,就很容易写一个能在我的 App 框架中非常容易使用的 VbmObject 类。在 VbmObject 类中,写一个 loadFromVBM() 方法,以从文件中加载顶点数据,同时获取顶点个数的信息。然后写一个 setup() 方法,用来创建相应的 VAO 和 VBO,并向缓存中存入数据,并启用顶点属性。这里需要特别注意的是,该模型文件中的数据,是每一个属性集中存放的,所以调用 glVertexAttribPointer() 方法时要特别注意。最后,写一个 render() 方法进行渲染,render() 方法很简单,就是调用 glDrawArrays(),当然,调用该方法之前需要绑定 VAO。

vbm.hpp 的完整代码如下:

#ifndef __VBM_H__
#define __VBM_H__#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
#include <string>
#include <string.h>
#include <GL/glew.h>
#include <iostream>typedef struct VBM_HEADER_t
{unsigned int magic;unsigned int size;char name[64];unsigned int num_attribs;unsigned int num_frames;unsigned int num_vertices;unsigned int num_indices;unsigned int index_type;unsigned int num_materials;unsigned int flags;
} VBM_HEADER;typedef struct VBM_ATTRIB_HEADER_t
{char name[64];unsigned int type;unsigned int components;unsigned int flags;
} VBM_ATTRIB_HEADER;typedef struct VBM_FRAME_HEADER_t
{unsigned int first;unsigned int count;unsigned int flags;
} VBM_FRAME_HEADER;class VbmObject{protected:unsigned char* file_data;unsigned char* vertex_data;unsigned int vertex_num;GLuint VAO, VBO;public:bool loadFromVBM(const char * filename){std::cout << "File name: " << filename << std::endl;FILE * f = NULL;f = fopen(filename, "rb");if(f == NULL)return false;fseek(f, 0, SEEK_END);size_t filesize = ftell(f);fseek(f, 0, SEEK_SET);file_data = new unsigned char [filesize];fread(file_data, filesize, 1, f);fclose(f);VBM_HEADER * header = (VBM_HEADER *)file_data;vertex_data = file_data + header->size + header->num_attribs * sizeof(VBM_ATTRIB_HEADER) + header->num_frames * sizeof(VBM_FRAME_HEADER);vertex_num = header->num_vertices;std::cout << "Num of Vertices: " << vertex_num << std::endl;return true;}void setup(){glCreateVertexArrays(1, &VAO);glBindVertexArray(VAO);glCreateBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glNamedBufferStorage(VBO, 9*sizeof(GLfloat)*vertex_num, vertex_data, 0);glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat)*vertex_num*4));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat)*vertex_num*3));glEnableVertexAttribArray(2);}void render(){glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, vertex_num);}~VbmObject(){if(file_data != NULL){delete file_data;}}
};#endif

主程序文件是 DumpVbm.cpp,其框架结构还是和前面的差不多,先是继承 App 类,在 init() 方法中初始化数据,比如调用 VbmObject 对象的 loadFromVBM() 方法,调用 setup() 方法,同时创建 shader。然后在 display() 中准备模型、视图、投影矩阵,向 shader 中传递这些矩阵数据,然后调用 VbmObject 对象的 render() 方法。

DumpVbm.cpp 的完整内容如下:

#include "../include/app.hpp"
#include "../include/shader.hpp"
#include "../include/vbm.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};VbmObject armadillo;VbmObject bunny;VbmObject ninja;Shader* shaderDumpVbm;public:void init(){ShaderInfo shaders[] = {{GL_VERTEX_SHADER, "dumpvbm.vert"},{GL_FRAGMENT_SHADER, "dumpvbm.frag"},{GL_NONE, ""}};shaderDumpVbm = new Shader(shaders);armadillo.loadFromVBM("armadillo.vbm");armadillo.setup();bunny.loadFromVBM("bunny.vbm");bunny.setup();ninja.loadFromVBM("ninja.vbm");ninja.setup();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, -5.0f))* glm::rotate(I, t, Y);glm::mat4 projection_matrix = glm::perspective(glm::radians(45.0f), aspect, 1.0f, 100.0f);glm::mat4 armadillo_model_matrix = glm::translate(I, glm::vec3(-2.0f, 0.0f, 0.0f)) * glm::scale(I, glm::vec3(0.015f, 0.015f, 0.015f)) * glm::rotate(I, glm::radians(180.0f), Y);shaderDumpVbm->setModelMatrix(armadillo_model_matrix);shaderDumpVbm->setViewMatrix(view_matrix);shaderDumpVbm->setProjectionMatrix(projection_matrix);shaderDumpVbm->setCurrent();armadillo.render();glm::mat4 bunny_model_matrix =  glm::scale(I, glm::vec3(10.0f, 10.0f, 10.0f));shaderDumpVbm->setModelMatrix(bunny_model_matrix);bunny.render();glm::mat4 ninja_model_matrix = glm::translate(I, glm::vec3(2.0f, -1.0f, 0.0f)) * glm::scale(I, glm::vec3(0.015f, 0.015f, 0.015f));shaderDumpVbm->setModelMatrix(ninja_model_matrix);ninja.render();}~MyApp(){if(shaderDumpVbm != NULL){delete shaderDumpVbm;}}};DECLARE_MAIN(MyApp)

shader 文件和之前没有区别。编译运行,命令如下:

g++ DumpVbm.cpp -o DumpVbm -lGL -lglfw -lGLEW
./DumpVbm

就可以看到效果了。如下:

扒几个 3D 模型备用相关推荐

  1. Open3DGen:从RGB-D图像重建纹理3D模型的开源软件

    点云PCL免费知识星球,点云论文速读. 文章:Open3DGen: Open-Source Software for Reconstructing Textured 3D Models from RG ...

  2. PBR游戏3D模型合集包 PBR Game 3D-Models Bundle February 2022

    PBR游戏3D模型捆绑包2022年2月 大小解压后:6.99G MAX| OBJ | FBX |TEX  模型获取:PBR游戏3D模型合集包 PBR Game 3D-Models Bundle Feb ...

  3. Blender制作3D模型导出到UE5完整学习教程

    学习如何在Blender中创建AAA游戏资产,然后导出到虚幻引擎5的完整指南 你会学到什么 遵循关于创建一套AAA游戏就绪的优质中世纪市场摊位的完整指南 最大化您的推荐人的潜力,以开发强大的游戏资产概 ...

  4. PCB 3D模型与渲染

    如何制作一张印刷电路板(PCB)的3D渲染效果图? - 况琪的回答 - 知乎             不错 从AltiumDesigner导出电路完美3D模型至Solidworks的方法        ...

  5. BundleTrack:无需实例或类级别3D模型的6D姿态跟踪算法(IROS2021)

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨paopaoslam 来源丨泡泡机器人SLAM 标题:BundleTrack: 6D Pose ...

  6. CV进入三维时代!Facebook在ICCV 2021 发布两个3D模型,自监督才是终极答案?

    来源:Facebook AI 编辑:LRS [导读]长久以来CV的训练一直停留在二维数据上,三维数据因为标注成本高等原因都需要专业人员来开发专用模型.Facebook在ICCV 2021 发布两个3D ...

  7. ResNet也能用在3D模型上了,清华「计图」团队新研究已开源

    鱼羊 发自 凹非寺 量子位 报道 | 公众号 QbitAI 用AI处理二维图像,离不开卷积神经网络(CNN)这个地基. 不过,面对三维模型,CNN就没有那么得劲了. 主要原因是,3D模型通常采用网格数 ...

  8. PNAS | 开发用于优化蛋白质设计的3D模型

    研究人员开发了一个计算程序,以帮助设计治疗用蛋白质,预测相互作用. 研究人员已经开发出一种过程,他们说这种过程可以减少计算蛋白设计所涉及的工作.该技术使用三维(3D)结构模型来预测分子嵌段的新颖组合如 ...

  9. 一步一步教您打印出自己大脑的3D模型

    作为一名3D打印爱好者,您有没有想过打印一个自己大脑的3D模型呢?现在机会来了,近日在开源硬件热站Instructables上,一个昵称为3d_printed_brain的用户贴出了一个简单的教程,教 ...

最新文章

  1. html文本框对齐 display:block,html – 在所有浏览器中垂直对齐标签和文本框
  2. Logstash(四)插件Output详解
  3. Nature最新封面:两大数学难题被AI突破!DeepMind YYDS
  4. git 提交代码命令_Git命令可视化展示,代码管理再也不愁了,建议收藏!
  5. Could not calculate build plan: Plugin org.apache.maven.plugins:maven-war-plugin:2.4
  6. 通俗易懂,带你了解Kafka
  7. django ajax 更新表格_Django(反向解析,路由分发、名称空间、视图层、虚拟环境、Django版本、json,CBV)...
  8. Nodejs nmp 常用命令
  9. Java可变引用,Java – 对可变对象的易失性引用 – 对对象的字段的更新对所有线程都是可见的...
  10. mysql选择哪个隔离级别更好_深入理解Mysql的四种隔离级别
  11. 【Oracle】数据库热备
  12. 第二阶段冲刺 站立会议 -01个人进度
  13. 2017OKR年终回顾与2018OKR初步规划
  14. python图片分类毕业设计成果报告书_4period;毕业设计成果报告书
  15. MyBatis中jdbc和managed的区别
  16. ICC Profile
  17. 开源项目SMSS开发指南(二)——基于libevent的线程池
  18. 3.罗马数字转整数(JS)
  19. Python的文件处理
  20. 北京卫星地图 百度卫星地图高清版(含道路地名标签叠加)

热门文章

  1. 一键移除所有权限密码,不到1MB!
  2. double处理arithmeticexception为什么不报错_板式换热器为什么冷热不均匀?应怎样检查并简单处理?...
  3. 网络安全08-虚拟机运行架构(寄居架构+原生架构)、虚拟机产品简单介绍、windows操作系统--屏蔽系统自动更新
  4. 网络安全06_安装Windows XP_几个虚拟机能够相互ping通,并且能够ping通互联网114.114.114.114
  5. ps 替换文字_Python操作PPT实现自动查找替换
  6. easyui中的option设置selected没有效果
  7. Confluence 6 为空白空间编辑默认主页
  8. Docker入门(CentOS7)
  9. 利用反射操作bean的属性和方法
  10. ASP.NET里创建Microsoft Word文档