主頁 >  其他 > LearnOpenGL從入門到入魔(2):如何使用OpenGL?

LearnOpenGL從入門到入魔(2):如何使用OpenGL?

2020-12-23 12:46:15 其他

總目錄

LearnOpenGL從入門到入魔(1):什么是OpenGL?
LearnOpenGL從入門到入魔(2):如何使用OpenGL?
LearnOpenGL從入門到入魔(3):著色器語言和坐標系統
LearnOpenGL從入門到入魔(4):光照(光斬訓礎,材質)
LearnOpenGL從入門到入魔(5):光照(光照貼圖,投光物)

1. OpenGL基本概念

1.1 影像渲染管線

?在OpenGL中,任何事物都在3D空間中,而螢屏和視窗卻是2D像素陣列,這導致OpenGL的大部分作業都是關于把3D坐標轉變為適應你螢屏的2D像素,3D坐標轉為2D坐標的處理程序是由OpenGL的圖形渲染管線(GraphicsPipeline,或稱管線)管理的,該程序可表述為將一堆原始圖形資料途經一個輸送管道,期間經過各種變化處理最終出現在螢屏的程序,圖形渲染管線可以被劃分為兩個主要部分:第一部分將輸入的3D坐標轉換為2D坐標,第二部分是把2D坐標轉變為實際的有顏色的像素,下圖表示一個圖形渲染管線的每個階段的抽線表示:

管線

?藍色部分著色器允許我們自定義,即可編程著色器,包括頂點著色器幾何著色器片段著色器,而幾何著色器通常使用默認的就可以了,其它部分也是用默認的即可,在現代OpenGL中,我們必須定義至少一個頂點著色器和一個片段著色器,因為GPU中沒有默認的頂點/片段著色器,

?圖形渲染管線各階段功能如下:

  • 頂點著色器(Vertex Shader)

?頂點著色器接收一組頂點陣列作為輸入,這些頂點資料使用頂點屬性表示,它會將每個頂點的3D坐標轉換為另一種3D坐標,同時對該頂點的頂點屬性進行一些基本處理,然后再將經過處理后的頂點陣列,

  • 頂點屬性

?頂點屬性指定了每個頂點的各種屬性,包括坐標(postion)、顏色(color)、法線(normal)以及紋理(Texture)等,下面演示了如何定義一組頂點資料,其中每個頂點包含了坐標、顏色和紋理屬性:

  GLfloat vertex[ 4*(3+4+2)] =
    {   //x,y,z,              r,g,b,a                  s,t(紋理)
        -0.5f,  0.5f, 0.0f,   0.0f, 0.0f, 0.5f, 1.0f,  0.0f, 1.0f, // 第1個頂點
         0.5f,  0.5f, 0.0f,   0.0f, 0.5f, 0.0f, 1.0f,  1.0f, 0.0f, // 第2個頂點
        -0.5f, -0.5f, 0.0f,   0.5f, 0.0f, 1.0f, 1.0f,  0.0f, 1.0f, // 第3個頂點
         0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 0.5f, 1.0f,  1.0f, 1.0f, // 第4個頂點
    };

?在OpenGL中,頂點屬性資料存盤在一段連續的記憶體空間,下圖展示了3個頂點的存盤情況,其中每個頂點的坐標和顏色屬性分別占3個float,即12位元組(BYTE):
image

  • 形狀(圖元)裝配(SHAPE/Primitive Assembly)

?圖元(Primitive),或稱形狀,是指OpenGL的渲染型別,當我們將輸入的坐標和顏色渲染成具體的某種表示時,需要在呼叫OpenGL的指令時指定圖元,比如GL_POINTS表示點,GL_TRIANGLES表示三角形以及GL_LINE_STRIP表示一系列的連續直線等,因此,圖元裝配的作用時將頂點著色器輸出的所有頂點作為輸入,輸出制定的基本圖元,

  • 幾何著色器(Gemometry Shader)

?幾何著色器把基本圖元形式的頂點的集合作為輸入,可以通過產生新頂點構造出新的(或是其他的)基本圖元來生成其他形狀,

  • 光柵化(Rasterization Stage)

?光柵化階段將幾何著色器輸出圖元映射為最終螢屏上相應的像素,生成供片段著色器使用的片段(Fragment),在片段著色器運行之前會執行裁切,而裁切的目的即為丟棄超出我們視圖以外的所有像素,以便提升執行效率,

