[OpenGL紅寶書]第一章 OpenGL概述

第一章 OpenGL概述

標籤(空格分隔): OpenGL


1.1 什麼是OpenGL

OpenGLwikipedia是一種應用程序編程接口(API),它是一種可以對圖形硬件設備特性進行訪問的軟件庫。

一個用來渲染圖像的OpenGL程序需要執行的主要操作如下:
1. 從OpenGL的幾何圖元中設置數據,用於構建形狀。
2. 使用不同的着色器(shader)對輸入的圖元數據執行計算操作,判斷它們的位置、顏色,以及其他渲染屬性。
3. 將輸入圖元的數學描述轉化爲與屏幕位置對應的像素片元(fragment)。這一步也稱爲光柵化(rasterization)。
4. 最後,針對光柵化過程產生的每個片元,執行片元着色器(fragment shader),從而決定這個片元的最終顏色和位置。
5. 如果有必要,還需要對每個片元執行一些額外的操作,例如判斷片元對應的對象是否可見,或者將片元的顏色與當前屏幕位置的顏色進行融合。

何爲光柵化(rasterization)?
將輸入圖元的數學描述轉換爲與屏幕位置對應的像素片元,稱爲光柵化。

1.2 初識OpenGL程序

一些圖形學名詞:
1. 幾何圖元,包括點、線、三角形以及Patch。
2. 渲染(render),表示計算機從模型創建最終圖像的過程。
3. 着色器(shader),專爲圖形處理單元(GPU)編譯的一種小型程序。
4. 四種不同的着色階段(shander stage),其中最常用的包括頂點着色器(vertex shader)以及片元着色器,前者用於處理頂點數據,後者用於處理光柵化後的片元數據。所有OpenGL程序都需要用到這兩類着色器。
5. 幀緩存(framebuffer),像素(pixel),是顯示器上最小的可見單元。計算機系統將所有的像素保存到幀緩存當中,後者是有圖形硬件設備管理的一塊獨立內存區域,可以直接映射到最終的顯示設備上。

例1.1 第一個OpenGL程序triangles.cpp
代碼(堅果雲)
在VS 2013上實現,其中包含了紅書官網所提供的源文件中的頭文件,以及其中的庫文件。運行結果如下:
運行結果

1.3 OpenGL語法

本書所使用的GLUT(OpenGL Utility Toolkit)版本爲Freeglut,是原始GLUT庫的一個新變種。

1.4 OpenGL渲染管線

rendering pipeline,它是一系列數據處理過程,並且將應用程序的數據轉換到最終渲染的圖像。下圖爲OpenGL4.3版本的管線。包括:
頂點數據,頂點着色器,細分着色器(細分控制着色器,細分計算着色器),幾何着色器,圖元設置,剪切,光柵化,片元着色器
RenderingPipeline

1.4.1 準備向OpenGL傳輸數據

OpenGL需要將所有的數據都保存到緩存對象(buffer object)中。
我們可以使用多種方式創建這樣的數據緩存,最常用的是glBufferData()wiki

Buffer Objects are OpenGL Objects that store an array of unformatted memory allocated by the OpenGL context (aka: the GPU). These can be used to store vertex data, pixel data retrieved from images or the framebuffer, and a variety of other things.
–from wiki

1.4.2 將數據傳輸到OpenGL

當將緩存初始化完畢後,通過調用OpenGL的一個繪製命令來請求渲染幾何圖元。glDrawArrays()wiki就是一個常用的繪製命令。OpenGL的繪製通常就是將頂點數據傳輸到OpenGL服務端。

1.4.3 頂點着色

對於繪製命令傳輸的每個頂點,OpenGL都會調用一個頂點着色器來處理頂點相關的數據。
只是將數據複製並傳遞到下一個着色階段,叫做傳遞着色器(pass-through shader)。
通常來說,一個複雜的應用程序可能包含許多頂點着色器,但在同一時刻只能有一個頂點着色器起作用。

1.4.4 細分着色

