主頁 >  其他 > openGL 學習筆記 (二) 使用GL API 繪制出屬于自己的矩形

openGL 學習筆記 (二) 使用GL API 繪制出屬于自己的矩形

2020-09-10 02:45:55 其他

在OpenGL中所有的事物都是在3D空間中,但是我們所看到的螢屏成像卻是2D的像素陣列,這導致OpenGL的大部分作業就是把得到的3D坐標轉換為適應螢屏的2D影像,轉換的整個處理程序是由OpenGL的圖形渲染管線管理的,

OpenGL圖形渲染管線:

1> 指的是一堆原始圖形資料途經一個輸送管道,期間經過各種變化處理最終出現在螢屏的程序
2> 圖形渲染管線可以被劃分為兩個主要部分:第一部分把你的3D坐標轉換為2D坐標,第二部分是把2D坐標轉變為實際的有顏色的像素,
3> 圖形渲染管線可以劃分為幾個階段,每個階段都是把上個階段的輸出當做輸入來處理的,由于每隔階段的處理都是高度專門化的,所以很容易并行執行,所以在GPU中可用成千上萬的小處理核心可以為每個管線階段的著色器并行處理,
4> OpenGL的渲染管線大致可分為以下幾個階段:
定點資料輸入——> >圖元(片元)裝配——>幾何著色器——>光柵化——>片段著色器——>Alpha測驗與顏色混合
5> 有些著色器語序可以通過開發者自己實作,從而達到更細致的控制影像的渲染結果和速度,其中頂點著色器,幾何著色器,片段著色器是我們可以重新實作的部分,

 

簡略概括:
頂點資料輸入: 首先將頂點資料以陣列的形式將n個3D坐標作為整個圖形渲染管線的輸入,頂點資料是一系列頂點的集合,一個頂點是一個3D坐標的資料的集合,在OpenGL中頂點資料是用頂點屬性表示的,他可以包含任何可能呢會用到的資料,

頂點著色器: 接受一個單獨的頂點進行輸入,主要作業是將3D坐標變為另一種3D坐標,他接受開發者自己實作,

圖元(片元)裝配:

1> OpenGL需要去指定我們想要繪制的影像的坐標和顏色構成,是一系列的點,或是一系列的三角形,還是一系列的線,這些最基礎的構成被稱為圖元(片元), // TODO eg:
2> 圖元裝配將頂點著色器的所有輸出作為輸入,(注意,如果圖元型別為GL_PIONTS,則只接受一個頂點),然后將所有的點裝配成指定好的圖元形狀,

幾何著色器: 接受圖元形式的一系列頂點作為輸入,通過產生新頂點構造出新的圖元生成其他圖元,

光柵化: 將圖元映射為最終螢屏上相應的像素,生成供片段著色器使用的片段,并進行裁切,將超出你的視圖以外的所有像素丟棄,用來提升執行效率,

片段著色器:

1> 這里的片段是指OpenGL渲染一個像素所需的所有資料,
2> 片段著色器將計算一個像素的最終顏色,這也是所有OpenGL高級效果產生的地方,通常,片段著色器包含3D場景的資料(如光照、陰影、光的顏色等),這些資料可以被用來計算該片段最終像素的顏色,

Alpha測驗與顏色混合: 該階段會檢測對應片段對應的深度值,用來判斷這個片段是否存在于其他的片段之前或者之后,從而判斷該片段是否要被丟棄,同時也會檢查Alpha(透明度)值并對片段進行顏色混合

 

詳細程序及代碼實作:

定義頂點資料

float vertices[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f,  0.5f, 0.0f
};

(OpenGL僅當3D坐標在3個軸(x、y和z)上都為-1.0到1.0的范圍內時才處理它,)

將定義的定點資料作為輸入發送給頂點著色器,頂點著色器在GPU上創建記憶體用于儲存頂點資料,還要配置OpenGL如何解釋這些記憶體,并且指定其如何發送給顯卡,頂點著色器接著會處理我們在記憶體中指定好數量的頂點,
這個時候如果一個頂點一個頂點的發送,其缺點是顯而易見的,將頂點資料從CPU發送至GPU的速度相對較慢,使用頂點緩沖物件來管理這些頂點的記憶體則可以一次性發送盡可能多的資料,當頂點資料發送至顯存中后,頂點著色器幾乎能立即訪問頂點,