片段(Fragment):指OpenGL渲染一個像素所需的所有資料,

  • 片段著色器(Fragment Shader)

?片段著色器以光柵化階段生成的片段作為輸入,它的作用是計算一個像素的最終顏色,這也是所有OpenGL高級效果產生的地方,通常,片段著色器包含3D場景的資料(比如光照、陰影、光的顏色等等),這些資料可以被用來計算最終像素的顏色值,

  • 測驗與混合(Test & Blend)

?該階段的目的為檢測片段對應的深度值,用它們來判斷這個像素是其它物體的前面還是后面,決定是否應該丟棄,同時,也會檢查用于定義物體透明度的alpha值,并對物體進行混合,所以,即使在片段著色器中計算出來了一個像素輸出的顏色,在渲染多個有重疊的物體的時候最后的像素顏色也可能完全不同,

1.2 著色器

?圖形渲染管線接受一組3D坐標,然后把它們轉變為螢屏上的有色2D像素輸出,圖形渲染管線可以被劃分為幾個階段,每個階段將會把前一個階段的輸出作為輸入,所有這些階段都是高度專門化的(它們都有一個特定的函式),并且很容易并行執行,正是由于它們具有并行執行的特性,當今大多數顯卡都有成千上萬的小處理核心,它們在GPU上為每一個(渲染管線)階段運行各自的小程式,從而在圖形渲染管線中快速處理你的資料,這些小程式叫做著色器(Shader),OpenGL的著色器使用著色器語言GLSL(OpenGL Shading Language)撰寫,通過編譯頂點著色器的原始碼,就可以在程式中使用它,

1.2.1 頂點著色器

?頂點著色器接收一組頂點資料(VertextData)作為輸入,這些頂點資料使用頂點屬性表示,它會將每個頂點的3D坐標轉換為另一種3D坐標(標準化設備坐標),同時對該頂點的頂點屬性進行一些基本處理,需要注意的是,頂點著色器依次處理頂點集合中的頂點資料,比如,我們要繪制一個三角形,那么就需要向頂點著色器輸入3個頂點資料,且每個頂點的坐標(postion)是3D的,下列代碼描述一個非常簡單的頂點著色器原始碼:

// 注釋(1)
#version 330 core
// 注釋(2)
layout (location = 0) in vec3 aPos;

