QT+OpenGL学习笔记(1)

发布于 2024-08-21  2440 次阅读


首先创建一个默认qt widget项目,然后用git做一下版本控制,奇怪的是我github上的项目什么文件都没有,push时却提醒我与最新版本不一致,pull下来时报错,gpt的回答是:

出现这个错误信息 fatal: refusing to merge unrelated histories 是因为你尝试将两个没有共同历史的分支进行合并。这通常发生在仓库的历史在某处被重写或者你克隆了一个全新的仓库并尝试与现有的仓库合并时。

为了解决这个问题,你可以使用 --allow-unrelated-histories 选项来强制合并这两个历史记录不相关的分支。

强行pull下来合并,再push就好了

GISlxz/QT_learn_demo (github.com)

另附github基本教程

【小白向】最新最详细的GitHub全站使用指南 - 知乎 (zhihu.com)

本地项目推送到远程仓库gitHub上(超详细) - 知乎 (zhihu.com)

主页 - LearnOpenGL CN (learnopengl-cn.github.io)

VS2022配置OpenGLVS2022 + OpenGL GLFW Windows环境配置_windows glfw-CSDN博客

glad.c直接放进工程文件里

先重学一边qt,忘差不多了

《Qt6 C++开发指南 》2023(上册,完整版)_哔哩哔哩_bilibili

EXP1

跟着教程做一个简单的应用

教程还是用的qmake,但我项目默认的是cmake,导入qrc资源的时候有点麻烦,Cmake里这么写。网上搜的教程都是再写一个qt_add_executable,但是冲突了,直接加在已有的qt_add_executable里

set(RESOURCES icons.qrc)
qt6_add_resources(RESOURCES_CPP ${RESOURCES})

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(test1
        MANUAL_FINALIZATION
        ${PROJECT_SOURCES}
        ${RESOURCES_CPP}
    )

QT属性系统Qt笔记(六十一)之Qt属性系统Q_PROPERTY - 知乎 (zhihu.com)

Opengl渲染管线看不明白,先看一下games101复习一下计算机图形学

https://www.bilibili.com/video/BV1X7411F744

先从opengl开始学吧,顺便在复习一下工程管理,Cmake什么的

【B站最好OpenGL】6-工程管理-编译与链接_哔哩哔哩_bilibili

大概把opengl入门部分过了一遍,现在开始在qt里实现

QT OpenGL窗口

首先新建qt工程,cmake构建,拖入opengl widget,并在cmake中导入

在帮助中选择索引,查找QopenglWidget,复制cmake语句粘贴cmake文件

在mainwindow.cpp中把CentralWidget设置成opengl窗口

但我们还是想要自定义一个基于opengl的widget,于是创建一个MyOpenGLWidget类继承自QOpenGLWidget,之后在mainwindow中作为成员变量并设置为centralWidget

myopenglwidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H

#include <QOpenGLWidget>

class MyOpenGLWidget : public QOpenGLWidget
{
    Q_OBJECT
public:
    explicit MyOpenGLWidget(QWidget* parent = nullptr);
};

#endif // MYOPENGLWIDGET_H

myopenglwidget.cpp
#include "myopenglwidget.h"

MyOpenGLWidget::MyOpenGLWidget(QWidget* parent):QOpenGLWidget(parent)
{
}

mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "myopenglwidget.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    MyOpenGLWidget *openGLWidget;
};
#endif // MAINWINDOW_H

mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    openGLWidget = new MyOpenGLWidget(parent);
    setCentralWidget(openGLWidget);
}

MainWindow::~MainWindow()
{
    delete ui;
}

之后重写OpenglWidget的三个关键函数,并且多重继承,加入QOpenGLFunctions_4_5_Core以调用opengl的函数

myopenglwidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>

class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_4_5_Core
{
    Q_OBJECT
public:
    explicit MyOpenGLWidget(QWidget* parent = nullptr);

protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();
};

#endif // MYOPENGLWIDGET_H

myopenglwidget.cpp
#include "myopenglwidget.h"

MyOpenGLWidget::MyOpenGLWidget(QWidget* parent):QOpenGLWidget(parent){

}

void MyOpenGLWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
}

void MyOpenGLWidget::resizeGL(int w, int h)
{

}

void MyOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}

之后VBO,EBO,VAO,SHADER都和VS环境下一样,略过。

#include "myopenglwidget.h"

