利用Qt结合openGL实现简易3D查看器

前言

Win10系统有一款非常优秀的3维模型查看工具3D viewer,功能十分强大。出于学习openGL的目的,采用Qt结合openGL实现3D查看器的部分功能,工程中涉及到的opengl相关代码基本上都是参考opengl中文网站(LearnOpenGL-CN)及网上的例子来写的。本文主要阐述工程中遇到的问题及解决方案,内容如下:1、效果及功能介绍;2、遇到的问题及解决方案;3、工程源码及说明。

一、效果及功能介绍

自制的3d查看器主要包括模型加载、修改背景颜色及动画等功能。动画效果如下图,工程中所有的3d模型均从Windows自带的3D查看器中的3D资源库中下载。模型格式都是glb格式,纹理数据直接包含在模型中,其他格式下的模型纹理数据可能用单独的文件存放,工程应该也能支持加载(实际没测试过)。代码主要三方库assimp(Releases · assimp/assimp (github.com))进行3d模型加载,通过三方库stb(项目概览 - stb - GitCode)进行图像加载。首先调用assimp库的GetEmbeddedTexture函数,若返回的aiTexture对象非空,表明模型包含纹理数据,调用stb库的stbi_load_from_memory函数从内存中加载纹理,否则通过stbi_load函数从指定路径中加载纹理。

arduino 复制代码
const aiTexture* GetEmbeddedTexture(const char* filename) const

二、遇到的问题及解决方案

1、模型比较大时,图像显示不全以及有黑色锯齿

openGL的坐标系包括三个部分:模型矩阵、观察矩阵和投影矩阵,learnopengl网站也对此进行了详细权威的说明。模型的旋转、缩放及移动,一般都只涉及到前面两个矩阵即可。投影矩阵一般只在resizeGL函数中调用:

java 复制代码
void perspective(float verticalAngle, float aspectRatio, float nearPlane, float farPlane)

第一个参数表示视野(Field of View)的意思,表明了观察空间的大小,对于一个真实的观察效果,其值经常设置为45°;第二个参数表示窗口宽高比,直接传入widget的宽高即可;第三和第四个参数设置了平截头体的近和远平面,教程上建议我们分别设置为0.1与100.0,实际上这两个参数最好不要写死,这两个参数控制了控制了视野的最小、大距离。如果加载的模型的尺寸很大,参数farPlane设置比较小,就会出现模型显示不全的问题;同理,参数nearPlane设置比较小,就会出现模型有黑色锯齿的问题,当然参数nearPlane设置太大时,会导致相机太靠近一个物体时会直接穿过去。因此nearPlane, farPlane要根据模型的尺寸来确定,调整后,工程能支持1~3000尺寸范围内的模型正确导入,包括滚轮的滚动系数的确定,应该也与模型尺寸有关系。确定最小、大视野距离的逻辑如下,感觉这里应该存在相应的一个数学公式。

ini 复制代码
float maxPosition = 计算模型最大尺寸();
if (maxPosition < 5) {
    m_camera.m_zNear = 0.01;
    m_camera.m_zFar = maxPosition * 100;
}
else if (maxPosition < 100) {
    m_camera.m_zNear = 0.05;
    m_camera.m_zFar = maxPosition * 50;
}
else if (maxPosition < 1000) {
    m_camera.m_zNear = 0.05;
    m_camera.m_zFar = maxPosition * 30;
}
else if (maxPosition < 2000) {
    m_camera.m_zNear = 0.07;
    m_camera.m_zFar = maxPosition * 10;
}
else if (maxPosition < 3000) {
    m_camera.m_zNear = 0.4;
    m_camera.m_zFar = maxPosition * 10;
}
else {
    m_camera.m_zNear = 0.5;
    m_camera.m_zFar = maxPosition * 10;
}
//resizeGL函数
void OpenGLWindow::resizeGL(int w, int h)
{
    qreal aspect = qreal(w) / qreal(h ? h : 1);
    m_camera.m_projection.setToIdentity();
    m_camera.m_projection.perspective(m_camera.m_fovy, aspect, m_camera.m_zNear, m_camera.m_zFar);
}

2 纹理坐标不对导致纹理贴图错乱

三方库stb中,有一个stbi_set_flip_vertically_on_load函数,用于翻转y轴坐标。在片段找色器语言代码中一般会对纹理坐标进行赋值操作,这两部分是相关联的,对应不上就会导致纹理错乱。

csharp 复制代码
//stb
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)

//片段找色器代码
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D texture_diffuse1;
void main()
{    
    FragColor = texture(texture_diffuse1, TexCoords);
}

3 帧率fps获取方式

不要在paintGL函数中,通过QPainter.drawText的方式直接将文本绘制在窗口上,这样会影响到opengl模型对象的显示,将文本绘制在QLabel(opengl窗口作为其父对象)上再show出来。帧率如果直接在paintGL函数中计算,可能会非常不准确,这里通过定时器的方式来获取帧率(间隔1s),但会损耗部分性能。

4 其他

1、代码中有一个宏START_INFO_CONSOLE,控制是否开启debug控制台,用于观察是否有报错日志,打开后会产生两个窗口,日志通过三方库spdlogl来实现。 2、工程中有一个Tree with Falling Leaves.glb的模型加载出来很奇怪,目前还不清楚。

三、工程源码及说明

工程源码路径:1995zyl/3D-viewer: This is a project based on qt opengl for viewing 3D models (github.com) 由于三方库只编译了Windows的release版,工程只能运行在Windows x64 RelWithDebInfo/Release下。本地只需要安装cmake工具与visual studio,不需要Qt环境,qt相关依赖以三方库的形式上传到该工程的third_libray目录了。cmake版本为3.23左右,vs版本为vs2022左右,其他见工程readme文件。

相关推荐
爱吃生蚝的于勒2 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
小白学大数据3 小时前
Python爬虫开发中的分析与方案制定
开发语言·c++·爬虫·python
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
杜杜的man5 小时前
【go从零单排】Closing Channels通道关闭、Range over Channels
开发语言·后端·golang
java小吕布5 小时前
Java中Properties的使用详解
java·开发语言·后端
versatile_zpc6 小时前
C++初阶:类和对象(上)
开发语言·c++
小鱼仙官6 小时前
MFC IDC_STATIC控件嵌入一个DIALOG界面
c++·mfc
神仙别闹6 小时前
基本MFC类框架的俄罗斯方块游戏
c++·游戏·mfc
2401_857610036 小时前
Spring Boot框架:电商系统的技术优势
java·spring boot·后端