嵌入式系统大作业——基于QT的3D模型展示
- 写在前面
- 实验设备
- 实现内容
- 实现过程
- 在win10上利用SolidWorks软件对模型进行预处理:
- 编写代码实现功能:
- 效果演示
- 参考资料
写在前面
该大作业是本博主研究生课程所需,结合自身数字孪生方向,在Ubuntu中利用QT实现3D模型的展示,主要功能有3D模型的旋转、放缩、移动和灯光渲染位置的调整。
该文章文末会附上实现代码以及演示视频。编写不易,若有帮助请点赞关注收藏,谢谢!
实验设备
硬件:
电脑 (主频2.8GHz,内存24GB)
软件:
Windows 10 家庭中文版
VMware 16
Ubuntu 22.04.1
SolidWorks 2022
实现内容
在Linux中利用QT设计一款可以显示3D模型的界面,并通过浏览本地文件夹打开指定的3D模型。可以通过键盘对模型进行操作,实现模型的平移、旋转、缩放这三大基本功能,并且可以通过改变灯光位置,在不同角度对模型进行渲染展示。
实现过程
在win10上利用SolidWorks软件对模型进行预处理:
建议将模型转换为.OBJ格式,.OBJ文件是一种文本文件,可以直接用写字板打开进行查看和编辑修改,因此在Linux中更好读取与展示。由于原模型文件太大,在转换时还需要进行简化处理。
编写代码实现功能:
- 编写QT界面程序
modelviewer.cpp
:
#include "modelviewer.h"
#include "ui_modelviewer.h"
#include "scene.h"
ModelViewer::ModelViewer(QWidget *parent) :
QMainWindow (parent),
ui(new Ui::ModelViewer)
{
ui->setupUi(this);
setFocusPolicy(Qt::TabFocus);
//central widget
QWidget *central = new QWidget();
setCentralWidget(central);
//scene
Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow();
QWidget *sceneWidget = QWidget::createWindowContainer(view);
scene = new Scene(view);
view-> installEventFilter(this);
//slider
QSlider *xSlider = createSlider();
QSlider *ySlider = createSlider();
QSlider *zSlider = createSlider();
zSlider->setValue(-1);
connect(xSlider,&QSlider::valueChanged,scene,&Scene::LightXChanged);
connect(ySlider,&QSlider::valueChanged,scene,&Scene::LightYChanged);
connect(zSlider,&QSlider::valueChanged,scene,&Scene::LightZChanged);
//layout
QLabel *xLabel = new QLabel();
QLabel *yLabel = new QLabel();
QLabel *zLabel = new QLabel();
xLabel->setText("X");
yLabel->setText("Y");
zLabel->setText("Z");
QVBoxLayout *xLayout = new QVBoxLayout;
QVBoxLayout *yLayout = new QVBoxLayout;
QVBoxLayout *zLayout = new QVBoxLayout;
xLayout->addWidget(xSlider);
xLayout->addWidget(xLabel);
yLayout->addWidget(ySlider);
yLayout->addWidget(yLabel);
zLayout->addWidget(zSlider);
zLayout->addWidget(zLabel);
QWidget *xWidget = new QWidget();
QWidget *yWidget = new QWidget();
QWidget *zWidget = new QWidget();
xWidget->setLayout(xLayout);
yWidget->setLayout(yLayout);
zWidget->setLayout(zLayout);
xWidget->setFixedWidth(50);
yWidget->setFixedWidth(50);
zWidget->setFixedWidth(50);
QHBoxLayout *sceneLayout = new QHBoxLayout;
sceneLayout->addWidget(sceneWidget);
sceneLayout->addWidget(xWidget);
sceneLayout->addWidget(yWidget);
sceneLayout->addWidget(zWidget);
QWidget *mainWidget = new QWidget();
mainWidget->setLayout(sceneLayout);
QLabel *controlDescription = new QLabel();
controlDescription->setStyleSheet("font: 12pt;");
controlDescription->setText("File->Open 打开一个3D模型文件. \n"
"利用方向键可平移模型. \n"
"利用shift + 方向键可缩放模型. \n"
"利用ctrl + 方向键可旋转模型. \n"
"利用右侧的滑块可调整灯光位置.");
QVBoxLayout *topLayout = new QVBoxLayout;
topLayout->addWidget(mainWidget);
topLayout->addWidget(controlDescription);
central->setLayout(topLayout);
}
QSlider *ModelViewer::createSlider()
{
QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(-180, 180);
slider->setSingleStep(1);
slider->setPageStep(15);
slider->setTickInterval(15);
slider->setTickPosition(QSlider::TicksRight);
return slider;
}
ModelViewer::~ModelViewer()
{
delete ui;
}
bool ModelViewer::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress){
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
scene->KeyControls(keyEvent);
}
return QObject::eventFilter(obj, event);
}
void ModelViewer::on_actionOpen_File_triggered()
{
QString filename= QFileDialog::getOpenFileName(this,"Open file");
QFile file(filename);
currentFile = filename;
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this,"Warning", "Cannot open "+file.errorString());
}
setWindowTitle(filename);
Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh();
QUrl data =QUrl::fromLocalFile(filename);
mesh->setSource(data);
scene->NewScene(mesh);
}
2.编写界面程序头文件modelviewer.h
:
#ifndef MODELVIEWER_H
#define MODELVIEWER_H
#include <QMainWindow>
#include <Qt3DCore/QEntity>
#include <Qt3DWindow>
#include <QKeyEvent>
#include <QFile>
#include <QFileDialog>
#include <QString>
#include <QMessageBox>
#include <QMesh>
#include <QUrl>
#include <QSlider>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <scene.h>
namespace Ui {
class ModelViewer;
}
class ModelViewer : public QMainWindow
{
Q_OBJECT
public:
explicit ModelViewer(QWidget *parent = nullptr);
~ModelViewer();
void SetLayout();
protected:
bool eventFilter(QObject *obj, QEvent *event);
signals:
void KeyPressed();
private slots:
void on_actionOpen_File_triggered();
private:
Ui::ModelViewer *ui;
QSlider *createSlider();
Scene *scene;
QString currentFile = "";
};
#endif // MODELVIEWER_H
3.编写场景3D模型控制程序scene.cpp
:
#include "scene.h"
#include "modelviewer.h"
Scene::Scene(Qt3DExtras::Qt3DWindow *view, QObject *parent) : QObject (parent)
{
rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
view->defaultFrameGraph()->setClearColor(QColor(QRgb(0x4d4d9f)));//0x42f4bc
Camera(view);
StartScene();
}
void Scene::Camera(Qt3DExtras::Qt3DWindow *view)
{
Qt3DRender::QCamera *camera = view->camera();
camera->lens()->setPerspectiveProjection(45.0f,16.0f/9.0f, 0.1f, 10000.0f);
camera->setPosition(QVector3D(0, 0, 4000));
camera->setViewCenter(QVector3D(0, 0, 0));
}
void Scene::StartScene()
{
model = new Qt3DCore::QEntity(rootEntity);
Qt3DExtras::QTorusMesh *torusMesh = new Qt3DExtras::QTorusMesh(model);
torusMesh->setRadius(5);
torusMesh->setMinorRadius(1);
torusMesh->setRings(100);
torusMesh->setSlices(20);
model->addComponent(torusMesh);
SetupTransform();
SetupMaterial();
SetupLighting();
}
void Scene::NewScene(Qt3DRender::QMesh *mesh)
{
delete model;
model = new Qt3DCore::QEntity(rootEntity);
SetupMesh(mesh);
SetupTransform();
SetupMaterial();
SetupLighting();
}
void Scene::SetupMesh(Qt3DRender::QMesh *mesh)
{
model->addComponent(mesh);
}
void Scene::SetupTransform()
{
transform = new Qt3DCore::QTransform(model);
transform->setScale3D(QVector3D(1, 1, 1));
transform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), 0));
model->addComponent(transform);
}
void Scene::SetupMaterial()
{
Qt3DRender::QMaterial *material = new Qt3DExtras::QPhongMaterial(model);
model->addComponent(material);
}
void Scene::SetupLighting()
{
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity(rootEntity);
light = new Qt3DRender::QDirectionalLight(lightEntity);
light->setColor("white");
light->setIntensity(1);
light->setWorldDirection(QVector3D(0,0,-1));
lightEntity->addComponent(light);
}
void Scene::KeyControls(QKeyEvent *event){
if (event->modifiers().testFlag(Qt::ControlModifier)){
if(event->key()==Qt::Key_Up){
transform->setRotationX(transform->rotationX()-10);
}
if(event->key()==Qt::Key_Down){
transform->setRotationX(transform->rotationX()+10);
}
if(event->key()==Qt::Key_Left){
transform->setRotationY(transform->rotationY()-10);
}
if(event->key()==Qt::Key_Right){
transform->setRotationY(transform->rotationY()+10);
}
}else if (event->modifiers().testFlag(Qt::ShiftModifier)) {
if(event->key()==Qt::Key_Up){
transform->setTranslation(QVector3D(transform->translation().x(),transform->translation().y(),transform->translation().z()-30));
}
if(event->key()==Qt::Key_Down){
transform->setTranslation(QVector3D(transform->translation().x(),transform->translation().y(),transform->translation().z()+30));
}
}else{
if(event->key()==Qt::Key_Up){
transform->setTranslation(QVector3D(transform->translation().x(),transform->translation().y()+30,transform->translation().z()));
}
if(event->key()==Qt::Key_Down){
transform->setTranslation(QVector3D(transform->translation().x(),transform->translation().y()-30,transform->translation().z()));
}
if(event->key()==Qt::Key_Left){
transform->setTranslation(QVector3D(transform->translation().x()-30,transform->translation().y(),transform->translation().z()));
}
if(event->key()==Qt::Key_Right){
transform->setTranslation(QVector3D(transform->translation().x()+30,transform->translation().y(),transform->translation().z()));
}
}
}
void Scene::LightXChanged(int angle)
{
light->setWorldDirection(QVector3D(angle,light->worldDirection().y(),light->worldDirection().z()));
}
void Scene::LightYChanged(int angle)
{
light->setWorldDirection(QVector3D(light->worldDirection().x(),angle,light->worldDirection().z()));
}
void Scene::LightZChanged(int angle)
{
light->setWorldDirection(QVector3D(light->worldDirection().x(),light->worldDirection().y(),angle));
}
4.编写场景模型控制程序头文件scene.h
:
#ifndef SCENE_H
#define SCENE_H
#include <QObject>
#include <QWidget>
#include <Qt3DCore/QEntity>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QPointLight>
#include <Qt3DRender/QDirectionalLight>
#include <Qt3DCore/QTransform>
#include <Qt3DWindow>
#include <Qt3DExtras/QForwardRenderer>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QTorusMesh>
#include <QKeyEvent>
#include <QTransform>
#include <QMesh>
class Scene : public QObject
{
Q_OBJECT
public:
explicit Scene(Qt3DExtras::Qt3DWindow *, QObject *parent=nullptr);
void NewScene(Qt3DRender::QMesh *);
void KeyControls(QKeyEvent *event);
public slots:
void LightXChanged(int angle);
void LightYChanged(int angle);
void LightZChanged(int angle);
private:
Qt3DCore::QEntity *rootEntity;
Qt3DCore::QEntity *model;
Qt3DCore::QTransform *transform;
Qt3DRender::QDirectionalLight *light;
void StartScene();
void Camera(Qt3DExtras::Qt3DWindow *view);
void SetupMesh(Qt3DRender::QMesh *mesh);
void SetupTransform();
void SetupMaterial();
void SetupLighting();
};
#endif // SCENE_H
编辑主函数main.c
:
#include "modelviewer.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ModelViewer *modelViewer =new ModelViewer();
modelViewer->show();
modelViewer->setWindowState(Qt::WindowMaximized);
return a.exec();
}
5.QT配置文件3DModelViewer.pro
:
#-------------------------------------------------
#
# Project created by QtCreator 2019-04-25T13:57:37
#
#-------------------------------------------------
QT += core gui 3dcore 3drender 3dinput 3dextras
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = 3DModelViewer
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
modelviewer.cpp \
scene.cpp
HEADERS += \
modelviewer.h \
scene.h
FORMS += \
modelviewer.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
6.整体文件夹架构:
效果演示
“File->Open 打开一个3D模型文件. \n”
“利用方向键可平移模型. \n”
“利用shift + 方向键可缩放模型. \n”
“利用ctrl + 方向键可旋转模型. \n”
“利用右侧的滑块可调整灯光位置.”
注意:本文使用的模型是处理后的.obj格式,模型不予提供。
参考资料