天天看點

初步認識OpenGL之QT1. OpenGL 概述2. QT 項目

初始Opengl之QT

  • 1. OpenGL 概述
  • 2. QT 項目

1. OpenGL 概述

OpenGL

是一個規範,此規範有各個廠家最終實作。

OpenGL

的對外展現就是一個庫的概念。

OpenGL

現在分為核心模式和立即渲染模式(即固定的渲染管線),核心模式是

OpenGL 3.3

開始支援的,核心模式指可程式設計模式,核心模式的渲染管線(其實就是從一系列資料到最終配置圖像的流程)如下:

初步認識OpenGL之QT1. OpenGL 概述2. QT 項目

其中

頂點着色器

片段着色器

需要程式員自己實作。

若要寫一個

OpenGL

的可視化程式則需要另外兩個庫的輔助

GLFW

GLAD

  1. GLFW

    的作用是用來解決圖形界面顯示、消息處理及使用者輸入處理。
  2. GLAD

    則使得代碼可以用于不同的

    OpenGL

    驅動,即對

    OpenGL

    做的封裝,使得不同系統的上層使用可以更簡便的使用

    OpenGL

可以借助

QT

OpenGL

的封裝,實作無需配置第三方庫的簡便開發。

CPU

資料如何與

GPU

資料互動,我們在程式中書寫的頂點資料是在

CPU

中的,如果

GPU

要使用這些資料,我們應該明确的告訴

GPU

應該怎麼使用。這其中涉及到

VBO(Vertex Buffer Objects)

VAO(Vertex Array Objects)

VEO(Vertex Element Objects)

,其中

VEO

也被稱為

VAO(Vertex Index Objects)

  • VBO

    : 用來在

    GPU

    中建立記憶體并存儲頂點資料,頂點緩沖對象類型為

    GL_ARRAY_BUFFER

  • VAO

    : 用來配置

    OpenGL

    如果解析這些記憶體,

    VAO

    中存儲的是

    VBO

    資料類型的定義,而不存儲資料本身
  • VEO

    : 它用來存儲頂點資料的索引,繪圖時根據索引進行繪制

    VBO

    VAO

    VEO

    的關系:
    初步認識OpenGL之QT1. OpenGL 概述2. QT 項目

2. QT 項目

UI界面:

初步認識OpenGL之QT1. OpenGL 概述2. QT 項目

黑色視窗為

OpenGL Widget

,這裡對其進行了提升即修改了其繼承的父類為自定的

MyOpenGLWidget

,在控件中右鍵選擇

提升

即可操作界面如下:

初步認識OpenGL之QT1. OpenGL 概述2. QT 項目

myopenglwidget.h内容如下:

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>

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


protected:
    virtual void initializeGL() override;
    virtual void paintGL() override;
signals:

private:
    GLuint m_VBO;
    GLuint m_VAO;

    // 這裡使用非靜态變量glCreateProgram會報錯,目前還不清楚具體的是為什麼(也可能是我環境本身的問題)
    static GLuint m_shaderProgram;
    // 這裡使用非靜态變量報錯,目前還不清楚具體的是為什麼(也可能是我環境本身的問題)
    static GLuint m_EBO;


};

#endif // MYOPENGLWIDGET_H
           

myopenglwidget.cpp内容如下:

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

GLuint MyOpenGLWidget::m_shaderProgram = 0;
GLuint MyOpenGLWidget::m_EBO = 0;
MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{

}

MyOpenGLWidget::~MyOpenGLWidget()
{
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}


GLfloat vertex[]={
    -0.5f,-0.5f,0.0f,
    0.5f,-0.5f,0.0f,
    0.0f,0.5f,0.0f
};


// 存在重複的坐标點
//GLfloat vertexRectangle[]={
//    0.5f,0.5f,0.0f,
//    0.5f,-0.5f,0.0f,
//    -0.5f,0.5f,0.0f,
//    0.5f,-0.5f,0.0f,
//    -0.5f,-0.5f,0.0f,
//    -0.5f,0.5f,0.0f
//};