頂點着色器處理每個頂點的關聯數據之後,如果同時激活了細分着色器,那麼它將進一步處理這些數據。(第9章介紹)
細分着色器階段會用到兩個着色器來分別管理Patch數據併產生最終的形狀。

1.4.5 幾何着色

第10章介紹。

1.4.6 圖元裝配

圖元裝配將頂點及相關的集合圖元之間組織起來,準備下一步剪切和光柵化操作。

1.4.7 剪切

頂點可能落在視口(viewport)之外,此時與頂點相關的圖元會做出改動,以保證相關的像素不會在視口外繪製。剪切(clipping)由OpenGL自動完成。

1.4.8 光柵化

將更新後的圖元(primitive)傳遞到光柵化單元,生成對應的片元(fragment)。我們將一個片元是爲一個“候選的像素”。也就是可以放置在幀緩存(framebuffer)中的像素,但是它也可能被最終剔除,不再更新對應的像素位置。之後兩個階段將會執行片元的處理。

1.4.9 片元着色

最後一個可以通過編程控制屏幕上顯示顏色的階段。在Fragment Shader階段中,我們使用着色器計算片元的最終顏色和它的深度值。

頂點着色器與片元着色器之間的區別:
頂點着色(包括細分和幾何着色)決定了一個圖元應該位於屏幕的什麼位置,而片元着色使用這些信息來決定某個片元的顏色應該是什麼。

1.4.10 逐片元的操作

在這個階段會使用深度測試(depth test,或者通常也稱爲z-bufffering)和模板測試(stencil test)的方式來決定一個片元是否是可見的。

1.5 第一個程序:深入分析

1.5.1 進入main()函數

int main (int argc, char ** argv)
{
    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_RGBA);
    glutInitWindowSize (512, 512);
    glutInitContextVersion (4, 3);                  
    glutInitContextProfile (GLUT_CORE_PROFILE); 
    glutCreateWindow (argv[0]);
    glewExperimental = GL_TRUE;     

    if (glewInit ()) {
        cerr << "Unable to initialize GLEW ... exiting..." << endl;
        exit (EXIT_FAILURE);
    }

    init ();

    glutDisplayFunc (display);

    glutMainLoop ();

前面的6行使用GLUT(OpenGL Utility Toolkit)初始化和打開了一個渲染用的窗口:
1. glutInit()負責初始化GLUT庫,負責設置其他GLUT例程所必須的數據結構。
2. glutInitDisplayMode()設置了程序所使用的窗口的類型。在這個例子中只設置了窗口使用的RGBA顏色空間。
3. glutInitWindowSize()設置所需的窗口大小。
4. glutInitContextVersion()、glutInitContextProfile設置所需的OpenGL環境(context)的類型。這個例子中使用OpenGL 4.3版本的核心模式(core profile)來創建環境。這個模式確保使用的只是OpenGL的最新特性,否則也可以使用兼容模式,這樣自OpenGL 1.0以來的所有特性都可以在程序中使用。
5. glutCreateWindow(),如果當前的系統環境可以滿足glutInitDisplayMode()的顯示模式要求,這裏就會創建一個窗口(此時會調用計算機窗口系統的接口)。只有GLUT創建了一個窗口之後(其中包含創建創建OpenGL環境的過程),我們纔可以使用OpenGL相關的函數。
接下來會調用glewInit()函數,屬於另一個輔助庫GLEW(OpenGL Extention Wrangler)。GLEW可以簡化獲取函數地址的過程,並且包含了可以跨平臺使用的其他一些OpenGL編程方法。

到這,完成了使用OpenGL之前的全部設置工作。之後init()函數初始化OpenGL相關的所有數據。在之後完成渲染工作。

  1. glutDisplayFunc(),它設置了一個顯示回調(diplay callback),即GLUT在每次更新窗口內容的時候回自動調用該例程。
  2. glutMainLoop(),這是一個無限執行的循環,它會負責一直處理窗口和操作系統的用戶輸入等操作。(注意:不會執行在glutMainLoop()之後的所有命令。)

1.5.2 OpenGL的初始化過程

