将learningopengl入门篇的Coordinate Systems代码在QT框架下实现,绘制一些带纹理贴图的动态立方体
一、环境
操作系统:Windows 11 IDE:Microsoft Visual Studio Community 2022 (64 位) QT:5.12.12
二、代码实现
learningopengl Hello Triangle章节教程:
https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/
我们要做的是这节教程最后的绘制10个立方体,相应的GLFW框架下的代码:
https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/6.3.coordinate_systems_multiple/coordinate_systems_multiple.cpp
对照GLFW框架下代码进行以下修改
glm
和二维渲染不同,在三维空间下显示、渲染涉及到物体的平移、旋转和相机的计算,需要大量的矩阵运算,OpenGL内没有实现矩阵运算的方法,常用的第三方库是GLM,教程中的代码使用的也是这个库
我使用的版本是glm0.9.9.8,下载链接:
https://github.com/g-truc/glm/tags
GLM是一个header only库,只需要包括了相应的头文件就可以使用它提供的类和函数。不需要进行额外的编译,只要设置工程的头文件包含目录即可。
着色器类 shader.h
教程中实现了一个自己的着色器类
https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader.h
这部分代码直接拿过来用,需要进行一些修改
glad->QOpenGLFunctions 对头文件进行替换
#include <QOpenGLFunctions>
QT框架下Shader 类需要继承QOpenGLFunctions 类,才能初始化和使用OpenGL的方法
class Shader : protected QOpenGLFunctions
{
...
...
...
}
初始化OpenGL函数指针,在构造函数最前面调用initializeOpenGLFunctions
Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
{
this->initializeOpenGLFunctions();
...
...
...
}
删除所有utility uniform functions相关方法的const修饰,这个应该是编译器差异的问题
着色器
教程中着色器代码写在了两个文件中
https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/6.3.coordinate_systems_multiple/6.3.coordinate_systems.vs https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/6.3.coordinate_systems_multiple/6.3.coordinate_systems.fs
直接创建两个相同的文件,并确保程序运行时能找到这两个文件
纹理贴图
教程中用到了木箱和笑脸的两个贴图
https://learnopengl-cn.github.io/img/01/06/container.jpg https://learnopengl-cn.github.io/img/01/06/awesomeface.png
将他们下载下来放在resources\textures\ 目录下,下面程序调用时是从这个目录读取
stb_image.h
读取纹理贴图时用到了这个单头像图像加载库stb_image.h
https://github.com/nothings/stb/blob/master/stb_image.h
使用这个库需要两个文件stb_image.h 和stb_image.cpp ,stb_image.h 直接下载,stb_image.cpp 只需要两行代码
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
QOpenGL控件
新建一个QOpenGL控件类CoordinateSystemsMultipleWidget ,继承QOpenGLWidget 类,并重载initializeGL 、resizeGL 、paintGL 三个方法,包含glm的头文件和stb_image.h,代码如下
#include "CoordinateSystemsMultipleWidget.h"
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <qDebug>
CoordinateSystemsMultipleWidget::CoordinateSystemsMultipleWidget(QWidget* parent)
: QOpenGLWidget(parent)
{
}
CoordinateSystemsMultipleWidget::~CoordinateSystemsMultipleWidget()
{
}
void CoordinateSystemsMultipleWidget::initializeGL()
{
this->initializeOpenGLFunctions();
}
void CoordinateSystemsMultipleWidget::resizeGL(int w, int h)
{
glViewport(0.0f, 0.0f, w, h);
}
void CoordinateSystemsMultipleWidget::paintGL()
{
}
初始化
复制将教程中main函数中while (!glfwWindowShouldClose(window)) 之前的初始化代码,找到上一步新建的CoordinateSystemsMultipleWidget 控件类的initializeGL() 方法,在initializeOpenGLFunctions 后粘贴。
1、删除最前面的GLFW初始化的代码和GLAD初始化的代码。
2、替换VAO相关的代码 将
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindVertexArray(0);
分别替换为
QOpenGLVertexArrayObject VAO;
VAO.create();
if(!VAO.isCreated()){
qDebug()<<"vao is not created.";
}
VAO.bind();
VAO.release();
3、stbi_load使用的FileSystem::getPath获取路径的方法报错,删除他们,直接写入字符串即可
渲染 render
复制将教程中main函数中while (!glfwWindowShouldClose(window)) 中渲染部分的代码,粘贴到CoordinateSystemsMultipleWidget 控件类的paintGL() 方法中
1、删除键盘、鼠标时间响应的代码processInput(window) 和最后面glfw相关的两行代码
2、ourShader 、texture1 和texture2 变量报错,将initializeGL() 中他们的定义的移动到函数外或类内,并修改相应的初始化代码和调用代码
3、将SCR_WIDTH 和SCR_HEIGHT 改为this->width() 和this->height()
4、修改glBindVertexArray(VAO); 为VAO.bind();
5、cubePositions 报错,将其定义从initializeGL() 中移动到函数外。
timer刷新和动态
到目前为止,教程中的代码已经全部在QT框架下实现,编译运行就能看到空间中的立方体,表面覆盖了木箱和笑脸的纹理贴图 但是显示的内容是静止的,这是因为在代码中每个立方体的位置和姿态都是静止的,并且我们也没有不断的调用paintGL() 中的渲染代码。
在initializeGL() 方法的最后面开启一个定时器,定时调用update() 方法,实现界面的不断刷新
void CoordinateSystemsMultipleWidget::initializeGL()
{
...
...
...
timer = new QTimer();
timer->setInterval(33);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start();
}
在paintGL() 方法中,在model = glm::translate(model, cubePositions[i]); 后增加一行代码让模型旋转起来
model = glm::rotate(model, angle, glm::vec3(0.5f, 1.0f, 0.0f));
最终paintGL() 方法的代码为:
void CoordinateSystemsMultipleWidget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
ourShader->use();
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), (float)this->width() / (float)this->height(), 0.1f, 100.0f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
ourShader->setMat4("projection", projection);
ourShader->setMat4("view", view);
VAO.bind();
static float angle = 0;
angle += 0.1;
if (angle >= 360)
angle -= 360;
for (unsigned int i = 0; i < 10; i++)
{
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cubePositions[i]);
model = glm::rotate(model, angle, glm::vec3(0.5f, 1.0f, 0.0f));
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
ourShader->setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
}
再次编译运行,界面中的每个立方体都旋转起来了。
相关链接和参考
https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/ https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/6.3.coordinate_systems_multiple/coordinate_systems_multiple.cpp https://blog.csdn.net/qq_42537915/article/details/104146135
|