MyOpenGLWidget::MyOpenGLWidget(QWidget* parent):QOpenGLWidget(parent){

}

MyOpenGLWidget::~MyOpenGLWidget()
{
    makeCurrent();glDeleteVertexArrays(1,&vao);
    glDeleteBuffers(1,&colorVbo);
    glDeleteBuffers(1,&posVbo);
    glDeleteBuffers(1,&ebo);
    glDeleteProgram(program);
    doneCurrent();
}

void MyOpenGLWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
    //prepareSingleBuffer();
    prepareVAO();
    prepareShader();
}

void MyOpenGLWidget::resizeGL(int w, int h)
{

}

void MyOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    //1 绑定当前的program
    glUseProgram(program);

    //2 绑定当前的vao
    glBindVertexArray(vao);

    //3 发出绘制指令
    //glDrawArrays(GL_TRIANGLES, 0, 3);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

void MyOpenGLWidget::prepareSingleBuffer() {
    //1 准备positions colors数据
    float positions[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };
    float colors[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f,  0.0f, 1.0f
    };


    //2 使用数据生成两个vbo posVbo, colorVbo
    GLuint posVbo, colorVbo;
    glGenBuffers(1, &posVbo);
    glGenBuffers(1, &colorVbo);

    glBindBuffer(GL_ARRAY_BUFFER, posVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);

    //3 生成vao并且绑定
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    //4 分别将位置/颜色属性的描述信息加入vao当中
    //4.1描述位置属性
    glBindBuffer(GL_ARRAY_BUFFER, posVbo);//只有绑定了posVbo,下面的属性描述才会与此vbo相关
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    //4.2 描述颜色属性
    glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    glBindVertexArray(0);
}
void MyOpenGLWidget::prepareVAO() {
    //1 准备positions
    float positions[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f,
        0.5f,  0.5f, 0.0f,
    };
    float colors[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f,  0.0f, 1.0f,
        0.3f,  0.1f, 0.7f
    };

    unsigned int indices[] = {
        0, 1, 2,
        2, 1, 3
    };

    GLuint posVbo, colorVbo;
    glGenBuffers(1, &posVbo);
    glGenBuffers(1, &colorVbo);

    glBindBuffer(GL_ARRAY_BUFFER, posVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);

    //3 EBO创建
    GLuint ebo;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    //4 VAO创建
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    //5 绑定vbo ebo 加入属性描述信息
    //5.1 分别将位置/颜色属性的描述信息加入vao当中
    //5.1.2描述位置属性
    glBindBuffer(GL_ARRAY_BUFFER, posVbo);//只有绑定了posVbo,下面的属性描述才会与此vbo相关
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    //5.1.2 描述颜色属性
    glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    //5.2 加入ebo到当前的vao
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

    glBindVertexArray(0);
}
void MyOpenGLWidget::prepareShader() {
    //1 完成vs与fs的源代码,并且装入字符串
    const char* vertexShaderSource =
        "#version 460 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "layout (location = 1) in vec3 aColor;\n"
        "out vec4 color;\n"
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "   color = vec4(aColor,1.0f);\n"
        "}\0";
    const char* fragmentShaderSource =
        "#version 330 core\n"
        "in vec4 color;\n"
        "out vec4 FragColor;\n"
        "void main()\n"
        "{\n"
        "   FragColor = color;\n"
        "}\n\0";


    //2 创建Shader程序(vs、fs)
    GLuint vertex, fragment;
    vertex = glCreateShader(GL_VERTEX_SHADER);
    fragment = glCreateShader(GL_FRAGMENT_SHADER);


    //3 为shader程序输入shader代码
    glShaderSource(vertex, 1, &vertexShaderSource, NULL);
    glShaderSource(fragment, 1, &fragmentShaderSource, NULL);

    int success = 0;
    char infoLog[1024];
    //4 执行shader代码编译
    glCompileShader(vertex);
    //检查vertex编译结果
    glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertex, 1024, NULL, infoLog);
        qDebug()<< "Error: SHADER COMPILE ERROR --VERTEX" << "\n" << infoLog;
    }

    glCompileShader(fragment);
    //检查fragment编译结果
    glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragment, 1024, NULL, infoLog);
        qDebug()<< "Error: SHADER COMPILE ERROR --VERTEX" << "\n" << infoLog;
    }

    //5 创建一个Program壳子
    program = glCreateProgram();

    //6 将vs与fs编译好的结果放到program这个壳子里
    glAttachShader(program, vertex);
    glAttachShader(program, fragment);

    //7 执行program的链接操作,形成最终可执行shader程序
    glLinkProgram(program);

    //检查链接错误
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(program, 1024, NULL, infoLog);
        //std::cout << "Error: SHADER LINK ERROR " << "\n" << infoLog << std::endl;
    }

    //清理
    glDeleteShader(vertex);
    glDeleteShader(fragment);
}