void init (void)
{
    glGenVertexArrays (NumVAOs, VAOs);
    glBindVertexArray (VAOs[Triangles]);
    GLfloat vertices[NumVertices][2] = {
        { -0.90, -0.90 },       // Triangle 1
        { 0.85, -0.90 },
        { -0.90, 0.85 },
        { 0.90, -0.85 },        // Triangle 2,
        { 0.90,  0.90 },
        { -0.85, 0.90 }
    };

    glGenBuffers (NumBuffers, Buffers);
    glBindBuffer (GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
    glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
    ShaderInfo shaders[] = {                            
        { GL_VERTEX_SHADER, "triangles.vert" },
        { GL_FRAGMENT_SHADER, "triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders (shaders); 
    glUseProgram (program);
    glVertexAttribPointer (vPosition, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET (0));
    glEnableVertexAttribArray (vPosition);
}

初始化頂點數組對象

  • glGenVertexArrays(NumVAOs, VAOs)分配頂點數組對象(vertext array object)。OpenGL會因此分配一部分(NumVAOs個)頂點數組對象的名稱供我們使用,保存到數組VAOs中。

void glGenVertexArrays(GLsizei n​, GLuint *arrays​);
n
Specifies the number of vertex array object names to generate.
arrays
Specifies an array in which the generated vertex array object names are stored.
The names returned in arrays​ are marked as used, for the purposes of glGenVertexArrays only, but they acquire state and type only when they are first bound.

很多OpenGL命令都是glGen*的形式,它們負責分配不同類型的OpenGL對象的名稱。這裏的名稱類似C語言中的指針變量,必須分配內存並且用名稱引用它之後,名稱纔有意義。在OpenGL中,這個分配的機制叫做綁定對象(bind an object)。這通過一系列glBind*形式的OpenGL函數集合去實現。

  • glBindVertexArray (VAOs[Triangles])創建一個頂點數組對象,並與其名稱(VAOs[Triangles])關聯起來。

    void glBindVertexArray(GLuint array​);
    array
    Specifies the name of the vertex array to bind.

  • 當我們第一次綁定對象時(例如,第一次用指定的對象名作爲參數調用glBind*()),OpenGL內部會分配這個對象所需的內存並且將它作爲當前對象,即後繼的操作都會作用於這個被綁定的對象。例如,這裏的頂點數組對象就會被後面執行的代碼所改變。
    有兩種情況我們需要綁定一個對象:

    1. 創建對象並初始化它所對應的數據時。
    2. 每次我們準備使用這個對象,而它並不是當前綁定的對象時。
  • (未使用)glDeleteVertexArrays(),當我們完成對頂點數組對象的操作之後,可以調用此函數將它(們)釋放。

    void glDeleteVertexArrays(GLsizei n​, const GLuint *arrays​);
    n
    Specifies the number of vertex array objects to be deleted.
    arrays
    Specifies the address of an array containing the n​ names of the objects to be deleted.

  • (未使用)glIsVertexArray(),檢查某個名稱是否已經關聯到一個頂點數組對象了。

    GLboolean glIsVertexArray(GLuint array​);
    array
    Specifies a value that may be the name of a vertex array object.

分配頂點緩存對象

頂點數組對象(VAO)負責保存一系列頂點的數據。這些數據保存到緩存對象(Buffer Object)中,並且由當前綁定的頂點數組對象管理。

官方解釋:A Vertex Array Object (VAO) is an OpenGL Object that stores all of the state needed to supply vertex data (with one minor exception noted below). It stores the format of the vertex data as well as the Buffer Objects providing the vertex data arrays.

  • glGenBuffers (NumBuffers, Buffers) 返回NumBuffers個當前未使用的緩存對象名稱到數組Buffers中。(名稱不一定是連續的整型數據)

    void glGenBuffers(GLsizei n​, GLuint * buffers​);
    n
    Specifies the number of buffer object names to be generated.
    buffers
    Specifies an array in which the generated buffer object names are stored.
    No buffer objects are associated with the returned buffer object names until they are first bound by calling glBindBuffer​.

  • glBindBuffer (GL_ARRAY_BUFFER, Buffers[ArrayBuffer])在分配緩存的名稱之後,就可以調用glBindBuffer()來綁定它們了。由於OpenGL中有很多種不同類型的緩存對象,因此綁定一個緩存時,需要指定它所對應的類型。此例中由於是將頂點數據保存到緩存中(故爲頂點緩存對象VBO),因此使用GL_ARRAY_BUFFER類型。

    void glBindBuffer(GLenum target​, GLuint buffer​);
    target
    Specifies the target buffer object. The symbolic constant must be GL_ARRAY_BUFFER, GL_ATOMIC_COUNTER_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, GL_DRAW_INDIRECT_BUFFER, GL_DISPATCH_INDIRECT_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER, GL_QUERY_BUFFER, GL_SHADER_STORAGE_BUFFER, GL_TEXTURE_BUFFER, GL_TRANSFORM_FEEDBACK_BUFFER, or GL_UNIFORM_BUFFER.
    buffer
    Specifies the name of a buffer object.
    When a buffer object is bound to a target, the previous binding for that target is automatically broken.

  • (未使用)glDeleteBuffers(),所有的緩存對象都可以使用glDeleteBuffers()直接釋放。

    void glDeleteBuffers(GLsizei n​, const GLuint * buffers​);
    n
    Specifies the number of buffer objects to be deleted.
    buffers
    Specifies an array of buffer objects to be deleted.

  • glIsBuffer(),使用此函數來判斷一個整數值是否爲緩存對象的名稱。

    GLboolean glIsBuffer(GLuint buffer);
    buffer
    Specifies a value that may be the name of a buffer object.
    A name returned by glGenBuffers​, but not yet associated with a buffer object by calling glBindBuffer​, is not the name of a buffer object.

將數據載入緩存對象

  • glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW),初始化頂點緩存對象之後(VBO),我們需要把頂點數據傳輸到緩存對象中。它主要有兩個任務:分配頂點數據所需的存儲空間(內存中),然後將數據從應用程序的數組中拷貝到OpenGL服務端的內存中。
    void glBufferData(GLenum target​, GLsizeiptr size​, const GLvoid * data​, GLenum usage​);
    target
    Specifies the target buffer object. The symbolic constant must be GL_ARRAY_BUFFER, GL_ATOMIC_COUNTER_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, GL_DRAW_INDIRECT_BUFFER, GL_DISPATCH_INDIRECT_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER, GL_QUERY_BUFFER, GL_SHADER_STORAGE_BUFFER, GL_TEXTURE_BUFFER, GL_TRANSFORM_FEEDBACK_BUFFER, or GL_UNIFORM_BUFFER.
    size
    Specifies the size in bytes of the buffer object’s new data store.
    data
    Specifies a pointer to data that will be copied into the data store for initialization, or NULL if no data is to be copied.
    usage
    Specifies the expected usage pattern of the data store. The symbolic constant must be GL_STREAM_DRAW, GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW, GL_DYNAMIC_READ, or GL_DYNAMIC_COPY.

初始化頂點與片元着色器

  • 對於每一個OpenGL程序,當它所使用的OpenGL版本高於或等於3.1時,就需要指定至少兩個着色器:頂點着色器和片元着色器。這個例子中,我們使用LoadShaders()來實現這個要求。
    對於OpenGL程序員而言,着色器就是使用OpenGL着色語言(GLSL,OpenGL Shading Language)編寫的一個小型函數。

例1.2 triangles.cpp對應的頂點着色器:triangles.vert

#version 430 core
layout (location = 0) in vec4 vPosition;
void
main ()
{
        gl_Position = vPosition;
}

事實上這就是我們所說的傳遞着色器(pass-through shader)。它只負責將輸入數據拷貝到輸出數據中。
第一行

 #version 430 core

指定了OpenGL着色語言的版本。每個着色器的第一行都應該設置#version,否則就會假設使用“110”版本。
下一步,分配了一個着色器變量,着色器變量是着色器與外部世界的聯繫所在。着色器並不知道自己的數據從哪裏來,它只是在每次運行時直接獲取數據對應的輸入變量。而我們必須自己完成着色管線的裝配,然後纔可以將應用程序中的數據與不同的OpenGL着色階段相互關聯。

layout (location = 0) in vec4 vPosition;
  • vPosition 是變量的名稱。這個變量保存的是頂點的位置信息。(字符“v”作爲這個頂點屬性名稱的前綴)
  • vec4,是vPosition的類型,在這裏它是一個GLSL的四維浮點數向量。、
  • in,它指定了數據進入着色器的流向。
  • layout(location = 0),叫做佈局限定符(layout qualifier),目的是爲變量提供元數據(meta data)。這裏設置vPosition的位置屬性location爲0。這個設置與init()函數的最後兩行會共同起作用。

最後,在着色器的main()函數中實現它的主體部分。對於這個着色器而言,它所實現的就是將輸入的頂點位置(存在vPosition中)複製到頂點着色器的指定輸出位置gl_Position

例1.3 triangles.cpp對應的片元着色器:triangles.frag

@version 430 core
out vec4 fColor
void
main()
{
    fColor = vec4(0.0, 0.0, 1.0, 1.0);
}
  • 變量名爲fColor,使用了out限定符,在這裏着色器將會把fColor對應的數值輸出,而這也就是片元所對應的顏色值。(因此這裏前綴“f”)
  • OpenGL使用了RGBA顏色空間,A即aplha值(透明度)。1.0爲不透明。

init()中的最後兩個函數指定了頂點着色器的變量與我們存儲在緩存對象中的數據的關係。這也就是我們所說的着色管線的裝配過程,即將程序與着色器之間,以及不同着色階段之間的數據通道連接起來。

  • glVertexAttribPointer (vPosition, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET (0));
    爲了輸入頂點着色器所需的數據,也就是OpenGL將要處理的所有頂點數據,需要在着色器中聲明一個in變量,然後使用glVertexAttribPointer()將它關聯到一個頂點屬性數組。glVertexAttribPointer()是一個非常靈活的命令,只要內存中的數據是規範組織的(保存在一個連續的數組中,不使用其他基於節點的容器,如鏈表),我們就可以使用glVertexArrtibPointer()告訴OpenGL直接從內存中獲取數據。

    void glVertexAttribPointer(GLuint index​, GLint size​, GLenum type​, GLboolean normalized​, GLsizei stride​, const GLvoid * pointer​);
    void glVertexAttribIPointer(GLuint index​, GLint size​, GLenum type​, GLsizei stride​, const GLvoid * pointer​);
    void glVertexAttribLPointer(GLuint index​, GLint size​, GLenum type​, GLsizei stride​, const GLvoid * pointer​);
    index
    Specifies the index of the generic vertex attribute to be modified.
    size
    Specifies the number of components per generic vertex attribute. Must be 1, 2, 3, 4. Additionally, the symbolic constant GL_BGRA is accepted by glVertexAttribPointer. The initial value is 4.
    type
    Specifies the data type of each component in the array. The different functions take different values.
    glVertexAttribPointer and glVertexAttribIPointer both take: GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, and GL_UNSIGNED_INT
    glVertexAttribPointer also can take: GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, GL_FIXED, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, and GL_UNSIGNED_INT_10F_11F_11F_REV.
    glVertexAttribLPointer takes only GL_DOUBLE.
    The initial value is GL_FLOAT.
    normalized
    For glVertexAttribPointer, specifies whether fixed-point data values should be normalized (GL_TRUE) or converted directly as fixed-point values (GL_FALSE) when they are accessed.
    stride
    Specifies the byte offset between consecutive generic vertex attributes. If stride​ is 0, the generic vertex attributes are understood to be tightly packed in the array. The initial value is 0.
    pointer
    Specifies a offset of the first component of the first generic vertex attribute in the array in the data store of the buffer currently bound to the GL_ARRAY_BUFFER target. The initial value is 0.

  • glEnableVertexAttribArray (vPosition);
    我們通過glEnableVertexAttribArray()來啓用頂點屬性數組。glVertexAttribPointer()初始化的屬性數組指針索引傳入這個函數。

    void glEnableVertexAttribArray(GLuint index​);
    void glDisableVertexAttribArray(GLuint index​);
    index
    Specifies the index of the generic vertex attribute to be enabled or disabled.

1.5.3 第一次使用OpenGL進行渲染

void display (void)
{
    glClear (GL_COLOR_BUFFER_BIT);
    glBindVertexArray (VAOs[Triangles]);
    glDrawArrays (GL_TRIANGLES, 0, NumVertices);
    glFlush ();
}


  • glClear(GL_COLOR_BUFFER_BIT),清除幀緩存的數據。

void glClear(GLbitfield mask​);
mask
Bitwise OR of masks that indicate the buffers to be cleared. The three masks are GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, and GL_STENCIL_BUFFER_BIT.

  • (未使用)glClearColor(GLfloat red​, GLfloat green​, GLfloat blue​, GLfloat alpha​),設置當前使用的清除顏色值,用於RGBA模式下對顏色緩存的清除工作。

    清除顏色本身也是OpenGL狀態機制的一個例子,它的數值會一直保留在當前OpenGL環境當中。
    OpenGL有一個龐大的狀態量列表。當創建一個新的OpenGL環境時,所有的狀態量都會被初始化爲默認值。因爲OpenGL會保留所有更改的狀態值,所以我們可以減少設置狀態數值的次數。
    故而,在設置清除顏色爲白色時,可以在display()函數中調用glClearColor(1, 1, 1, 1),也可以在init()函數中調用glClearColor(1, 1, 1, 1),後者效率更高,因爲可以避免冗餘的狀態切換。

  • glBindVertexArray (VAOs[Triangles]),選擇作爲頂點數據使用的頂點數組。我們可以使用這個函數來切換程序中保存的多個頂點數據對象集合。

  • glDrawArrays (GL_TRIANGLES, 0, NumVertices),使用當前綁定的頂點數組元素來建立一系列的幾何圖元。實現頂點數據向OpenGL管線的傳輸。

    glDrawArrays: render primitives from array data
    void glDrawArrays(GLenum mode​, GLint first​, GLsizei count​);
    mode
    Specifies what kind of primitives to render. Symbolic constants GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_TRIANGLE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY and GL_PATCHES are accepted.
    first
    Specifies the starting index in the enabled arrays.
    count
    Specifies the number of indices to be rendered.

  • glFlush (),強制所有進行中的OpenGL命令立即完成並傳輸到OpenGL服務端處理。

    glFlush: force execution of GL commands in finite time
    void glFlush(void​);

  • (未使用)glFinish(),會等待所有當前的OpenGL操作完成。而glFlush()則只是強制所有運行中的命令送入OpenGL服務端而已,不會等待所有的命令完成。因此當我們需要了解OpenGL是在是麼時候完成操作的時候使用glFinish()。

    glFinish: block until all GL execution is complete
    void glFinish(void​);
    glFinish does not return until the effects of all previously called GL commands are complete. Such effects include all changes to GL state, all changes to connection state, and all changes to the frame buffer contents.

  • (未使用)glEnable(),glDisable(),啓用或禁用OpenGL的操作模式。

    glEnable: enable or disable server-side GL capabilities
    void glEnable(GLenum cap​);
    void glDisable(GLenum cap​);
    cap
    Specifies a symbolic constant indicating a GL capability.

  • (未使用)glIsEnabled(),返回是否啓用指定模式。

    glIsEnabled, glIsEnabledi: test whether a capability is enabled
    GLboolean glIsEnabled(GLenum cap​);
    GLboolean glIsEnabledi(GLenum cap​, GLuint index​);
    cap
    Specifies a symbolic constant indicating a GL capability.
    index
    Specifies the index of the capability.


  • 相關鏈接:
    - OpenGL WIKI
    - 關於VAO && VBO官方
    - 關於VAO && VBO博客
    - GLUT API
    - FreeGLUT API
    - GLEW 官網

    發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章