一、 设计思路
1. 用例图管理
2.困惑点:
(1) 在程序编写中,为什么主程序的Shader可以关联到model类中的数据。
解决:在加载数据方面,利用VBO、VAO等,已经将数据通过处理推到GPU上。
二、 代码实现
GLHeader.h
#pragma once #include <QOpenGLShaderProgram> #include <QMatrix4x4> #include <QOpenGLExtraFunctions> #include <QOpenGLBuffer> #include <QOpenGLTexture> #include <QOpenGLContext> #ifdef _DEBUG #include <QOpenGLDebugLogger> #endif #define GLFuncName QOpenGLExtraFunctionsView Code
main.cpp
#include <qapplication.h> #include "widget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }View Code
widget.h
#pragma once #ifndef WIDGET_H #define WIDGET_H #include <QOpenGLWidget> #include <QOpenGLExtraFunctions> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLVertexArrayObject> #include <QOpenGLTexture> #include <QTimer> #include <QTime> #include <QtMath> #include <memory> #include "model.h" #include "Camera.h" class Widget : public QOpenGLWidget, public QOpenGLExtraFunctions { Q_OBJECT public: Widget(QWidget* parent = 0); ~Widget(); protected: virtual void initializeGL() override; virtual void resizeGL(int w, int h) override; virtual void paintGL() override; virtual bool event(QEvent* e) override; private: std::shared_ptr<model> m_cube = nullptr; QVector<QVector3D> cubePositions; QOpenGLShaderProgram shaderProgram; QOpenGLVertexArrayObject VAO; QTimer timer; //QVector3D cameraPos; //QVector3D cameraTarget; //QVector3D cameraDirection; //QVector3D cameraRight; //QVector3D cameraUp; Camera camera; }; #endif // WIDGET_HView Code
widget.cpp
#include "widget.h" #include <QtMath> Widget::Widget(QWidget* parent) : QOpenGLWidget(parent) ,camera(this) { cubePositions = { { 0.0f, 0.0f, 0.0f }, { 2.0f, 5.0f, -15.0f }, {-1.5f, -2.2f, -2.5f }, {-3.8f, -2.0f, -12.3f }, { 2.4f, -0.4f, -3.5f }, {-1.7f, 3.0f, -7.5f }, { 1.3f, -2.0f, -2.5f }, { 1.5f, 2.0f, -2.5f }, { 1.5f, 0.2f, -1.5f }, {-1.3f, 1.0f, -1.5f }, }; timer.setInterval(18); connect(&timer, &QTimer::timeout, this, static_cast<void (Widget::*)()>(&Widget::update)); timer.start(); } Widget::~Widget() { makeCurrent(); m_cube = nullptr; doneCurrent(); } void Widget::initializeGL() { this->initializeOpenGLFunctions(); //初始化opengl函数 m_cube = std::make_shared<model>(this); camera.init(); } void Widget::resizeGL(int w, int h) { this->glViewport(0, 0, w, h); //定义视口区域 } void Widget::paintGL() { this->glClearColor(0.1f, 0.5f, 0.7f, 1.0f); //设置清屏颜色 this->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色缓存 float time = QTime::currentTime().msecsSinceStartOfDay() / 1000.0; shaderProgram.bind(); //使用shaderProgram着色程序 QMatrix4x4 projection; projection.perspective(45.0f, width() / (float)height(), 0.1f, 100.0f); QOpenGLVertexArrayObject::Binder{ &VAO }; for (unsigned int i = 0; i < 10; i++) { QMatrix4x4 model; model.translate(cubePositions[i]); model.rotate(180 * time + i * 20.0f, QVector3D(1.0f, 0.5f, 0.3f)); m_cube->draw(shaderProgram, model, camera.getView(), projection); } } bool Widget::event(QEvent* e) { camera.handle(e); return QWidget::event(e); //调用父类的事件分发函数 }View Code
model.h
#pragma once #include"GLHeader.h" #include <QOpenGLVertexArrayObject> #include <QTime> class model { public: model(GLFuncName* func); ~model(); void draw(QOpenGLShaderProgram& program, const QMatrix4x4& model, const QMatrix4x4& view, const QMatrix4x4& project); QVector<float> vertices; QVector<QVector3D> cubePositions; QOpenGLShaderProgram myprogram; QOpenGLBuffer VBO; QOpenGLVertexArrayObject VAO; QOpenGLTexture texture; QOpenGLTexture texture1; private: void initBuffer(); void initShader(); void initTexture(); private: GLFuncName* m_func; };View Code
model.cpp
#include "model.h" model::model(GLFuncName* func) : m_func(func) , texture(QOpenGLTexture::Target2D) , texture1(QOpenGLTexture::Target2D) , VBO(QOpenGLBuffer::VertexBuffer) { initShader(); initBuffer(); initTexture(); } model::~model() { VBO.destroy(); texture.destroy(); texture1.destroy(); } void model::draw(QOpenGLShaderProgram& program, const QMatrix4x4& model, const QMatrix4x4& view, const QMatrix4x4& project) { program.removeAllShaders(); program.addShaderFromSourceFile(QOpenGLShader::Vertex, "./triangle.vert"); program.addShaderFromSourceFile(QOpenGLShader::Fragment, "./triangle.frag"); if (!program.link()) { qWarning() << program.log(); return; } if (!program.bind()) { qWarning() << program.log(); return; } program.setUniformValue("projection", project); program.setUniformValue("view", view); program.setUniformValue("model", model); texture.bind(0); //将texture绑定到纹理单元0 program.setUniformValue("ourTexture", 0); //让ourTexture从纹理单元0中获取纹理数据 texture1.bind(1); //将texture绑定到纹理单元1 program.setUniformValue("ourTexture1", 1); //让ourTexture从纹理单元1中获取纹理数据 m_func->glDrawArrays(GL_TRIANGLES, 0, 36); } /// <summary> /// 数组初始化,着色器绑定 /// </summary> void model::initBuffer() { VBO.create(); //生成VBO对象 vertices = { -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.5f, -0.5f, 0.5f, 1.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f }; QOpenGLVertexArrayObject::Binder{ &VAO }; VBO.bind(); //将VBO绑定到当前的顶点缓冲对象(QOpenGLBuffer::VertexBuffer)中 //将顶点数据分配到VBO中,第一个参数为数据指针,第二个参数为数据的字节长度 VBO.allocate(vertices.data(), sizeof(float) * vertices.size()); //设置顶点解析格式,并启用顶点 myprogram.setAttributeBuffer("aPos", GL_FLOAT, 0, 3, sizeof(GLfloat) * 5); myprogram.enableAttributeArray("aPos"); myprogram.setAttributeBuffer("aTexCoord", GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(GLfloat) * 5); myprogram.enableAttributeArray("aTexCoord"); m_func->glEnable(GL_DEPTH_TEST); } /// <summary> /// 着色器编写 /// </summary> void model::initShader() { if (!myprogram.addShaderFromSourceFile(QOpenGLShader::Vertex, "./triangle.vert")) { //添加并编译顶点着色器 qDebug() << "ERROR:" << myprogram.log(); //如果编译出错,打印报错信息 } if (!myprogram.addShaderFromSourceFile(QOpenGLShader::Fragment, "./triangle.frag")) { //添加并编译片段着色器 qDebug() << "ERROR:" << myprogram.log(); //如果编译出错,打印报错信息 } if (!myprogram.link()) { //链接着色器 qDebug() << "ERROR:" << myprogram.log(); //如果链接出错,打印报错信息 } } /// <summary> /// 纹理初始化 /// </summary> void model::initTexture() { texture.setData(QImage("./RC.jpg").mirrored()); texture.setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear); texture.setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); texture.setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); texture1.setData(QImage(":/sea.jpg").mirrored()); texture1.setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear); texture1.setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); texture1.setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); }View Code
Camera.h
#pragma once #include <QSet> #include <QVector3D> #include <QEvent> #include <QWidget> #include <QtMath> #include <QMatrix4x4> #include <QKeyEvent> #include <QTime> class Camera { public: Camera(QWidget* widget); float getMoveSpeed() const; void setMoveSpeed(float value); float getSensitivity() const; void setSensitivity(float value); float getYaw() const; void setYaw(float value); float getPitch() const; void setPitch(float value); QVector3D getCameraPos() const; void setCameraPos(const QVector3D& value); void init(); //初始化摄像机 void handle(QEvent* event); //处理窗口事件 QMatrix4x4 getView() const; //获取观察矩阵 qreal m_aspect = 45; QQuaternion m_rotation; private: QWidget* widget; float yaw = -90; //偏航角 float pitch = 0; //俯视角 float sensitivity; //鼠标灵敏度 QPoint m_last; QVector2D m_mousePressPosition; bool m_keys[1024] = { false }; QVector3D m_rotationAxis; float m_angularSpeed = 0; qreal m_elapsed = 100; QVector3D cameraPos; //摄像机初始位置 QVector3D cameraDirection; //摄像机方向 QVector3D cameraTarget; QVector3D cameraRight; //摄像机右向量 QVector3D cameraUp; //摄像机上向量 float moveSpeed; //控制移动速度 QSet<int> keys; //记录当前被按下按键的集合 int timeId; //定时器id:此定时器用于完成键盘移动事件 float deltaTime; // 当前帧与上一帧的时间差 float lastFrame; // 上一帧的时间 QMatrix4x4 view; //观察矩阵 };View Code
Camera.cpp
#include "Camera.h" Camera::Camera(QWidget* widget) : widget(widget) , yaw(0) , pitch(0) , sensitivity(0.005f) , cameraPos(0.0f, 0.0f, 10.0f) , cameraDirection(0.0f, 0.0f, -1.0f) , cameraTarget(0.0f, 0.0f, 0.0f) , cameraRight(QVector3D::crossProduct({ 0.0f,1.0f,0.0f }, cameraDirection)) , cameraUp(QVector3D::crossProduct(cameraDirection, cameraRight)) , moveSpeed(0.5f) , timeId(0) { } float Camera::getMoveSpeed() const { return moveSpeed; } void Camera::setMoveSpeed(float value) { moveSpeed = value; } float Camera::getSensitivity() const { return sensitivity; } void Camera::setSensitivity(float value) { sensitivity = value; } float Camera::getYaw() const { return yaw; } void Camera::setYaw(float value) { yaw = value; } float Camera::getPitch() const { return pitch; } void Camera::setPitch(float value) { pitch = value; } QVector3D Camera::getCameraPos() const { return cameraPos; } void Camera::setCameraPos(const QVector3D& value) { cameraPos = value; } void Camera::handle(QEvent* e) { //关闭鼠标跟踪才能正确显示鼠标点击、移动和释放 if (e->type() == QEvent::MouseButtonPress) { QMouseEvent* event = static_cast<QMouseEvent*>(e); m_last = event->pos(); //获取当前位置 m_mousePressPosition = QVector2D{ event->localPos() }; //以float型存储当前位置 } else if (e->type() == QEvent::MouseMove) { QMouseEvent* event = static_cast<QMouseEvent*>(e); QPointF diff = event->pos() - m_last; //计算位置 m_last = event->pos(); //更新当前鼠标位置 qreal sensitivity = 0.05; //灵敏度 qreal xoffset = diff.x() * sensitivity; //计算x偏移向量 qreal yoffset = diff.y() * sensitivity; //计算y偏移向量 //弧度与角度的转换会使得物体的平移产生问题 yaw += xoffset; pitch += yoffset; if (pitch > 89.0) //将俯视角限制到[-89°,89°],89°约等于1.55 pitch = 89.0; if (pitch < -89.0) pitch = -89.0; QVector3D front; front.setX(cos(qDegreesToRadians(yaw)) * cos(qDegreesToRadians(pitch))); front.setY(sin(qDegreesToRadians(pitch))); front.setZ(sin(qDegreesToRadians(yaw)) * cos(qDegreesToRadians(pitch))); cameraDirection = front.normalized(); view.setToIdentity(); view.lookAt(cameraPos, cameraPos + cameraDirection, cameraUp); } else if (e->type() == QEvent::Wheel) { QWheelEvent* event = static_cast<QWheelEvent*>(e); int offset = event->angleDelta().y() < 0 ? -1 : 1; qreal speed = 10; if (1 <= m_aspect + offset * speed && m_aspect + offset * speed <= 45) { m_aspect = m_aspect + offset * speed; } event->accept(); } else if (e->type() == QEvent::MouseButtonRelease) { QMouseEvent* event = static_cast<QMouseEvent*>(e); QVector2D diff = QVector2D{ event->localPos() } - m_mousePressPosition; QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized(); float acc = diff.length() / 100.0f; m_rotationAxis = (m_rotationAxis * m_angularSpeed + n * acc).normalized(); m_angularSpeed += acc; //获取当前位置 } else if (e->type() == QEvent::Timer) { float cameraSpeed = moveSpeed * m_elapsed / 1000.0; if (keys.contains(Qt::Key_W)) //前 { cameraPos += cameraSpeed * cameraDirection; } if (keys.contains(Qt::Key_S)) //后 { cameraPos -= cameraSpeed * cameraDirection; } if (keys.contains(Qt::Key_A)) //左 { cameraPos += QVector3D::crossProduct(cameraDirection, cameraUp).normalized() * cameraSpeed; } if (keys.contains(Qt::Key_D)) //右 { cameraPos -= QVector3D::crossProduct(cameraDirection, cameraUp).normalized() * cameraSpeed; } if (keys.contains(Qt::Key_Space)) //上浮 { cameraPos.setY(cameraPos.y() + cameraSpeed); } if (keys.contains(Qt::Key_Shift)) //下沉 { cameraPos.setY(cameraPos.y() - cameraSpeed); } view.setToIdentity(); view.lookAt(cameraPos, cameraPos + cameraDirection, cameraUp); } else if (e->type() == QEvent::KeyPress) { //isAutoRepeat用于判断此按键的来源是否是长按 QKeyEvent* event = static_cast<QKeyEvent*>(e); keys.insert(event->key()); //添加按键 if (!event->isAutoRepeat() && timeId == 0) { //如果定时器未启动,则启动定时器 timeId = widget->startTimer(1); } } else if (e->type() == QEvent::KeyRelease) { QKeyEvent* event = static_cast<QKeyEvent*>(e); keys.remove(event->key()); if (!event->isAutoRepeat() && timeId != 0 && keys.empty()) { //当没有按键按下且定时器正在运行,才关闭定时器 widget->killTimer(timeId); timeId = 0; //重置定时器id } } } void Camera::init() { view.lookAt(cameraPos, cameraPos + cameraDirection, cameraUp); widget->activateWindow(); //激活窗口 widget->setFocus(); } QMatrix4x4 Camera::getView() const { return view; }View Code
triangle.frag
#version 330 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D ourTexture; uniform sampler2D ourTexture1; void main() { FragColor = mix(texture(ourTexture, TexCoord), texture(ourTexture1, TexCoord), 0.4); }View Code
triangle.vert
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { // 注意乘法要从右向左读 gl_Position = projection * view * model * vec4(aPos, 1.0); TexCoord = aTexCoord; }View Code
引用:https://blog.csdn.net/qq_40946921/article/details/105902185
github中的天空模型,忘记网址。
标签:float,1.0,QT,管理,0.0,void,OpenGL,0.5,include From: https://www.cnblogs.com/wxd-/p/17549548.html