如果你是中途开始学习本教程的,即使你对OpenGL已经非常熟悉,请至少了解以下几个章节,因为Qt中提供了OpenGL的很多便捷操作,熟悉这些操作可以让我们在Qt中高效的使用OpenGL进行绘图。
Assimp
到目前为止,我们已经在所有的场景中大面积滥用了我们的容器盒小盆友,但就是容器盒是我们的好朋友,时间久了我们也会喜新厌旧。一些图形应用里经常会使用很多复杂且好玩儿的模型,它们看起来比静态的容器盒可爱多了。但是,我们无法像定义容器盒一样手动地去指定房子、货车或人形角色这些复杂模型的顶点、法线和纹理坐标。我们需要做的也是应该要做的,是把这些模型导入到应用程序中,而设计制作这些3D模型的工作应该交给像Blender、3DS Max或者Maya这样的工具软件。
那些3D建模工具,可以让美工们构建一些复杂的形状,并将贴图应用到形状上去,即纹理映射。然后,在导出模型文件时,建模工具会自己生成所有的顶点坐标、顶点法线和纹理坐标。这样,美工们可以不用了解大量的图像技术细节,就能有大量的工具集去随心地构建高品质的模型。所有的技术细节内容都隐藏在里导出的模型文件里。而我们,这些图形开发者,就必须得去关注这些技术细节了。
因此,我们的工作就是去解析这些导出的模型文件,并将其中的模型数据存储为OpenGL能够使用的数据。一个常见的问题是,导出的模型文件通常有几十种格式,不同的工具会根据不同的文件协议把模型数据导出到不同格式的模型文件中。有的模型文件格式只包含模型的静态形状数据和颜色、漫反射贴图、高光贴图这些基本的材质信息,比如Wavefront的.obj文件。而有的模型文件则采用XML来记录数据,且包含了丰富的模型、光照、各种材质、动画、摄像机信息和完整的场景信息等,比如Collada文件格式。Wavefront的obj格式是为了考虑到通用性而设计的一种便于解析的模型格式。建议去Wavefront的Wiki上看看obj文件格式是如何封装的。这会给你形成一个对模型文件格式的一个基本概念和印象。
模型加载库
现在市面上有一个很流行的模型加载库,叫做Assimp,全称为Open Asset Import Library。Assimp可以导入几十种不同格式的模型文件(同样也可以导出部分模型格式)。只要Assimp加载完了模型文件,我们就可以从Assimp上获取所有我们需要的模型数据。Assimp把不同的模型文件都转换为一个统一的数据结构,所有无论我们导入何种格式的模型文件,都可以用同一个方式去访问我们需要的模型数据。
当导入一个模型文件时,即Assimp加载一整个包含所有模型和场景数据的模型文件到一个scene对象时,Assimp会为这个模型文件中的所有场景节点、模型节点都生成一个具有对应关系的数据结构,且将这些场景中的各种元素与模型数据对应起来。下图展示了一个简化的Assimp生成的模型文件数据结构:
- 所有的模型、场景数据都包含在scene对象中,如所有的材质和Mesh。同样,场景的根节点引用也包含在这个scene对象中
- 场景的根节点可能也会包含很多子节点和一个指向保存模型点云数据mMeshes[]的索引集合。根节点上的mMeshes[]里保存了实际了Mesh对象,而每个子节点上的mMesshes[]都只是指向根节点中的mMeshes[]的一个引用(译者注:C/C++称为指针,Java/C#称为引用)
- 一个Mesh对象本身包含渲染所需的所有相关数据,比如顶点位置、法线向量、纹理坐标、面片及物体的材质
- 一个Mesh会包含多个面片。一个Face(面片)表示渲染中的一个最基本的形状单位,即图元(基本图元有点、线、三角面片、矩形面片)。一个面片记录了一个图元的顶点索引,通过这个索引,可以在mMeshes[]中寻找到对应的顶点位置数据。顶点数据和索引分开存放,可以便于我们使用缓存(VBO、NBO、TBO、IBO)来高速渲染物体。(详见Hello Triangle)
- 一个Mesh还会包含一个Material(材质)对象用于指定物体的一些材质属性。如颜色、纹理贴图(漫反射贴图、高光贴图等)
所以我们要做的第一件事,就是加载一个模型文件为scene对象,然后获取每个节点对应的Mesh对象(我们需要递归搜索每个节点的子节点来获取所有的节点),并处理每个Mesh对象对应的顶点数据、索引以及它的材质属性。最终我们得到一个只包含我们需要的数据的Mesh集合。
用建模工具构建物体时,美工通常不会直接使用单个形状来构建一个完整的模型。一般来说,一个模型会由几个子模型/形状组合拼接而成。而模型中的那些子模型/形状就是我们所说的一个网格。例如一个人形模型,美工通常会把头、四肢、衣服、武器这些组件都分别构建出来,然后在把所有的组件拼合在一起,形成最终的完整模型。一个网格(包含顶点、索引和材质属性)是我们在OpenGL中绘制物体的最小单位。一个模型通常有多个网格组成。
获取Assimp
方式一:官网:http://assimp.sourceforge.net/main_downloads.html
方式二(推荐):博主的蓝奏云网盘资源:https://italink.lanzous.com/icylqcf
因为Assimp官方的已编译库不能很好地覆盖在所有平台上运行,我们需要借助CMake来重新编译一个当前环境能够使用的库。
因此,你还需要一个CMake(同样你可以点击这里,下载博主的Cmake资源)
构建Assimp
博主的Qt使用的是32位MinGw编译,因此需要构建一个专门的assimp库。
步骤:
1、添加Cmake/bin环境变量:把刚刚下载的Cmake安装好,把安装目录下的/bin目录添加到系统环境变量,配置成功后,使用cmd输入cmake可以看到一系列指令,说明配置成功。
2、添加MinGW环境变量:把Qt的根目录下./Tool/mingw530_32/bin目录也添加到环境变量中,添加成功能在cmd中使mingw32-make指令
3、使用cmake构建Assimp库:
编辑指令:将下方指令中的路径修改为你本地Qt的MinGW路径:
cmake -G "MinGW Makefiles" -DENABLE_BOOST_WORKAROUND=ON -DBUILD_STATIC_LIB=ON -DCMAKE_RC_COMPILER=E:/Qt5.10.0/Tools/mingw530_32/bin/windres.exe -DCMAKE_MAKE_PROGRAM=E:/Qt5.10.0/Tools/mingw530_32/bin/mingw32-make.exe -DCMAKE_LFLAGS=-static -DCMAKE_LFLAGS_DLL=-static -DDX9_INCLUDE_PATH=E:/Qt5.10.0/Tools/mingw530_32/i686-w64-mingw32/include -DD3D9_LIBRARY=E:/Qt5.10.0/Tools/mingw530_32/i686-w64-mingw32/lib/libd3d9.a -DD3DX9_LIBRARY=E:/Qt5.10.0/Tools/mingw530_32/i686-w64-mingw32/lib/libd3dx9.a .
cd到Assimp的根目录,粘贴上方修改后的指令,回车运行。
构建完成后,不要关闭控制台窗口,进行下一步。
4、使用MinGW编译上面的Assimp库:
输入如下指令
mingw32-make -j 4
然后开始编译,如果你编译中途出错 ,可能是你对Assimp库进行了不正当的操作,可以把assimp删了,重新解压一个出来。
中途停住了,可以敲几下回车。
出现上图的情况,说明我们的Assimp已经可以使用了!
在项目中使用Assimp库
这里我重新创建一个QWidget项目用于测试
右键项目名——添加库——外部库
库文件为:...\assimp-4.1.0\lib\libassimp.dll.a
包含路径为:...\assimp-4.1.0\include
然后我们就成功导入Assimp库了,导入之后pro项目文件增加了
然后在main.cpp中使用一个Assimp对象看看
#include "widget.h"
#include <QApplication>
#include <assimp/Importer.hpp>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
Assimp::Importer importer;
return a.exec();
}
运行没出错就说明成功导入
如果崩溃了,说明你可能需要手动导入一下dll
将assimp4.1.0/bin/目录下的两个dll
复制到你的项目生成目录 比如你是debug模式运行,把dll复制到如下目录中
注意初次导入库可能会导致代码提示功能没有更新,需要重新打开一下QtCreator
好了,现在我们已经能够使用Assimp库了,下一节我们将开始使用Assimp来导入3D模型。
结语
笔者在完成这一节的教程真可谓是一波未平一波又起,learnopengl的网站不知怎么的,这几天进不去,而且原learnOpenGL所封装的模型加载库并不适用于Qt,因此需要做很多调整,耽搁了几天。废话不多说,下节我们来玩个好玩的东西!
Assimp环境的配置十分感谢下面两位博主的博文!
https://blog.csdn.net/qq_31615919/article/details/80480497
https://blog.csdn.net/gamesdev/article/details/26969559