void main()
{
    // 注釋(3)
    // GLSL中一個向量最多有四個分量,使用vec4表示
    // 每個分量值表示空間中的坐標,即
    // vec.x 表示x軸坐標;
    // vec.y 表示y軸坐標;
    // vec.z 表示z軸坐標;
    // vec.w 不用做表達空間的位置,主要應用再透視除法(后續解釋)
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

?注釋(1) 用于指明OpenGL的使用版本為3.3,采用核心模式;注釋(2) 有兩個作用,一是使用in關鍵字在頂點著色器中宣告所有的輸入頂點屬性(注:本例中只關心頂點的位置position資料),二是通過layout (location = 0)設定輸入變數的位置值Location;注釋(3) 用于設定頂點著色器的輸出,即將位置資料賦值給預定義變數gl_Position,該變數的型別為vec4,這是一個包含4個分量的向量,

  • 標準化設備坐標

?當頂點坐標經過頂點著色器處理后,將會被轉換為標準化設備坐標,所謂標準化設備坐標,是一個x、y和z值在-1.0~1.0的一小段空間,任何落在范圍外的坐標都會被丟棄或者裁剪,不會顯示在設備螢屏上,下圖展示了在標準化坐標中定義一個2D三角形,即忽略z軸,

image

1.2.2 片段著色器

?片段著色器用于計算一個像素的最終顏色值,該階段是OpenGL高級效果產生的地方,通常,片段著色器包含3D場景的資料,比如光照、陰影、光的顏色等等,這些資料將被用來計算最終像素的顏色值,在計算機圖形中,一個像素的顏色值由4個分量組成,即R(紅色)、G(綠色)、B(藍色)和A(透明度),就是我們熟知的RGBA,在OpenGL中,顏色的每個分量的取值范圍為0.0f~1.0f,顏色程度由淺色到深色,下列代碼演示了如何創建一個簡單的片段著色器:

// 指明OpenGL的使用版本為3.3,并采用核心模式
#version 330 core
// 定義片段著色器的輸出
// 輸出一個vec4型別的變數,該變數表示像素的顏色值
out vec4 FragColor;

void main()
{
    // 對變數進行賦值
    // 即每個像素的顏色為紅色
    FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
} 

1.3 VAO、VBO和EBO

1.3.1 VBO

?VBO,Vertex Buffer Object,即頂點緩沖物件,該物件用于管理GPU的一段記憶體(在GPU中又稱顯存),這段記憶體存盤了大量的頂點資料以供頂點著色器和片段著色器使用,使用這些VBO的好處就是我們可以一次性由CPU向GPU發送一大批資料,以提高著色器訪問頂點資料的效率,而不是每個頂點發送一次,畢竟CPU把資料發送到顯卡相對較慢,VBO創建程序:

  • 首先,呼叫glGenBuffers函式創建一個VBO物件,并生成一個對應的唯一ID;
unsigned int mVBOId;
// 函式原型:glGenBuffers(GLsizei n, GLuint *buffers);
//
// 函式作用:創建一組緩沖物件,并指定各自對應的唯一ID,這里創建的是VAO物件
// 引數說明:  
// n 表示VBO物件的數目
// buffers 指定這些VBO物件對應的唯一ID;
glGenBuffers(1, &mVBOId);
  • 其次,呼叫glBindBuffer函式將創建的VBO物件(快取)系結到GL_ARRAY_BUFFER目標上,OpenGL有很多緩沖物件型別,頂點緩沖物件的緩沖型別是GL_ARRAY_BUFFER,OpenGL允許我們同時系結多個緩沖,只要它們是不同的緩沖型別,
// 函式原型:glBindBuffer(GLenum target, GLuint buffer)
//
// 函式作用:系結VBO物件到指定目標快取型別
// 引數說明:
// target 指定該VBO物件要系結到的目標緩沖型別,比如GL_ARRAY_BUFFER;
// buffer 指定該VBO物件對應的ID;
glBindBuffer(GL_ARRAY_BUFFER, mVBOId);  
  • 第三,呼叫glBufferData函式將定義的頂點資料復制到當前系結的緩沖的記憶體中,
// 函式原型:
//  glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage)  
//
// 函式作用:拷貝頂點資料到GPU顯存
// 引數說明: 
// target 指定當前快取記憶體(VBO)系結的目標,這里為GL_ARRAY_BUFFER
// size 指定發送頂點資料的大小(位元組)
// data 指定發送的頂點資料
// usage 指定GPU如何管理這些資料,有三種形式:  
//       GL_STATIC_DRAW :資料不會或幾乎不會改變;
//       GL_DYNAMIC_DRAW:資料會被改變很多;
//       GL_STREAM_DRAW :資料每次繪制時都會改變;
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

注釋:三角形的位置資料不會改變,每次渲染呼叫時都保持原樣,所以它的使用型別最好是GL_STATIC_DRAW,如果,比如說一個緩沖中的資料將頻繁被改變,那么使用的型別就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,這樣就能確保顯卡把資料放在能夠高速寫入的記憶體部分,

  • 最后,使用完VBO后需要釋放相關資源,
// 函式原型:glDeleteBuffers(GLsizei n, GLuint *buffers);
//
// 函式作用:釋放一組VBO對所占資源
// 引數說明:  
// n 表示VBO物件的數目
// buffers 指定這些VBO物件對應的唯一ID;
glDeleteBuffers(1, &mVBOId);

?下圖演示了VBO在記憶體中的存盤形式:

在這里插入圖片描述

?其中,VBO1的每個頂點只包含位置(pos)屬性;VBO2的每個頂點包含了位置(pos)和顏色(color)屬性,然而,雖然我們已經把輸入頂點資料發送給了GPU,并指示了GPU如何在頂點和片段著色器中處理它,但是,OpenGL還不知道它該如何解釋記憶體中的頂點資料,以及它該如何將頂點資料鏈接到頂點著色器的屬性上,因此,在渲染之前,我們必須指定OpenGL該如何解釋頂點資料,而這個程序被稱之為鏈接頂點屬性,下圖展示了經過鏈接頂點屬性操作后,顯存中的頂點資料被決議后的樣子:

在這里插入圖片描述

?從上圖可知,這段GPU顯存(快取)中連續存盤了三個頂點資料,即VEXTEX1、VEXTEX2、VEXTEX3,每個頂點由位置(pos)顏色(color)紋理三個屬性組成,其中,位置屬性包含X、Y、Z分量,每個分量占1float(=4位元組),因此位置屬性共占12個位元組;顏色屬性包含R、G、B分量,每個分量占1float(=4位元組),因此顏色屬性共占12個位元組;紋理屬性包含S、T分量,每個分量占1float(=4位元組),因此顏色屬性共占8個位元組,此外,兩個相鄰頂點之間相同的屬性之間的距離稱為為步長(STRIDE),即第一個頂點某個屬性起始分量到第二個頂點相同屬性的起始分量所占位元組數,在OpenGL中,我們使用glVertexAttribPointer函式告訴OpenGL該如何決議頂點資料,即將這些資料標識具體的頂點屬性,代碼如下:

// 函式原型:glVertexAttribPointer(GLuint index, GLint size, GLenum type,
//              GLboolean normalized, GLsizei stride, const void *pointer);
//
// 函式作用:定義一個頂點屬性資料
// 引數說明:
// index 指定要配置的某個頂點屬性索引index,如著色器原始碼中layout(location=0)
//       表示將位置屬性索引設定為0,當我們希望將資料傳遞到這個位置屬性中,
//       直接傳入這個位置屬性的索引0即可;
// size 指定某個頂點屬性的大小,比如位置屬性,是一個vec3,因此大小是3;
// type 指定頂點屬性的型別,比如位置屬性,每個分量資料型別為浮點型;
// normalized 設定資料是否被標準化(歸一化),即資料被映射到-1~1之間;
// stride 指定步長(Stride)大小;
// pointer 指定頂點屬性資料在快取中起始位置的偏移量(offset);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

// 函式原型:glEnableVertexAttribArray(GLuint index);
//
// 函式作用:啟用頂點屬性
// 引數說明:
// index 頂點屬性索引
glEnableVertexAttribArray(0);

1.3.2 VAO

?VAO,Vertex Array Object,即頂點陣列物件,該物件存盤的是一系列指向頂點屬性存盤地址的指標,如上上圖所示,以VAO2為例,它的atttribute pointer 0指向VBO2中第一個頂點的位置屬性(pos[0])的存盤地址,atttribute pointer 1指向VBO2中第一個頂點的顏色屬性(col[0])的存盤地址,atttribute pointer 1指向VBO2中第二個頂點的位置屬性(pos[0])的存盤地址等等,使用頂點陣列物件的好處時,當我們配置鏈接好頂點屬性后,只需要初始化的時候執行一次,再后續的繪制物體的時候就無需再執行一遍拷貝資料到顯存,鏈接頂點屬性,而是直接系結相應的VAO即可,VAO創建程序如下:

  • 首先,創建一組VAO物件,并為每個VAO物件生成一個對應的唯一ID;
unsigned int mVAOId;
// 函式原型:glGenVertexArrays(GLsizei n, GLuint *arrays);
//
// 函式作用:創建一組VAO物件
// 引數說明:
// n 表示VAO物件的數目
// arrays 指定這些VAO物件對應的唯一ID,是一個陣列;
glGenVertexArrays(1, &mVAOId);
  • 然后,呼叫glBindVertexArray系結VAO,
// 函式原型: glBindVertexArray(GLuint array);
//
// 函式作用:系結一個VAO物件
// 引數說明:
// array 表示一個VAO物件對應的ID
glBindVertexArray(mVAOId);
  • 最后,釋放VAO物件所占資源,
// 函式原型:glDeleteVertexArrays(GLsizei n, GLuint *arrays);
//
// 函式作用:創建一組VAO物件
// 引數說明:
// n 表示VAO物件的數目
// arrays 指定這些VAO物件對應的唯一ID,是一個陣列
glDeleteVertexArrays(1, &mVAOId);

1.3.3 EBO

?EBO,Element Buffer Object,即索引快取物件,顧名思義,索引緩沖物件是用來存盤索引資料的,所謂索引資料,是指一組索引,而這個索引就是頂點資料在陣列中的下標,索引緩沖物件的作用,就是用于解決繪制一個物體時同一個頂點被重復指定的問題,這對于擁有上千個三角形來說,作業量是巨大的,比如,我們要繪制一個矩形,在OpenGL中通常通過繪制兩個三角形實作,因為OpenGL主要處理三角形,此時輸入的頂點資料可為:

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   // 左上角
};

