嵌入式系统大作业——基于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中更好读取与展示。由于原模型文件太大,在转换时还需要进行简化处理。

编写代码实现功能:

  1. 编写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.整体文件夹架构:

qt unity_ide

效果演示

“File->Open 打开一个3D模型文件. \n”
“利用方向键可平移模型. \n”
“利用shift + 方向键可缩放模型. \n”
“利用ctrl + 方向键可旋转模型. \n”
“利用右侧的滑块可调整灯光位置.”

qt unity_ide_02


注意:本文使用的模型是处理后的.obj格式,模型不予提供。


参考资料

https://github.com/reybar/3DModelViewer