GLfloat vertexRectangle[]={
    0.5f,0.5f,0.0f,
    0.5f,-0.5f,0.0f,
    -0.5f,-0.5f,0.0f,
    -0.5f,0.5f,0.0f
};

GLuint index[]={
    0,1,3,
    1,2,3
};


const char* vertexShaderSource = "#version 330 core\n"
                             "layout (location = 0) in vec3 aPos;\n"
                             "void main()\n"
                             "{\n"
                             " gl_Position = vec4(aPos,1.0f);\n"
                             "}\n\0";

const char* fragmentShaderSource="#version 330 core\n"
                             "out vec4 FragColor;\n"
                             "void main()\n"
                             "{\n"
                             " FragColor = vec4(1.0f,0.5f,0.2f,1.0f);\n"
                             "}\n\0";

void MyOpenGLWidget::initializeGL()
{
    /*
     * 初始化OpenGL的函數接口
     * 這個接口不能寫在構造函數中,否則會程式運作出錯,因為initializeOpenGLFunctions()中會需要視窗句柄,而視窗句柄在構造函數完成之後才會被建立
    */
    initializeOpenGLFunctions();

    // 建立VAO
    glGenVertexArrays(1,&m_VAO);
    // 建立VBO
    glGenBuffers(1,&m_VBO);

    // 綁定VAO和VBO對象
    glBindVertexArray(m_VAO);
    glBindBuffer(GL_ARRAY_BUFFER,m_VBO);

    // 為緩沖對象建立一個新的資料存儲
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertexRectangle),vertexRectangle,GL_STATIC_DRAW);

    // 告訴先看如何解析緩沖裡的屬性值
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3 * sizeof(GLfloat),(void*)0);

    // 開啟VAO管理的第一個屬性值
    glEnableVertexAttribArray(0);


    // 建立EBO,必須在VAO解綁之前建立,因為EBO也會被VAO所記錄,但是解綁VBO卻不會對VAO中的記錄産生影響
    glGenBuffers(1,&m_EBO);
    // 綁定EBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
    // 給EBO配置設定空間,并存儲資料
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(index),index,GL_STATIC_DRAW);

    // 解除EBO,若此處解綁EBO則VAO中就會不在記錄EBO内容,需要在使用的時候重新綁定EBO,這裡放在析構函數中解綁
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    // 解除VBO和VAO的綁定
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glBindVertexArray(0);



    // 建立頂點着色器
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    // 綁定着色器
    glShaderSource(vertexShader,1,&vertexShaderSource,NULL);
    // 編譯頂點着色器
    glCompileShader(vertexShader);

    // 建立片段着色器
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader,1,&fragmentShaderSource,NULL);
    // 編譯片段着色器
    glCompileShader(fragmentShader);

    // 連結 shaders
    m_shaderProgram = glCreateProgram();
    glAttachShader(m_shaderProgram,vertexShader);
    glAttachShader(m_shaderProgram,fragmentShader);
    glLinkProgram(m_shaderProgram);

    //釋放
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    // 指定繪圖的模式,這裡設定為GL_LINE,此模式隻繪制線框而不進行填充
    glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);

}

void MyOpenGLWidget::paintGL()
{
    // 設定Clear的屬性
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    // 使用
    glClear(GL_COLOR_BUFFER_BIT);

    // 使用shader
    glUseProgram(m_shaderProgram);
    // 綁定VAO
    glBindVertexArray(m_VAO);
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_EBO);
    // 繪制圖形
    //glDrawArrays(GL_TRIANGLES,0,3);
    /*
     * 根據索引繪制
     * 若沒有建立EBO,最後一個參數可以直接傳遞索引的數組
    */
    glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0/*&index*/);
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    // 解除VAO
    glBindVertexArray(0);

}
           

mainwindow.cpp内容如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

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

其他代碼均為存在修改,運作效果如下:

初步認識OpenGL之QT1. OpenGL 概述2. QT 項目

繼續閱讀