QOpenGLShaderProgram

自带的QOpenGLShaderProgram给shader注册和绑定都带了遍历,在qt项目中新建资源文件夹,将现有的shader代码放进文本文件中,由QOpenGLShaderProgram调用

void MyOpenGLWidget::prepareShaderProgram(){
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");
    bool success = shaderProgram.link();
    if(!success){
        shaderProgram.log();
    }
    shaderProgram.bind();
}

void MyOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    //1 绑定当前的program
    //glUseProgram(program);
    glUseProgram(shaderProgram.programId());

    //2 绑定当前的vao
    glBindVertexArray(vao);

    //3 发出绘制指令
    //glDrawArrays(GL_TRIANGLES, 0, 3);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

Uniform

shader的uniform如果要随时间变化的话,在qt里面的实现一般是调用定时器

MyOpenGLWidget::MyOpenGLWidget(QWidget* parent):QOpenGLWidget(parent){
    timer.setInterval(15);
    connect(&timer,&QTimer::timeout,[=](){
        makeCurrent();
        int timeValue = QTime::currentTime().msec()/999;
        setUniformFloat("time",timeValue);
        doneCurrent();
        update();
    });
    timer.start();
}
void MyOpenGLWidget::setUniformFloat(const QString &name, float value)
{
    //1 通过名称拿到Uniform变量的位置Location
    GLint location = glGetUniformLocation(shaderProgram.programId(), name.toStdString().c_str());

    //qDebug()<<value;

    //2 通过Location更新Uniform变量的值
    shaderProgram.setUniformValue(location, value);
}

Texture

对于Texture,qt也有相应的封装

//.h
    QOpenGLTexture* texture0;
    QOpenGLTexture* texture1;

//.cpp - preparetexture()
    texture0 = new QOpenGLTexture(QOpenGLTexture::Target2D);
    texture0->setMinificationFilter(QOpenGLTexture::Linear);
    texture0->setMagnificationFilter(QOpenGLTexture::Linear);
    texture0->setWrapMode(QOpenGLTexture::ClampToEdge);
    texture0->setData(QImage(":/resources/sayo.jpg").mirrored());

    texture1 = new QOpenGLTexture(QOpenGLTexture::Target2D);
    texture1->setMinificationFilter(QOpenGLTexture::Linear);
    texture1->setMagnificationFilter(QOpenGLTexture::Linear);
    texture1->setWrapMode(QOpenGLTexture::ClampToEdge);
    texture1->setData(QImage(":/resources/maomiao.png").mirrored());

//.cpp - paintGL()
    texture0->bind(0);
    texture1->bind(1);

//frag.glsl
#version 460 core
in vec4 color;
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D texture0;
uniform sampler2D texture1;
void main()
{
   //FragColor = color;
    FragColor = mix(texture(texture0,TexCoord),texture(texture1,TexCoord),0.5)*color;
};

Matrix

Qt自带的Matrix类,好用!

MyOpenGLWidget::MyOpenGLWidget(QWidget* parent):QOpenGLWidget(parent){
    timer.setInterval(15);
    connect(&timer,&QTimer::timeout,[=](){
        makeCurrent();
        float timeValue = QTime::currentTime().msec()/1000.0f*360.0f;
        //qDebug()<<timeValue;
        QMatrix4x4 matrix;
        matrix.translate(0.5f,-0.5f,0.0f);
        matrix.rotate(timeValue,0.1f,0.1f,1.0f);
        shaderProgram.setUniformValue("rotationmMatrix",matrix);
        doneCurrent();
        update();
    });
    timer.start();
}

#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec4 color;
out vec2 TexCoord;
uniform mat4 rotationmMatrix;
//uniform float time;

void main()
{
   gl_Position = rotationmMatrix*vec4(aPos.x, aPos.y, aPos.z, 1.0);
   //gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
   color = vec4(aColor, 1.0f);
   TexCoord = aTexCoord;
};

坐标系

局部空间-世界空间-观察空间-裁剪空间-正射投影/透视投影