創建一個頂點緩沖物件的程序如下:

首先生成一個緩沖區物件

// generate buffer object names
void glGenBuffers(GLsizei n, GLuint * buffers);

第一個引數是要生成的緩沖物件的數量,第二個是要輸入用來存盤緩沖物件名稱的長度為n的id陣列,如果 n == 1, 則只需要一個id,
該函式僅僅生成一個緩沖物件的名稱,這個緩沖物件并不具備任何意義,即它僅僅是個緩沖物件,還不是頂點緩沖物件,他就像C中的一個指標變數,可以給他分配記憶體物件并且用它的名稱來參考這個記憶體物件,

unsigned int VBO;
glGenBuffers(1, &VBO);

頂點緩沖物件的緩沖區型別(緩沖區ID)為 GL_ARRAY_BUFFER,
要指定緩沖物件的型別則需要用到下面的函式

// bind a named buffer object
void glBindBuffer(GLenum target, GLuint buffer);

一個引數是緩沖物件的型別,第二個引數是已經創建的緩沖物件的名稱,使用該函式將創建好的VBO物件系結到OpenGL的背景關系環境中,如果系結的buffer值為零,那么OpenGL將不再對當前target使用任何物件,
需要注意的是函式的第二個引數雖然是Gluint型的,

OpenGL允許我們同一同時系結多個不同型別的緩沖物件,但是不能系結兩個相同的緩沖物件,這就是背景關系之所以用背景關系來理解它的意義,

緩沖物件只是OpenGL物件的一種,使用其他物件的思路跟緩沖物件是差不多的,都是創建物件——>系結背景關系——>設定資料

// 創建并初始化緩沖區物件的資料存盤
void glBufferData (GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);

該函式將洗掉之前存在于指定型別緩沖區的資料,為當前已系結的指定型別的緩沖區物件創建一個新的資料存盤,第一個引數是指定目標緩沖區型別,有GL_ARRAY_BUFFER(VBO)、GL_ELEMENT_ARRAY_BUFFER(EBO)兩種,第二個引數用來指定緩沖區物件的新資料需要的記憶體大小,第三個引數是我們希望發送的實際資料的指標,第四個引數用來指定給GPU資料的預期使用方式,這使得GL的實作能夠做出更明智的選擇,可能會影響緩沖區物件的性能,它不會限制資料存盤的實際使用,usage可分解為兩個部分理解,第一,訪問的頻率,第二,訪問的性質:
訪問頻率:
STREAM // 資料存盤內容將被修改一次并最多使用幾次,
STATIC // 資料存盤內容將被修改一次并多次使用,
DYNAMIC // 資料存盤內容將被重復修改并多次使用,
訪問的性質
DRAW // 資料存盤內容由應用程式修改,并用作GL繪圖和影像規范命令的源,
和在一起則是以下三個
GL_STATIC_DRAW // 資料不會或幾乎不會改變,
GL_DYNAMIC_DRAW // 資料會被改變很多,
GL_STREAM_DRAW // 資料每次繪制時都會改變,

到此頂點資料已經被存在GPU記憶體中了,

 

頂點著色器:
頂點著色器就要使用著色器語言GLES,用字串存盤,發送給GL,然后編譯這個著色器,eg:

#version 330 core
layout (lcoation = 0) in vec3 aPos;
void main()
{
    //注意,如果圖元型別為GL_PIONTS,則只接受一個頂點
    sition = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

每個著色器都起始與一個版本宣告,同時說明我們使用的渲染模式是核心模式,
in 關鍵字用來頂點著色器中宣告的所有頂點屬性,(上面的只有一個頂點位置屬性),layout (lcoation = 0) 代表輸入變數的位置值,
在主函式中設定著色器的雙輸出值,這里將gl_Position頂點位置屬性進行更改,

 

然后是編譯著色器:

// 創建一個著色器物件
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);