?"聰明絕頂"的你應該知道,兩個三角形共用斜邊,這就意味著矩形的左下角和右上角的頂點被指定兩次,如上述陣列可知,那么有沒有一種方法,頂點陣列中只包含不同的頂點,當需要繪制有重復的頂點時,我們只需要通過陣列的下標將頂點資料取出來就好,有!這個方法就是索引緩沖物件,OpenGL呼叫這些頂點的索引來決定該繪制哪個頂點,

// 頂點陣列
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  // 第二個三角形
};

EBO創建、使用程序:

  • 首先,創建一組EBO物件,并分別指定對應的唯一ID;
unsigned int mEBOId;
// 函式原型:glGenBuffers(GLsizei n, GLuint *buffers);
//
// 函式作用:創建一組緩沖物件,并指定各自對應的唯一ID,
// 引數說明:這里創建的是EBO物件
// n 表示EBO物件的數目
// buffers 指定這些EBO物件對應的唯一ID;
glGenBuffers(1, &mEBOId);
  • 其次,系結EBO,再呼叫glBufferData將索引資料拷貝到索引緩沖區中,其中緩沖目標設定為GL_ELEMENT_ARRAY_BUFFER
// 函式原型:glBindBuffer(GLenum target, GLuint buffer);
// 
// 函式作用:系結一個命名的緩沖區物件
// 引數說明:
// target 指定緩沖物件被系結到的目標型別,EBO的目標型別為GL_ELEMENT_ARRAY_BUFFER
// buffer 指定要系結緩沖物件的ID
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBOId);