由于矩阵乘法性质,要从右向左依次乘

Vclip=Mprojection⋅Mview⋅Mmodel⋅Vlocal

.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QOpenGLShaderProgram>
#include <QTimer>
#include <QTime>
#include <QOpenGLTexture>

class myOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_4_5_Core
{
    Q_OBJECT
public:
    explicit myOpenGLWidget(QWidget* parent = nullptr);
    ~myOpenGLWidget();

public:
    void prepareInterleavedBuffer();
    void prepareShaderProgram();
    void prepareMatrix();

protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();

private:
    GLuint vbo,vao;
    QOpenGLShaderProgram shaderProgram;
    QOpenGLTexture* texture0;
    QTimer timer;
    QMatrix4x4 projection,view,model;

private:
    void checkError();
};

#endif // MYOPENGLWIDGET_H

.cpp
#include "myopenglwidget.h"

myOpenGLWidget::myOpenGLWidget(QWidget* parent):QOpenGLWidget(parent)
{
    timer.setInterval(15);
    connect(&timer, &QTimer::timeout, this, [=](){update();});
    timer.start();
}

myOpenGLWidget::~myOpenGLWidget()
{
    qDebug()<<"release";
    delete texture0;
    makeCurrent();
    glDeleteVertexArrays(1,&vao);
    glDeleteBuffers(1,&vbo);
    doneCurrent();
}

void myOpenGLWidget::prepareInterleavedBuffer()
{
    float 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, 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,

        -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
    };
    glGenBuffers(1,&vbo);
    glBindBuffer(GL_ARRAY_BUFFER,vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glGenVertexArrays(1,&vao);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    //position
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);

    //uv
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float),  (void*)(3 * sizeof(float)));

    texture0 = new QOpenGLTexture(QOpenGLTexture::Target2D);
    texture0->setMinificationFilter(QOpenGLTexture::Linear);
    texture0->setMagnificationFilter(QOpenGLTexture::Linear);
    texture0->setWrapMode(QOpenGLTexture::Repeat);
    texture0->setData(QImage(":/resources/sayo.jpg").mirrored());

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER,0);

    projection.setToIdentity();
    projection.perspective(45,(float)width()/height(),0.1f,100.0f);
    shaderProgram.setUniformValue("projection",projection);

    view.setToIdentity();
    view.translate(0.0f,0.0f,-3.0f);
    shaderProgram.setUniformValue("view",view);

}

void myOpenGLWidget::prepareShaderProgram()
{
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");
    bool success = shaderProgram.link();
    if(!success){
        shaderProgram.log();
    }
    shaderProgram.bind();
    shaderProgram.setUniformValue("texture0",0);
}

void myOpenGLWidget::prepareMatrix()
{
    QMatrix4x4 matrix;
    matrix.rotate(150,30,30,1);
    //matrix.translate(0.5f,-0.5f,0.0f);
    shaderProgram.setUniformValue("rotationMatrix",matrix);
}

void myOpenGLWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
    prepareShaderProgram();
    prepareInterleavedBuffer();
    //prepareMatrix();
    glEnable(GL_DEPTH_TEST);
}

void myOpenGLWidget::resizeGL(int w, int h)
{

}

void myOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glUseProgram(shaderProgram.programId());
    glBindVertexArray(vao);
    texture0->bind(0);
    model.setToIdentity();
    float timeValue = QTime::currentTime().msec()/999.0f*360;
    model.rotate(timeValue,30.0f,30.0f,0.0f);
    shaderProgram.setUniformValue("model",model);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);
    texture0->release();
}

glsl
#version 460 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.x, aPos.y, aPos.z, 1.0);
   //gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
   TexCoord = aTexCoord;
};

绘制更多立方体,其实就是平移加多次调用program

摄像机

前面讨论了观察矩阵以及如何使用观察矩阵移动场景(我们向后移动了一点)。OpenGL本身没有摄像机(Camera)的概念,但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动。

F1摄像机_哔哩哔哩_bilibili

摄像机向量计算原理

由lookat封装好的api

void myOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
    currentFrame = elapsedTimer.elapsed();
    float deltaTime = currentFrame-lastFrame;
    if(deltaTime>50) deltaTime = 50;
    lastFrame = currentFrame;
    float cameraSpeed = 0.004*deltaTime;
    QVector3D up(0.0f,1.0f,0.0f);
    QVector3D cameraRight = QVector3D::crossProduct(cameraFront,up);
    switch(event->key()){
    case Qt::Key_W:cameraPos += cameraSpeed*cameraFront;break;
    case Qt::Key_S:cameraPos -= cameraSpeed*cameraFront;break;
    case Qt::Key_A:cameraPos -= cameraSpeed*cameraRight;break;
    case Qt::Key_D:cameraPos += cameraSpeed*cameraRight;break;
    }
}

void myOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    static QPoint deltaPos;
    static QPoint lastPos;
    static float yaw=-90;
    static float pitch=0;

    auto currentPos=event->pos();
    deltaPos=currentPos-lastPos;
    //qDebug()<<deltaPos.x()<<","<<deltaPos.y();
    if(abs(deltaPos.x())>50||abs(deltaPos.y())>50) deltaPos=QPoint(0,0);
    lastPos=currentPos;
    float sensitivity = 0.1f;// change this value to your liking
    deltaPos *= sensitivity;
    yaw += deltaPos.x();
    pitch -= deltaPos.y();//reversed since y-coordinates go from bottom
    if(pitch >89.0f)pitch = 89.0f;
    if(pitch<-89.0f)pitch = -89.0f;
    cameraFront.setX(cos(yaw*M_PI/180)*cos(pitch*M_PI/180));
    cameraFront.setY(sin(pitch*M_PI/180));
    cameraFront.setZ(sin(yaw*M_PI/180)*cos(pitch*M_PI/180));
    cameraFront.normalize();
}

void myOpenGLWidget::wheelEvent(QWheelEvent *event)
{
    fov -= event->angleDelta().y()/120;
    if(fov<=1.0) fov = 1.0f;
    if(fov>=75.0f) fov = 75.0f;
    makeCurrent();
    projection.setToIdentity();
    projection.perspective(fov,(float)width()/height(),0.1f,100.0f);
    shaderProgram.setUniformValue("projection",projection);
    doneCurrent();
}

光照

设置光源位置、颜色

模型加上法线,主要是改shader

shapes.vert
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
   gl_Position = projection*view*model*vec4(aPos, 1.0);
   FragPos = vec3(model * vec4(aPos, 1.0));
   //Normal = vec3(model*vec4(aNormal,1.0));
   Normal = mat3(transpose(inverse(model))) * aNormal;
};

shapes.frag
#version 460 core
in vec3 Normal;
in vec3 FragPos;
out vec4 FragColor;
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform vec3 lightPos;
uniform vec3 viewPos;

void main()
{
    //ambient环境光
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;
    //diffuse漫反射
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;
    //specular镜面反射
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos-FragPos);
    vec3 reflectDir = reflect(-lightDir,norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = specularStrength * spec * lightColor;
    vec3 result = (ambient + diffuse + specular) * objectColor;
    FragColor = vec4(result, 1.0);
};

myopenglwidget.cpp
#include "myopenglwidget.h"

myOpenGLWidget::myOpenGLWidget(QWidget* parent):QOpenGLWidget(parent)
{
    setFocusPolicy(Qt::StrongFocus);
    elapsedTimer.start();
    timer.setInterval(15);
    connect(&timer, &QTimer::timeout, this, [=](){update();});
    timer.start();
}

myOpenGLWidget::~myOpenGLWidget()
{
    qDebug()<<"release";
    makeCurrent();
    glDeleteVertexArrays(1,&vao);
    glDeleteBuffers(1,&vbo);
    doneCurrent();
}

void myOpenGLWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
    prepareShaderPrograms();
    prepareLightingBuffer();
    prepareMatrixs();
    glEnable(GL_DEPTH_TEST);
}

void myOpenGLWidget::resizeGL(int w, int h)
{

}

void myOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBindVertexArray(vao);

    shaderProgram.bind();
    float timeValue = elapsedTimer.elapsed()/1000.0f;
    model.setToIdentity();
    model.rotate(timeValue*15,35.0f,30.0f,1.0f);
    shaderProgram.setUniformValue("projection",projection);
    shaderProgram.setUniformValue("model",model);
    shaderProgram.setUniformValue("view",view);
    shaderProgram.setUniformValue("viewPos",cameraPos);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    lightingShaderProgram.bind();
    QMatrix4x4 model1;
    model1.setToIdentity();
    model1.translate(lightPos);
    model1.scale(0.2f);
    //qDebug()<<model1;
    lightingShaderProgram.setUniformValue("projection",projection);
    lightingShaderProgram.setUniformValue("model",model1);
    lightingShaderProgram.setUniformValue("view",view);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    glBindVertexArray(0);
}