// 將著色器原始碼附加到著色器物件上
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL)
// 第一個引數是創建好的著色器物件,第二個引數指定的傳遞的原始碼字串數量,第三個引數是頂點著色器所需要的原始碼,
    
// 編譯著色器
glCompileShader(vertexShader);

// 檢測著色器是否編譯編譯成功,并獲取錯誤資訊,
int  success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}

片段著色器:

片段著色器的撰寫,編譯和頂點著色器的程序大同小異,只是型別變成了GL_FRAGMENT_SHADER,

#version 330 core
out vec4 FragColor;
void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
// 片段著色器只需要一個輸出變數,這個變數是一個vec4型別,他代表的是最終的 輸出顏色,使用out關鍵字宣告輸出變數,
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);

兩個著色器寫完之后,將兩個著色器物件鏈接到一個用來渲染的著色器程式中,
著色器程式物件是多個著色器物件鏈接在一起的成果,在渲染物件的時候激活需要的著色器程式,在發送渲染呼叫的時候GL會使用當前激活的著色器程式,
當鏈接著色器至一個程式的時候,它會把每個著色器的輸出鏈接到下個著色器的輸入,當輸出和輸入不匹配的時候,你會得到一個連接錯誤,

 

// 創建一個著色器程式物件

// 創建一個著色器程式物件
unsigned int shaderProgram;
shaderProgram = glCreateProgram();

// 將著色器物件加入著色器程式物件并鏈接
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

// 檢查鏈接成功:
int successs;
char infoLog[512];
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if(!success) {
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
}

// 在鏈接成功后洗掉之前創建的著色器物件,釋放記憶體
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

// 在需要的時候,使用著色器
glUseProgram(shaderProgram);

在glUseProgram函式呼叫之后,每個著色器呼叫和渲染呼叫都會使用這個著色器程式物件,

頂點資料已經發送給了GPU,也指示了GPU在頂點著色器和片段著色器中如何處理它,但是GPU還不知道如何處理已經存在GPU記憶體中的的頂點數據,即如何將已經在GPU中的頂點是資料以頂點著色器可以接受的方式發送給頂點著色器,頂點著色器可以接受任何以頂點屬性形式的輸入,所以頂點著色器有著很強的靈活性,但是這樣我們必須手動指定輸入資料的哪一個部分對應頂點著色器需要的的哪一個頂點屬性,所以在開始渲染之前,必選要告訴頂點著色器如何解釋頂點資料,

上文在傳入的時候,vertices是一個float型別的一維陣列,即一段連續的浮點型記憶體,在GPU中頂點緩沖資料則會被決議為以下特征的連續記憶體:
1> 每個資料會被存盤32位(4位元組)的float型別值;
2> 每個位置包含三個這樣的值;
3> 存盤的記憶體和傳入的時候一樣是連續的;
4> 資料的第一個值在緩沖開始的位置,即陣列的第0個元素,

通過既定的這個資訊,就可以使用glVertexAttribPointer函式來告訴GL如何去解釋頂點緩沖物件,eg:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// 引數含義
1> 第一個引數指定配置的頂點屬性,在撰寫頂點著色器的時候,第二句中有個 layout(location = 0),該陳述句設定position頂點的屬性位置值(location)為0,如果希望把頂點資料傳遞到這一個頂點屬性中,那么這里引數傳入0;
2> 第二個引數用來指定頂點屬性的大小,因為用到的頂點位置資料為vec3,由三個值組成,大小為3;
3> 第三個引數指定資料的型別;
4> 第四個引數指定是否希望資料被標準化,如果為GL_TRUE,所有傳入的資料會被映射到0到1(有符號輸為-1到1)之間,
5> 第五個引數可以用“步長”來解釋,他用來告訴GL整個頂點屬性第二次出現的地方到整個陣列0位置之間有多少位元組,也可以傳入0讓GL自己檢測,但是只能在傳入連續記憶體(即陣列)的情況下使用,
6> 第六個引數用來表示位置資料在緩沖中起始位置的偏移量(Offset),但要注意,改引數的型別為void*,所以要講型別強制轉換為void*,