// 函式原型:glBufferData(GLenum target,GLsizeiptr size,const void * data, GLenum usage);
// 
// 函式作用:把資料拷貝到緩沖區,這里是把索引資料拷貝到EBO
// 引數說明:
// target 指定緩沖物件被系結到的目標型別;
// size 指定索引資料的大小,以位元組為單位;
// data 指定索引資料
// usage 指定GPU如何管理這些資料,有三種形式:  
//       GL_STATIC_DRAW :資料不會或幾乎不會改變;
//       GL_DYNAMIC_DRAW:資料會被改變很多;
//       GL_STREAM_DRAW :資料每次繪制時都會改變;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
  • 第三,使用索引緩沖物件的索引資料,繪制圖形,使用前需要系結EBO,glDrawElements函式從當前系結到GL_ELEMENT_ARRAY_BUFFER目標的EBO中獲取索引,由于VAO系結時正在系結的索引緩沖物件會被保存為VAO的元素緩沖物件,系結VAO的同時也會自動系結EBO,因此,我們再使用索引渲染一個物體時只需要再初始化時系結相應的EBO即可,
// 系結一個EBO物件
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBOId);

// 函式原型:void glDrawElements(GLenum mode,GLsizei count,GLenum type,const void * indices);
//
// 函式作用:從陣列獲取資料繪制圖元
// 引數說明:
// mode 指定渲染的圖元型別,比如三角形為GL_TRIANGLES;
// count 指定渲染元素的數量,本例正方形為6個頂點;
// type 指定索引資料元素型別,本例為無符號整型(unsigned int);
// indices 指定一個指向索引存盤位置的指標,本例起始位置為0;
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
  • 最后,釋放緩沖物件所占資源,
// 函式原型:glDeleteBuffers(GLsizei n, GLuint *buffers);
//
// 函式作用:釋放緩沖物件所占資源
// 引數說明:
// n 表示緩沖物件的數目,這里表示EBO物件的數目;
// buffers 指定這些緩沖對應唯一ID,這里表示一組EBO物件對應的ID;
glDeleteBuffers(1, &mEBOId);

?下圖演示了EBO在記憶體中的存盤:
在這里插入圖片描述

?從上圖可知,EBO物件會被保持為VAO的元素緩沖物件,

2. OpenGL實戰

?CPU與GPU通信程序:

在這里插入圖片描述

