QT3D场景提供了一种快速设置3D场景的一种方式,用户凭借着封装好的实体可以快速的在顶层实体(画布)当中增加各种各样的实体,并且通过3DMax软件构造的OBJ文件与QT3D实现信息交互可以的帮助用户摆脱OpenGL的用代码绘制图形的繁琐。本人在做这方面的工作时也看了很多来自CSDN的文章,但是直接将封装好的3D实体放入画布的例子有点少(是不是大家觉得太简单了?),所以作为一个QT 3D建模的小白,我也简要的谈一谈这方面的认识。
在这里,我们将基于QT3D当中的例程BASIC 3D当中的代码一步步的通过讲解并修改其代码可以真正实现一个基于QT的画布。我们将实现的代码有着如下的性质:
这里首先要对QT 3D当中例子代码进行分析,我用到的是QT5.6.0版本,用QT5以上版本的童鞋可以完全参考本丸当中对代码的分析,由于QT5和QT4头文件差别还是蛮大的,所以用QT4的可以大致了解一下思想。(QT4当中有些类在QT5当中是废弃的,而QT5在便宜当中并不会告诉你缺少的头文件,这点非常蛋疼)
首先打开QT,进入到程序初始界面。
在搜索界面输入“3D”,得到的如下图所示。
选择右下角的“QT 3D Basic Shapes C++ Example”工程,得到欢迎界面,此时再次选择这个工程就可以得到工程的源代码如下所示。
QT具有非常好的现代编译器所具有的代码实时查错功能,一些比较基本的语法错误会在你写入代码的时候实时显示出来,所以一旦看到你的代码下面有着一条线(可能是红色的也可能是橙色的)那么就会表明这句话一定是有问题的,而且是较为基本的问题。
下面开始对这个基本的3D程序进行一些分析,在分析之前有必要将这个程序的功能演示以下使得各位对这个程序的大体脉络有一个基本的认识,点击左下角的运行(绿色三角)按钮就可以显示出来本程序基本的3D建模图形如下所示。
在这个程序当中有四个不同形状的QT基本实体,在右边有着与之相关联的四个checkbox,每个CheckBox默认都是选中的,一旦取消选中了某个checkbox那么与之相对应的实体就会消失。下面从代码层面大体上讲一讲功能是如何实现的。
首先就是容器的放置,在本程序当中如果不向QWidget当中放置容器的话那么三维图形就显示不出,因此首先就是搭建可以显示实体的大体环境,代码如下
Window *view = new Window();//window class QWidget *container = QWidget::createWindowContainer(view); QSize screenSize = view->screen()->size(); container->setMinimumSize(QSize(100, 100)); container->setMaximumSize(screenSize);
这几行代码描述的功能就是创建一个容器和一个窗口类,然后通过setMinimumSize和setMaximumSize设定容器的大小,完成了对容器的初始化。
创建了容器之后就要开始对整体窗口布局进行设计,在QT当中可以用水平布局QHBoxLayout和垂直布局QVBoxLayout分别对窗口布局进行设计,而后仅需将不同的窗口以及实体加入这些布局当中就可以完成设计。综合本例子来看,水平布局以及垂直布局都是我们所需要的,因为左边有一整个窗口去显示整体的环境,右边有4个checkbox待摆放,那么在这两类实体之间的空间上的链接就是用QHBoxLayout去实现相连,而由于右边的信息以及checkbox摆放关系是垂直的,所以要用到垂直布局,代码如下所示。
QWidget *widget = new QWidget; QHBoxLayout *hLayout = new QHBoxLayout(widget);//在widget中加入水平的layout QVBoxLayout *vLayout = new QVBoxLayout();//不用参数,因为垂直layout作为组件加入到水平layout当中,也就加入到了widget中 vLayout->setAlignment(Qt::AlignTop);//在顶端对齐 hLayout->addWidget(container, 1);//加入容器 hLayout->addLayout(vLayout);
可以看到,在这段代码当中首先创建了一个QWidget类型的实体,然后分别创建了水平以及垂直布局(hLayout,vLayout),其中水平布局加入了容器,而垂直布局设定顶端对齐,最后用addLayout函数实现了将垂直布局添加到水平布局的功能。
在显示3D图形的过程当中,摄像机是必不可少的,只有摄像机摆放的合适人眼才能看到3D建模的样子。在Qt当中三维图形将实体添加到世界坐标当中然后通过投影的方式去投射到摄像机位置(也就相当于人眼),其中投影到镜头如下面的函数所示。
cameraEntity->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
其中cameraEntity是新建的camera实体,lens表示了镜头,这个函数有着四个参数,第一个参数是视觉域,第二个是分辨率(这里选择了16比9),第三个参数是近平面摆放位置,最后一个是远平面放置位置,后两个参数的选择当中要特别注意,只有在远近平面之间的物体才会显示,所以要是想全部显示出所加入的实体,那么近平面就要足够近,远平面就要足够远。
而后对于摄像机的参数配置就是要确定摄像机摆放的位置,确定向上的单位向量和确定观察的中心,最后在QtInputAspect当中的实体中加入摄像机就算是完成配置了,具体代码如下所示。
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();//创建顶层实体rootEntity
Qt3DInput::QInputAspect *input = new Qt3DInput::QInputAspect; engine.registerAspect(input); Qt3DCore::QCamera *cameraEntity = new Qt3DCore::QCamera(rootEntity);//在新创建的rootEntity里面添加摄像机 cameraEntity->setObjectName(QStringLiteral("cameraEntity")); cameraEntity->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f); cameraEntity->setPosition(QVector3D(0, 0, -20.0f));//设定摄像机初始位置 cameraEntity->setUpVector(QVector3D(0, 1, 0)); cameraEntity->setViewCenter(QVector3D(0, 0, 0));//设定一进入的中心点 input->setCamera(cameraEntity);//使摄像机能左右转动
但是单纯这么设定照相机是不够的,因为照相机所能呈现出来的画面是要在前端显示的,因此需要对前端有一定的交互,而背景颜色也是在前端当中设置的,配置前端首先要创建前端实体
Qt3DRender::QFrameGraph *frameGraph = new Qt3DRender::QFrameGraph(); Qt3DRender::QForwardRenderer *forwardRenderer = new Qt3DRender::QForwardRenderer();
然后需要架设摄像机,设定背景颜色最后激活前端。
forwardRenderer->setCamera(cameraEntity);//架设摄像机forwardRenderer->setClearColor(QColor(QRgb(0xff4d4f)));//设置前端背景颜色frameGraph->setActiveFrameGraph(forwardRenderer);//激活frameGraph然后令设定好的前端加入到顶层实体rootEntity当中。
rootEntity->addComponent(frameGraph);
把这些显示的基本部件构建完成之后就“万事俱备只欠东风”了,下面就是放置3D实体工作了,在这里有如下的几种方法可以选择,首先就是用OpenGL画出实体,然后就是用Qt自带的基本实体,最后就是用例如3DMax导出的OBJ文件实现与QT之间的信息交互。OpenGL画出实体适合OpenGL的老手,不适于新手去画图,而3DMax由于自身较为上手,如今已经成为越来越多人的首选,Qt自带基本实体虽然较为容易构造但是过于简单,只适合学习取用,本程序当中采用的就是Qt基本实体去创建。
再Qt中任何实体加入到三维模型中最为简单的配置方法分为以下几步,首先再根实体中创建实体,然后配置渲染、颜色等于实体相关的元素,最后配置实体缩放大小以及实体再三维模型中的位置。下面就单拿出本代码其中一个实体的设置进行分析。
// Cuboid shape data Qt3DRender::QCuboidMesh *cuboid = new Qt3DRender::QCuboidMesh(); // CuboidMesh Transform Qt3DCore::QTransform *cuboidTransform = new Qt3DCore::QTransform(); cuboidTransform->setScale(4.0f); cuboidTransform->setTranslation(QVector3D(4.0f, -4.0f, 0.0f)); Qt3DRender::QPhongMaterial *cuboidMaterial = new Qt3DRender::QPhongMaterial(); cuboidMaterial->setDiffuse(QColor(QRgb(0x665423))); //Cuboid m_cuboidEntity = new Qt3DCore::QEntity(m_rootEntity); m_cuboidEntity->addComponent(cuboid); m_cuboidEntity->addComponent(cuboidMaterial); m_cuboidEntity->addComponent(cuboidTransform);
这关代码构建的是立方体实体(就是左下角实体),在这个实体当中创建了三个子实体,分别是mesh、transform以及material,分别是渲染、位置以及颜色,在最后代码将三个子实体加入到立方体实体当中,并将立方体实体加入到根实体中完成了立方体的配置,其他实体的加入也将如法炮制,这里便不再赘述。
由于Qt采用的是C++为背景去编写相关代码,所以对于C++的了解将会使得代码的编写更加有效率,C++是面向对象编程的语言,面向对象讲究的是类与封装,每个类在被构造函数创建的时候都会去调用构造函数的代码,从而达到对实体进行调用的目的。在C++的类元素当中(通常在头文件中查看)有着这个类所需要的函数以及各种被此类包含的实体,在C++中,有public类型元素,protected类型元素以及private类型元素,相关作用于不再详述,可以自行查看C++专门书籍,而重点提一下protected类型元素,在Qt当中这个类型之下一般都会是重写鼠标、键盘以及滚轮事件的函数,这一点要注意。在Qt当中为了便于信号与槽之间的连接还增添了两个类型,一个是signal类型,另一个是public slot类型,信号类型当中的函数都是不经方法详述的函数,也就是说此函数不能再与之对应的cpp文件当中定义方法,而仅仅是当作一个信号发出,具体机制由Qt进行配置;而public slot函数可以看作是一个具体的函数,每当一个信号从一个类发出之后另一个类当中的实体一旦接受到此信号就会调用与之相互关联的槽函数,槽函数是有方法说明的。
在本源程序当中,信号和槽的连接就是把checkbox与画布当中的实体进行一一相连并在选中checkbox的时候显示而关闭的时候消失。这种功能是通过checkbox的动作改变发射信号并用connect函数将其信号与函数槽连接起来形成的,就简单拿一个实体的代码来举例子吧,其余的实体都是一样的。
QCheckBox *torusCB = new QCheckBox(widget); torusCB->setChecked(true); torusCB->setText(QStringLiteral("Torus"));
首先是在顶层QWidget当中添加checkbox,其中第二行是默认了checkbox为true,也就是默认了程序运行后所有的实体都将得到显示。
QObject::connect(torusCB, &QCheckBox::stateChanged,modifier, &SceneModifier::enableTorus);
这段代码调用的是connect函数,将checkbox的改变状态的信号与画布当中的“enableTorus”方法连接起来,下面就是enableTorus方法的实现。
void SceneModifier::enableTorus(bool enabled) { m_torusEntity->setParent(enabled ? m_rootEntity : Q_NULLPTR); }
这段代码直接控制了选中的实体能否在画布当中的以显示,其中“m_rotusEntity”表示了当前实体,而后面是一个三元判断语句,检查与之相连的checkbox时候被选中,选中的话就在顶层画布当中显示,而不能选中的话就与空指针连接起来,也就是在画布当中不显示。
大体上分析的就是这么多。但是本代码并不适合进行大工程的开发,因为很多函数都是在main函数当中,例如3D选项的注册,摄像机的摆放以及前端渲染都是在主函数当中完成的,那么为了满足要求我们就需要对函数进行改造,使得代码尽量都放在构造函数当中,解放main函数,在实体构造当中自动调用类方法,达到与原主函数一样的功能。一些操作上的东西就用void类方法就可以,没什么好说的,但是很多人都会遇到在主函数前边创建并进行操作的变量在函数末尾也进行调用了的问题,这种函数在进行可移植性的修改类方法就不能用void作为函数类型,因为在类不同方法之间创建的变量编译器是不会把它们看作是同一变量的,最后就会导致函数调用错误。那么一个比较好的解决方法就是在前面的方法当中返回被后面函数调用到的变量,而再将这个变量作为参数传递到后面的函数当中,代价就是这个变量的创建不能放在某个方法当中,而要放在构造函数当中,注意事项就是这么多,以下是本人的修改代码(删去了有关checkbox的代码)。
首先是main.cpp
#include
#include "myglwidget.h" #include "scenemode.h" #include #include #include int main(int argc, char *argv[]) { QApplication app(argc, argv); SceneMode *defalut = new SceneMode; defalut->resize(400, 300); defalut->setWindowTitle(QStringLiteral("5G huge scale simulation"));//确定窗口标题 defalut->show(); return app.exec(); }
其次是scenemode.cpp
#include "scenemode.h" #include
#include SceneMode::SceneMode(QWidget *parent) :QWidget(parent) { QWidget *container; //isCameraEntityFlag = false; screen = new MyGLWidget(); //先生成window类型,create()了,但此时还未显示出来 rootEntity = new Qt3DCore::QEntity(); initConnection(); this->sizeHint();//初始化窗口大小 container = this->initWidget(); this->initEngine(); this->initCameraEntity(); this->initLight(); this->initFrameGraph(); this->initBasicEntity(); this->setEngine(); this->updateRatio(container); this->initConnection2();//由于camera相关变量需要在initCameraEntity相关函数之后才能调用,所以三个按键与camera的联系不能写进initconnection函数当中(2018年5月4号,今天工作结束) qDebug() << "Basic is OK"; } void SceneMode::initConnection() { connect(screen,SIGNAL(cameraLevel(int)),this,SLOT(changeCameraLevel(int)));//滚轮(screen.h)改变视角(changeCameraLevel中实现,还没看)--看到这里2018年3月18号 // connect(view1,SIGNAL(clicked(bool)),this,SLOT(sysView())); // connect(view2,SIGNAL(clicked(bool)),this,SLOT(picView())); // connect(view3,SIGNAL(clicked(bool)),this,SLOT(userView())); } void SceneMode::initConnection2() { connect(view1,SIGNAL(clicked(bool)),this,SLOT(sysView())); connect(view2,SIGNAL(clicked(bool)),this,SLOT(picView())); connect(view3,SIGNAL(clicked(bool)),this,SLOT(userView())); connect(pic1,SIGNAL(picModes(int)),this,SLOT(pressx(int))); } QWidget* SceneMode::initWidget() { QHBoxLayout* displaylayout = new QHBoxLayout(this);//changed QVBoxLayout* buttongroup = new QVBoxLayout; displaylayout->setMargin(6);//控件与窗体的左右边距是6个坐标轴单位据 this->setLayout(displaylayout); QWidget *container = QWidget::createWindowContainer(screen); //将window类放入容器里 QSize screenSize = screen->screen()->size(); container->setMinimumSize(QSize(100,100)); container->setMaximumSize(screenSize); displaylayout->addWidget(container,1);//使得displaylayout能显示3D container->show(); //最后再显示存放绘图的window类的容器显示出来 return container; } void SceneMode::initEngine() { QVariantMap data; this->engine = new Qt3DCore::QAspectEngine(); this->engine->registerAspect(new Qt3DRender::QRenderAspect()); this->input = new Qt3DInput::QInputAspect(); this->engine->registerAspect(this->input); data.insert(QStringLiteral("surface"), QVariant::fromValue(static_cast (this->screen))); data.insert(QStringLiteral("eventSource"), QVariant::fromValue(this->screen)); this->engine->setData(data);//89到96行尚未攻克 2018年3月20日 } void SceneMode::initCameraEntity() { //qDebug() << "Camera is OK"; cameraEntity = new Qt3DCore::QCamera(rootEntity); cameraEntity->setObjectName(QStringLiteral("cameraEntity")); //qDebug() << "1"; cameraEntity->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);// void setPerspectiveProjection(float fieldOfView, float aspect,float nearPlane, float farPlane); yzaxisDelta = QVector3D(-7,-10,9);//调试出来一个比较好的结果 camerapos = QVector3D(100,150,-90.0f); camCenter = QVector3D(0,0,0); // camera3dPosition = QVector3D(800.0f, 2000.0f, 4000.0f); // camera3dCenter = QVector3D(800.0f, 0.0f, 0.0f); cameraEntity->setPosition(camerapos);//cameraEntity->setPosition(QVector3D(100, 150, -90.0f)); cameraEntity->setUpVector(QVector3D(0, 1, 0)); //UpVector表示了你在建立视点坐标系时的摄像机向上的方向,决定了坐标系该轴的正值延伸方向(取±1)。 //levelDelta = QVector3D(0.0f, 0.5f, 0.0f); //yaxisDelta = QVector3D(0.0f, 0.0f, 100.0f); //视角中心与相机位置的差值 cameraEntity->setViewCenter(camCenter); //摄影机投射的中心点,cameraEntity->setViewCenter(QVector3D(0,0,0)); input->setCamera(cameraEntity); } void SceneMode::initLight() { Qt3DRender::QSphereMesh* lightMesh = new Qt3DRender::QSphereMesh(rootEntity); //设置光源:把光源附着在某一Entity上就可以确定移动位置 lightMesh->setRings(1); lightMesh->setSlices(1); lightMesh->setRadius(1); Qt3DRender::QSpotLight* spotLight = new Qt3DRender::QSpotLight(rootEntity); //可以共用 //SpotLight聚光灯源设置方向、截止角、强度、衰减因子 spotLight->setColor(Qt::white); //DirectionalLight平行光源设置方向、强度、衰减因子 spotLight->setDirection(QVector3D(0,0,0)); //PointLight点光源设置强度、衰减因子 spotLight->setIntensity(5); //位置都有附着的实体Entity决定,表示强度 spotLight->setCutOffAngle(360); spotLight->setAttenuation(QVector3D(0,0.02,0)); //衰减系数和强度是配套的 Qt3DCore::QTransform* lifht1Transform = new Qt3DCore::QTransform(rootEntity); //光源位置 lifht1Transform->setTranslation(QVector3D(0.0f, 10000.0f, 0.0f));//向y轴正方向平移十个单位 } void SceneMode::initFrameGraph() { Qt3DRender::QFrameGraph *frameGraph = new Qt3DRender::QFrameGraph(); Qt3DRender::QForwardRenderer *forwardRenderer = new Qt3DRender::QForwardRenderer(); forwardRenderer->setCamera(cameraEntity); forwardRenderer->setClearColor(QColor(QRgb(0xffffff))); frameGraph->setActiveFrameGraph(forwardRenderer); rootEntity->addComponent(frameGraph); } void SceneMode::initBasicEntity()//设定基本实体 { // Torus shape data //! [0] m_torus = new Qt3DRender::QTorusMesh(); m_torus->setRadius(1.0f);//圆环大小 m_torus->setMinorRadius(0.2f);//应该是从外向里的扩展大小,数越小则窟窿越大 m_torus->setRings(3);//设定多少等边形 m_torus->setSlices(300);//透明度设置,越大越不透明 //! [0] // TorusMesh Transform //! [1] Qt3DCore::QTransform *torusTransform = new Qt3DCore::QTransform(); torusTransform->setScale(3.0f);//放缩比例,越大则原有图像越大 torusTransform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(0.0f, 1.0f, 0.0f), 55.0f));//最后一个参数设置旋转角度 torusTransform->setTranslation(QVector3D(4.0f, 4.0f, 10.0f));//x表示左右,y是上下,z表示前后(左正右负,上正下负,后正前负) //! [1] //! [2] Qt3DRender::QPhongMaterial *torusMaterial = new Qt3DRender::QPhongMaterial(); torusMaterial->setDiffuse(QColor(QRgb(0xbeb3FF)));//设置颜色 //! [2] // Torus //! [3] m_torusEntity = new Qt3DCore::QEntity(m_rootEntity); m_torusEntity->addComponent(m_torus); m_torusEntity->addComponent(torusMaterial); m_torusEntity->addComponent(torusTransform); //! [3] // Cylinder shape data Qt3DRender::QCylinderMesh *cylinder = new Qt3DRender::QCylinderMesh(); cylinder->setRadius(1); cylinder->setLength(3); cylinder->setRings(100); cylinder->setSlices(20); // CylinderMesh Transform Qt3DCore::QTransform *cylinderTransform = new Qt3DCore::QTransform(); cylinderTransform->setScale(1.5f); cylinderTransform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(0.0f, 0.0f, 1.0f), 90.0f));//x指的是单个物体的前后,y指的是上下,z指的是物体的左右,最后的向量表示旋转的角度 cylinderTransform->setTranslation(QVector3D(-4.0f, 4.0f, -1.5)); Qt3DRender::QPhongMaterial *cylinderMaterial = new Qt3DRender::QPhongMaterial(); cylinderMaterial->setDiffuse(QColor(QRgb(0x928327))); // Cylinder m_cylinderEntity = new Qt3DCore::QEntity(m_rootEntity); m_cylinderEntity->addComponent(cylinder); m_cylinderEntity->addComponent(cylinderMaterial); m_cylinderEntity->addComponent(cylinderTransform); // Cuboid shape data Qt3DRender::QCuboidMesh *cuboid = new Qt3DRender::QCuboidMesh(); // CuboidMesh Transform Qt3DCore::QTransform *cuboidTransform = new Qt3DCore::QTransform(); cuboidTransform->setScale(4.0f); cuboidTransform->setTranslation(QVector3D(4.0f, -4.0f, 0.0f)); Qt3DRender::QPhongMaterial *cuboidMaterial = new Qt3DRender::QPhongMaterial(); cuboidMaterial->setDiffuse(QColor(QRgb(0x665423))); //Cuboid m_cuboidEntity = new Qt3DCore::QEntity(m_rootEntity); m_cuboidEntity->addComponent(cuboid); m_cuboidEntity->addComponent(cuboidMaterial); m_cuboidEntity->addComponent(cuboidTransform); // Sphere shape data Qt3DRender::QSphereMesh *sphereMesh = new Qt3DRender::QSphereMesh(); sphereMesh->setRings(20); sphereMesh->setSlices(20); sphereMesh->setRadius(2); // Sphere mesh transform Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform(); sphereTransform->setScale(1.3f); sphereTransform->setTranslation(QVector3D(-4.0f, -4.0f, 0.0f)); Qt3DRender::QPhongMaterial *sphereMaterial = new Qt3DRender::QPhongMaterial(); sphereMaterial->setDiffuse(QColor(QRgb(0xa69929))); // Sphere m_sphereEntity = new Qt3DCore::QEntity(m_rootEntity); m_sphereEntity->addComponent(sphereMesh); m_sphereEntity->addComponent(sphereMaterial); m_sphereEntity->addComponent(sphereTransform); //sphere shape data2 Qt3DRender::QSphereMesh *sphereMesh2 = new Qt3DRender::QSphereMesh(); sphereMesh2->setRings(100); sphereMesh2->setSlices(10); sphereMesh2->setRadius(1); //Sphere transformation // Qt3DCore::QTransform *sphereTransform2 = new Qt3DCore::QTransform(); // sphereTransform2->setScale(2.0f); // sphereTransform2->setTranslation(QVector3D(-16.0f,-8.0f,2.0f)); Qt3DRender::QPhongMaterial *sphereMaterial2 = new Qt3DRender::QPhongMaterial(); sphereMaterial2->setDiffuse(QColor(QRgb(0xFFFFFF))); //加载预置大小,材料以及位置参数 //t_sphereEntity = new Qt3DCore::QEntity(m_rootEntity); //t_sphereEntity->addComponent(sphereMesh2); //t_sphereEntity->addComponent(sphereMaterial2); //t_sphereEntity->addComponent(sphereTransform2); carEntity1 = new carEntity(m_rootEntity); carEntity1->transform()->setScale3D(QVector3D(10,10,10)); carEntity1->transform()->setTranslation(QVector3D(0,0,0)); } void SceneMode::updateRatio(QWidget* ctn) { QSize widgetSize = ctn->size(); float aspectRatio = float(widgetSize.width()) / float(widgetSize.height()); cameraEntity->lens()->setPerspectiveProjection(60.0f, aspectRatio, 0.1f, 100000.0f); } QSize SceneMode::sizeHint() const { return QSize(1024*3, 768*3); } void SceneMode::setEngine() { this->engine->setRootEntity(rootEntity); } SceneMode::~SceneMode() { } 经过这样的操作之后,就可以把原来的main当中程序塞进scenemode当中,完成了对代码可移植性的改造。这里面用到了this,指的是当前操作类,是C++当中的一个关键字。
下面就来讨论一下关于3DMax当中的“导出”功能与QT 3D场景的一个简单交互。正如OpenGL对于新手要求较高,所以对于3D建模的新手而言3DMax无疑是更好上手的,并且3DMax网上的素材也较多,可以把一部分3D建模的工作外放,减轻了工作量。那么来讨论一下由3DMax导出的OBJ文件是如何在QT上得到应用的。
QT当中支持3D文件的导入和贴图的导入,不过你要是想导入的话得先在QT所在工程当中添加资源,可不是直接把资源所在地址告知软件就可以大功告成的,这一点曾经折磨了我很长一段时间,因为当时我设定的实体就是不能在画布当中得到显示。
具体的添加方式:首先右键工程,点击“添加新文件”,然后选择“Qt"一栏的"Qt resource file",设定前缀,并将所需文件添加进去就可以了。使用的时候都会有着自己的函数去进行使用,例如本文当中添加实体的OBJ文件就是在方法中对“mesh”进行添加,用到的是“setSource”函数,setSource函数后面的参数是QUrl类型的,在这个类型下用字符串去指定OBJ文件所在的位置,注意,这里面所在的位置并不是OBJ文件在计算机当中的位置,而是在工程当中的相对位置,这个文件在什么前缀当中,在哪个文件夹当中,名字是什么,是什么类型的都要在字符串当中一一进行指明。大体上就是如下面这条伪码所写的setSource(QUrl("qrc:/前缀/文件夹(可能不止一个)/文件名.文件类型))。
那么,对于最简单的3D建模实体的构造主要由以下几个方面构成,分别是mesh、transform和material,分别是渲染、位置以及材料,第一项加载OBJ文件,第二项在顶层画布当中调用,设定在画布当中坐标系的位置,第三项则负责调色,当然有条件的话就可以贴图以达到更加逼真的效果。
而对于一个3D文件如何使用3DMax导出我们需要的OBJ文件呢?方法就是使用“导出”,导出选定对象,就可以在导出格式当中选定OBJ文件进行导出了。要是想对一个3D实体的不同组成部分进行分别渲染,那么就需要将原有3D图形进行解组,然后再分别将其导出,只要各个part之间的相对位置没有改变,那么在顶层画布当中仅需将它们设定在同一坐标下就可以完成解组后的再次组合。