而 glEnableVertexAttribArray 則以頂點屬性位置值作為引數,啟用頂點屬性,因為頂點屬性默認是禁用的, 整套流程下來大概會是這樣:

// 創建并系結頂點緩沖物件
float vertices[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f,  0.5f, 0.0f
};

glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// 設定頂點屬性指標
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// 渲染時,使用著色器程式
glUseProgram(shaderProgram);

// 繪制物體
someOpenGLFunctionThatDrawsOurTriangle();

在繪制時,每繪制一個物體的時候都必須重復這一程序,這個時候系結正確的緩沖物件,為每次繪制配置正確的頂點屬性就變的比較麻煩,為了解決這個麻煩,可以使用頂點陣列物件,將所有繪制需要的狀態資訊儲存在一個物件中,

頂點陣列物件可以像頂點緩沖物件一樣被系結,任何隨后的頂點屬性呼叫都會被儲存著這個VAO物件中,這樣就等于說是在配置頂點屬性指標時,只需要將之前的呼叫執行一次,在繪制不同的物體時,只需要系結不同的VAO就可以了,(OPGL的核心渲染模式指定使用VAO物件,如果系結VAO物件失敗,OPGL會拒絕渲染)

一個頂點陣列物件將會存盤以下內容:
1> glEnableVertexAttribArray和glDisableVertexAttribArray的呼叫,
2> 通過glVertexAttribPointer設定的頂點屬性配置,(也就是資料放在哪里,和如何讀取資料)
3> 通過glVertexAttribPointer呼叫與頂點屬性關聯的頂點緩沖物件,

在記憶體中大概就是這個意思

VBO1 = {pos[0], pos[1], pos[2], ...}
VBO2 = {pos2[0], pos2[1], pos2[2], ...}
VAO1 = {*pos[0]}

VBO3 = {pos[0], color[0], pos[1], color[1], pos[2], color[2], ...}
VAO2 = {*pos[0], *color[0]}

創建一個VAO物件跟創建一個VBO物件相似:

unsigned int VAO;
glGenVertexArrays(1, &VAO);

在使用glBindVertexArray系結VAO之后,就可以對VAO物件進行操作,在系結之后,可把系結和配置對應的VBO和屬性指標然后解綁,供之后使用,在繪制物體的時候只需要把VAO物件系結到希望使用的設定上,新的使用VAO繪制的流程應該如下:

// 系結VAO
glBindVertexArray(VAO);

// 將BO復制到GL緩沖區中供GL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// 設定VBO物件
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// 繪制
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
someOpenGLFunctionThatDrawsOurTriangle();

// 實際繪制時(在回圈中)使用的代碼應如下
glUseProgram(shaderProgram);
glBindVertexArray(VAO); //系結我們需要的VAO,會導致上面所有VAO保存的設定自動設定完成
glDrawArrays();   

// 最后記得解綁不會再使用的VAO物件
glBindVertexArray(0);   //解綁VAO

 

索引緩沖物件(EBO):
如果是繪制一個矩形,可以使用繪制兩個三角形的辦法繪制,這會生成下面兩個索引緩沖物件:

float vertices[] = {
    // 第一個三角形
    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   // 左上角
}

可以看到,有兩對一模一樣的頂點,但實際上,一個矩形只有四個頂點這就會產生50%的額外開銷,如果模型越來愈大,這個問題會更加糟糕,會產生數不清的浪費,好的解決方案是存盤不同的頂點,并設定繪制這些頂點的順序,這就是索引緩沖物件的(EBO)作業方式和頂點緩沖物件一樣EBO是一個緩沖,他專門儲存索引,OpenGL呼叫這些頂點的索引來決定繪制哪個點,

1> 首先,我們要定義不懂位置的頂點,和繪制矩形所需要啊的索引:

float vertices[] = {
  0.5f, 0.5f, 0.0f, // 右上角
  0.5f, -0.5f, 0.0f, // 右下角
  -0.5f, 0.5f, 0.0f, // 左上角
  -0.5f, -0.5f, 0.0f, // 左下角
}

unsigned int indices[] = { // 注意索引從0開始! 
  0, 1, 3, // 第一個三角形
  1, 2, 3 // 第二個三角形
};

2> 創建索引緩沖物件:

ussigned int EBO;
glGenBuffers(1, &EBO);

3> 系結EBO然后用將索引復制到緩沖中

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

4> 用glDrawElements來替換glDrawArrays函式,來指明我們從索引緩沖渲染,

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

第一個引數指定了我們繪制的模式;
第二個引數是我們打算繪制頂點的個數,這里填6;
第三個引數是索引的型別;
最后一個引數里我們指定EBO中的偏移量,

如果整套流程下來(使用VAO、VBO、EBO)整個三角形的位置代碼會是這樣:

#include <iostream>
#include <glad/glad.h> 
#include <GLFW/glfw3.h>

#include "config.h"
using namespace std;

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

const char *strfragmentShaderSource = "#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";

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow *window = glfwCreateWindow(WIND_WIGHT, WIND_HEIGHT, "window", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "create window failed!" << endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "init glad failed!" << endl;
        return -1;
    }

    // 創建一個著色器,著色器型別為 GL_VERTEX_SHADER(頂點著色器)
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    // 把著色器原始碼附加到著色器物件
    glShaderSource(vertexShader, 1, &strvertexShaderSource, NULL);
    // 編譯著色器
    glCompileShader(vertexShader);

    int success;
    char infoLog[512];
    // 判斷編譯結果并列印出錯誤
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std:cout << "VERTEX SHADER  COMPILATION_FAILED" << infoLog << std::endl;
    }

    // 創建一個著色器,著色器型別為 GL_FRAGMENT_SHADER (片段著色器)
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &strfragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    // 判斷編譯結果并列印出錯誤
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "FRAGMENT SHADER COMPLATION_FAILED" << infoLog << std::endl;
    }

    // 鏈接著色器物件
    int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // 判斷鏈接結果并列印出錯誤
    if (!success)
    {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "LINK SHADER FAILED" << infoLog << std::endl;
    }

    // 鏈接成功后就可以將shader洗掉
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // 將頂點資料傳入頂點著色器
    // 定義一個頂點陣列
    float vertices[] = {
        0.5f, 0.5f, 0.0f,    // 右上角
        0.5f, -0.5f, 0.0f,   // 右下角
        -0.5f, -0.5f, 0.0f,  // 左下角
        -0.5f, 0.5f, 0.0f    // 左上角 
    };

    // 定義一個索引陣列
    unsigned int indices[] = {
        0, 1, 3,
        1, 2, 3
    };

    // 創建一個VAO物件,一個VBO物件,一個EBO物件
    unsigned int VAO, VBO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    // 系結VAO物件    
    glBindVertexArray(VAO);

    // 將VBO復制到GL緩沖區中供GL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    // 復制頂點陣列到緩沖中供OpenGL使用
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 告訴GL如何去解釋頂點緩沖物件(VBO)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    // 將EBO復制到GL緩沖區中供GL使用
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

    // 將頂點索引復制到緩沖中
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // 告訴GL啟用頂點屬性
    glEnableVertexAttribArray(0);

    // 解除背景關系系結VAO,這樣其他VAO呼叫不會意外地修改此VAO(通常情況下是不必要的)
    // glBindVertexArray(0);

    while (!glfwWindowShouldClose(window))
    {
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // 使用鏈接好的shader
        glUseProgram(shaderProgram);
        
        // 系結VAO物件(如果有操作解綁了VAO物件)
        // glBindVertexArray(VAO);
        
        // 指明從索引緩沖渲染
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // GC
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteProgram(shaderProgram);

    glfwTerminate();

    return 0;
}

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/562.html

標籤:其他

上一篇:復雜的光照與更復雜的陰影實作程序——ShaderCP9

下一篇:three.js中的矩陣計算

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more