?根據上圖可知,OpenGL的作業程序:

  • 首先,我們從CPU將要繪制的形狀頂點資料和紋理資料傳遞到GPU顯存中,在GPU顯存中主要是通過VBO、VAO和EBO來管理這些資料;
  • 其次,分別創建頂點著色器和片段著色器,并將頂點資料輸入給頂點著色器處理;
  • 第三,創建一個程式物件,并將編譯好的著色器附加到這個程式物件,再將這些著色器鏈接起來;
  • 最后,繪制圖形,

2.1 案例1:繪制三角形

1. 創建著色器

(1)創建著色器Shader物件,并回傳該物件對應的唯一ID;

GLuint mShaderId = NULL;

// 函式原型:GLuint glCreateShader(	GLenum shaderType)
//
// 函式作用:創建一個Shader物件
// 引數說明:
// shaderType 指定要創建的Shader物件型別,
//            比如 GL_VERTEX_SHADER 表示頂點著色器
//                 GL_FRAGMENT_SHADER 表示片段著色器
// mShaderId = glCreateShader(GL_VERTEX_SHADER);
mShaderId = glCreateShader(GL_FRAGMENT_SHADER);

(2)將著色器原始碼附著到著色器,由于該案例是創建一個漸變色的三角形,因此,我們將頂點著色器的輸出作為片段著色器的輸入,以實作每個像素的顏色值盡可能不一致;

// 頂點著色器原始碼
const char* mVertexShaderStr = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n" // 頂點著色器輸入
"out vec3 outPos;\n" // 頂點著色器輸出
"void main()\n"
"{\n"
"	outPos = aPos;"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";

// 片段著色器原始碼
const char* mFragmentShaderStr = "#version 330 core\n"
"out vec4 rgbColor;\n" // 片段著色器輸出
"in vec3 outPos;\n" // 以頂點著色器的輸出變數作為輸入
"void main()\n"
"{\n"
"   rgbColor = vec4(outPos, 1.0f);\n"
"}\n\0";

// 函式原型:glShaderSource(GLuint shader,GLsizei count,const GLchar **string,const GLint *length);
//
// 函式作用:替換著色器物件原始碼(將著色器原始碼附著到著色器物件中)
// 引數說明: 
// shader 著色器物件ID;
// count 指定字串數量或者陣列中字串中的數量,本例字串數為1;
// string 著色器原始碼
// length 著色器原始碼string長度(以位元組為單位),通常傳入NULL會自動計算;
glShaderSource(mShaderId, 1, &shaderStr, NULL);

(3)編譯著色器物件,實質是編譯著色器物件中存盤的原始碼,以便于著色器程式能夠鏈接它,

// 函式原型:void glCompileShader(GLuint shader);
//
// 函式作用:編譯一個著色器物件
// 引數說明: 
// shader 指定要編譯的著色器物件ID;
glCompileShader(mShaderId);

2. 創建程式Program

(1)創建一個程式物件,glCreateProgram()函式將創建一個空的程式物件,之前創建的著色器物件將都附著在這個程式物件上;

GLuint mShaderProgramId = NULL;
mShaderProgramId = glCreateProgram();

(2)將編譯好的著色器物件附著到程式物件;

// 函式原型:void glAttachShader(GLuint program, GLuint shader);
//
// 函式作用:附著著色器物件到程式物件
// 引數說明:
// program 創建的程式物件ID;
// shader 要附著到程式物件的著色器物件ID;
glAttachShader(mShaderProgramId, vertexShaderId);
glAttachShader(mShaderProgramId, fragmentShaderId);

(3)鏈接程式物件,只有在鏈接程式物件成功后,才能在后續呼叫glUseProgram函式使用該程式物件,從而讓著色器生效,當然,鏈接程式物件成功后,我們可以進行釋放著色器物件操作,以釋放相關資源,

// 函式原型:void glLinkProgram(GLuint program);
//
// 函式作用:鏈接程式物件
// 引數說明:
// program 創建的程式物件ID;
glLinkProgram(mShaderProgramId);

// 洗掉著色器物件
glDeleteShader(vertexShaderId);
glDeleteShader(fragmentShaderId);

3. 拷貝資料到顯存,并繪制三角形

(1)創建VBO、VAO物件,拷貝頂點資料到GPU顯存并鏈接頂點屬性;