void myOpenGLWidget::prepareLightingBuffer()
{
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };

    glGenBuffers(1,&vbo);
    glBindBuffer(GL_ARRAY_BUFFER,vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glGenVertexArrays(1,&vao);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    //position
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0);

    //Normal
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3*sizeof(float)));

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER,0);
}

void myOpenGLWidget::prepareShaderPrograms()
{
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");
    bool success = shaderProgram.link();
    if(!success){
        shaderProgram.log();
    }
    shaderProgram.bind();
    shaderProgram.setUniformValue("lightColor",lightColor);
    shaderProgram.setUniformValue("objectColor",objectColor);
    shaderProgram.setUniformValue("lightPos",lightPos);

    lightingShaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/lighting.vert");
    lightingShaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/lighting.frag");
    success = lightingShaderProgram.link();
    if(!success){
        lightingShaderProgram.log();
    }
    lightingShaderProgram.bind();
    lightingShaderProgram.setUniformValue("lightColor",lightColor);
}

void myOpenGLWidget::prepareMatrixs()
{
    projection.setToIdentity();
    projection.perspective(fov,(float)width()/height(),0.1f,100.0f);
    model.setToIdentity();
    model.rotate(130.0f,120.0f,150.0f,0.0f);
    view.setToIdentity();
    view.lookAt(cameraPos,cameraPos+cameraFront,QVector3D(0.0f,1.0f,0.0f));
}

void myOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    static QPoint deltaPos;
    static QPoint lastPos;
    static float yaw=-90;
    static float pitch=0;

    auto currentPos=event->pos();
    deltaPos=currentPos-lastPos;
    //qDebug()<<deltaPos.x()<<","<<deltaPos.y();
    if(abs(deltaPos.x())>50||abs(deltaPos.y())>50) deltaPos=QPoint(0,0);
    lastPos=currentPos;
    float sensitivity = 0.1f;// change this value to your liking
    deltaPos *= sensitivity;
    yaw += deltaPos.x();
    pitch -= deltaPos.y();//reversed since y-coordinates go from bottom
    if(pitch >89.0f)pitch = 89.0f;
    if(pitch<-89.0f)pitch = -89.0f;
    cameraFront.setX(cos(yaw*M_PI/180)*cos(pitch*M_PI/180));
    cameraFront.setY(sin(pitch*M_PI/180));
    cameraFront.setZ(sin(yaw*M_PI/180)*cos(pitch*M_PI/180));
    cameraFront.normalize();
    //shaderProgram.bind();
    view.setToIdentity();
    view.lookAt(cameraPos,cameraPos+cameraFront,QVector3D(0.0f,1.0f,0.0f));
    //shaderProgram.setUniformValue("view",view);
    update();
}

void myOpenGLWidget::wheelEvent(QWheelEvent *event)
{
    fov -= event->angleDelta().y()/120;
    if(fov<=1.0) fov = 1.0f;
    if(fov>=75.0f) fov = 75.0f;
    //shaderProgram.bind();
    projection.setToIdentity();
    projection.perspective(fov,(float)width()/height(),0.1f,100.0f);
    //shaderProgram.setUniformValue("projection",projection);
    update();
}

void myOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
    currentFrame = elapsedTimer.elapsed();
    float deltaTime = currentFrame-lastFrame;
    if(deltaTime>50) deltaTime = 50;
    lastFrame = currentFrame;
    float cameraSpeed = 0.002*deltaTime;
    QVector3D up(0.0f,1.0f,0.0f);
    QVector3D cameraRight = QVector3D::crossProduct(cameraFront,up);
    switch(event->key()){
    case Qt::Key_W:cameraPos += cameraSpeed*cameraFront;break;
    case Qt::Key_S:cameraPos -= cameraSpeed*cameraFront;break;
    case Qt::Key_A:cameraPos -= cameraSpeed*cameraRight;break;
    case Qt::Key_D:cameraPos += cameraSpeed*cameraRight;break;
    }
    //shaderProgram.bind();
    view.setToIdentity();
    view.lookAt(cameraPos,cameraPos+cameraFront,QVector3D(0.0f,1.0f,0.0f));
    //shaderProgram.setUniformValue("view",view);
    update();
}
届ける言葉を今は育ててる
最后更新于 2024-09-12