// 1. 創建VAO物件
glGenVertexArrays(1, &mVAOId);

// 2. 創建VBO,拷貝資料到GPU顯存,再配置頂點屬性
// (1) 分別創建VAO物件
GLuint vboId = 0;
glGenBuffers(1, &vboId);
// (2) 系結VAO物件
glBindVertexArray(mVAOId);
// (3) 將新創建的緩沖系結到頂點緩沖型別GL_ARRAY_BUFFER上
glBindBuffer(GL_ARRAY_BUFFER, vboId);
// (4) 將頂點資料復制到緩沖的顯存供OpenGL使用
// 并指定顯卡管理資料模式為GL_STATIC_DRAW,即資料不會或幾乎不會改變
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
// (5) 鏈接頂點屬性
glVertexAttribPointer(layout, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(layout);
// (6) 解綁VAO, VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

(2)使用程式物件,并系結VAO物件,然后呼叫glDrawArray函式繪制三角形,

// 函式原型:void glUseProgram(GLuint program);
//
// 函式作用:使用程式物件
// 引數說明:
// program 創建的程式物件ID
glUseProgram(mShaderProgramId);

// 函式原型:void glBindVertexArray(GLuint id);
//
// 函式作用:系結VAO物件
// 引數說明:
// id 創建的VAO物件對應的ID;
glBindVertexArray(mVAOId);

// 函式原型:void glDrawArrays(GLenum mode,GLint first,GLsizei count);
//
// 函式作用:繪制渲染形狀
// 引數說明:
// mode 指定渲染的圖元,比如三角形為GL_TRIANGLES;
// first 第一個元素的陣列下標,本例第一個頂點從0開始;
// count 繪制元素的數量,本例元素為3個頂點;
glDrawArrays(GL_TRIANGLES, 0, 3);

4. 釋放資源

// 釋放著色器物件資源
if (mShaderId != NULL) {
	glDeleteShader(mShaderId);
	mShaderId = NULL;
}

// 釋放VAO資源
if (mVAOId != NULL) {
	glDeleteVertexArrays(1, &mVAOId);
	mVAOId = NULL;
}

// 釋放VBO資源
if (vboId != NULL) {
	glDeleteBuffers(1, &vboId);
	vboId = NULL;
}

// 釋放程式物件資源
if (mShaderProgramId != NULL) {
	glDeleteProgram(mShaderProgramId);
	mShaderProgramId = NULL;
}

在這里插入圖片描述
.

2.2 案例2:繪制正方形

?繪制正方形案例演示了如何使用EBO物件,現在我們在案例1代碼的基礎上作如下處理,

(1)創建EBO物件,在鏈接頂點屬性之前將索引資料拷貝到EBO快取;

// 頂點陣列
float data[] = {
	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  // 第二個三角形
};

// 1. 創建VAO物件
glGenVertexArrays(1, &mVAOId);

// 2. 創建VBO,拷貝資料到GPU顯存,再配置頂點屬性
// (1) 分別創建VAO物件
GLuint vboId = 0;
glGenBuffers(1, &vboId);
// (2) 系結VAO物件
glBindVertexArray(mVAOId);
// (3) 將新創建的緩沖系結到頂點緩沖型別GL_ARRAY_BUFFER上
glBindBuffer(GL_ARRAY_BUFFER, vboId);
// (4) 將頂點資料復制到緩沖的顯存供OpenGL使用
// 并指定顯卡管理資料模式為GL_STATIC_DRAW,即資料不會或幾乎不會改變
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);

// 創建EBO物件
glGenBuffers(1, &mEBOId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBOId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// (5) 鏈接頂點屬性
glVertexAttribPointer(layout, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(layout);
// (6) 解綁VAO, VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

(2)呼叫glDrawElements函式使用索引資料渲染圖元;

glUseProgram(mShaderProgramId);
glBindVertexArray(mVAOId);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

?程式執行結果:

3. 參考文獻

1. LearnOpenGL中文檔案
2. OpenGL3 Reference Pages


Github原始碼:LearnOpenGL(如果覺得有用,記得給個小star哈~)

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

標籤:AI

上一篇:鴻蒙系統展示了華為的野心,難怪谷歌害怕

下一篇:Kubernetes — 在 OpenStack 上使用 kubeadm 部署高可用集群

標籤雲
其他(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