面試中常常被問到的 OpenGL ES 對象,你知道的有哪些?

面試中常常被問到的 OpenGL ES 對象,你知道的有哪些?


VBO 和 EBO

VBO(Vertex Buffer Object)是指頂點緩衝區對象,而 EBO(Element Buffer Object)是指圖元索引緩衝區對象,VBO 和 EBO 其實是對同一類 Buffer 按照用途的不一樣稱呼。web

OpenGL ES 2.0 編程中,用於繪製的頂點數組數據首先保存在 CPU 內存,在調用 glDrawArrays 或者 glDrawElements 等進行繪製時,須要將頂點數組數據從 CPU 內存拷貝到顯存。面試

可是不少時候咱們不必每次繪製的時候都去進行內存拷貝,若是能夠在顯存中緩存這些數據,就能夠在很大程度上下降內存拷貝帶來的開銷。編程

OpenGL ES 3.0 編程中, VBO 和 EBO 的出現就是爲了解決這個問題。數組

VBO 和 EBO 的做用是在顯存中提早開闢好一塊內存,用於緩存頂點數據或者圖元索引數據,從而避免每次繪製時的 CPU 與 GPU 之間的內存拷貝,能夠改進渲染性能,下降內存帶寬和功耗。
緩存

OpenGL ES 3.0 支持兩類緩衝區對象:頂點數組緩衝區對象、圖元索引緩衝區對象。微信

GL_ARRAY_BUFFER 標誌指定的緩衝區對象用於保存頂點數組,GL_ELEMENT_ARRAY_BUFFER 標誌指定的緩存區對象用於保存圖元索引。數據結構

VBO(EBO)的建立和更新:app

// 建立 2 個 VBO(EBO 實際上跟 VBO 同樣,只是按照用途的另外一種稱呼)
glGenBuffers(2, m_VboIds);

// 綁定第一個 VBO,拷貝頂點數組到顯存
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// 綁定第二個 VBO(EBO),拷貝圖元索引數據到顯存
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

GL_STATIC_DRAW 標誌標識緩衝區對象數據被修改一次,使用屢次,用於繪製。異步

本例中頂點着色器和片斷着色器增長 color 屬性:編輯器

//頂點着色器
#version 300 es                            
layout(location = 0) in vec4 a_position;   // 位置變量的屬性位置值爲 0 
layout(location = 1) in vec3 a_color;      // 顏色變量的屬性位置值爲 1
out vec3 v_color;                          // 向片斷着色器輸出一個顏色                          
void main()                                
{                                          
    v_color = a_color;                     
    gl_Position = a_position;              
};
//片斷着色器
#version 300 es
precision mediump float;
in vec3 v_color;
out vec4 o_fragColor;
void main()
{

    o_fragColor = vec4(v_color, 1.0);
}

頂點數組數據和圖元索引數據:

// 4 vertices, with(x,y,z) ,(r, g, b, a) per-vertex
GLfloat vertices[] =
        {
                -0.5f,  0.5f0.0f,       // v0
                1.0f,  0.0f0.0f,        // c0
                -0.5f-0.5f0.0f,       // v1
                0.0f,  1.0f0.0f,        // c1
                0.5f-0.5f0.0f,        // v2
                0.0f,  0.0f1.0f,        // c2
                0.5f,  0.5f0.0f,        // v3
                0.5f,  1.0f1.0f,        // c3
        };
// Index buffer data
GLushort indices[6] = { 012023};
VBO更新後內存中的數據結構

因爲頂點位置和顏色數據在同一個數組裏,一塊兒更新到 VBO 裏面,因此須要知道 2 個屬性的步長和偏移量。

爲得到數據隊列中下一個屬性值(好比位置向量的下個 3 維份量)咱們必須向右移動 6 個 float ,其中 3 個是位置值,另外 3 個是顏色值,那麼步長就是 6 乘以 float 的字節數(= 24 字節)。

一樣,也須要指定頂點位置屬性和顏色屬性在 VBO 內存中的偏移量。

對於每一個頂點來講,位置頂點屬性在前,因此它的偏移量是 0 。而顏色屬性緊隨位置數據以後,因此偏移量就是 3 * sizeof(GLfloat) ,用字節來計算就是 12 字節。

使用 VBO 和 EBO 進行繪製。

glUseProgram(m_ProgramObj);

//不使用 VBO 的繪製
glEnableVertexAttribArray(0);
glVertexAttribPointer(03, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), vertices);

glEnableVertexAttribArray(1);
glVertexAttribPointer(13, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (vertices + 3));

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

//使用 VBO 的繪製
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(03, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(13, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)(3 *sizeof(GLfloat)));

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);

VAO

VAO(Vertex Array Object)是指頂點數組對象,主要用於管理 VBO 或 EBO ,減小 glBindBuffer 、glEnableVertexAttribArray、 glVertexAttribPointer 這些調用操做,高效地實如今頂點數組配置之間切換

VAO 與 VBO 之間的關係


基於上小節的例子建立 VAO :


// 建立並綁定 VAO
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);

// 在綁定 VAO 以後,操做 VBO ,當前 VAO 會記錄 VBO 的操做
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(03, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(13, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)(3 *sizeof(GLfloat)));

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);


glBindVertexArray(GL_NONE);


使用 VAO 進行繪製:

// 是否是精簡了不少?
glUseProgram(m_ProgramObj);

glBindVertexArray(m_VaoId);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0)


UBO


UBO,Uniform Buffer Object  顧名思義,就是一個裝載 uniform 變量數據的緩衝區對象,本質上跟 OpenGL ES 的其餘緩衝區對象沒有區別,建立方式也大體一致,都是顯存上一塊用於儲存特定數據的區域。


當數據加載到 UBO ,那麼這些數據將存儲在 UBO 上,而再也不交給着色器程序,因此它們不會佔用着色器程序自身的 uniform 存儲空間,UBO 是一種新的從內存到顯存的數據傳遞方式,另外 UBO 通常須要與 uniform 塊配合使用。


本例將 MVP 變換矩陣設置爲一個 uniform 塊,即咱們後面建立的 UBO 中將保存 3 個矩陣。


#version 310 es
layout(location = 0in vec4 a_position;
layout(location = 1in vec2 a_texCoord;

layout (std140) uniform MVPMatrix
{
    mat4 projection;
    mat4 view;
    mat4 model;
};

out vec2 v_texCoord;
void main()
{
    gl_Position = projection * view * model * a_position;
    v_texCoord = a_texCoord;
}


設置 uniform 塊的綁定點爲 0 ,生成一個 UBO 。


GLuint uniformBlockIndex = glGetUniformBlockIndex(m_ProgramObj, "MVPMatrix");
glUniformBlockBinding(m_ProgramObj, uniformBlockIndex, 0);


glGenBuffers(1, &m_UboId);
glBindBuffer(GL_UNIFORM_BUFFER, m_UboId);
glBufferData(GL_UNIFORM_BUFFER, 3 * sizeof(glm::mat4), nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);

//定義綁定點爲 0 buffer 的範圍
glBindBufferRange(GL_UNIFORM_BUFFER, 0, m_UboId, 03 * sizeof(glm::mat4));


繪製的時候更新 Uniform Buffer 的數據,更新三個矩陣的數據,注意偏移量。


glBindBuffer(GL_UNIFORM_BUFFER, m_UboId);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), &m_ProjectionMatrix[0][0]);
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), &m_ViewMatrix[0][0]);
glBufferSubData(GL_UNIFORM_BUFFER, 2 *sizeof(glm::mat4), sizeof(glm::mat4), &m_ModelMatrix[0][0]);
glBindBuffer(GL_UNIFORM_BUFFER, 0);


FBO

FBO(Frame Buffer Object)即幀緩衝區對象,其實是一個可添加緩衝區的容器,能夠爲其添加紋理或渲染緩衝區對象(RBO)。

FBO 自己不能用於渲染,只有添加了紋理或者渲染緩衝區以後才能做爲渲染目標,它僅且提供了 3 個附着(Attachment),分別是顏色附着、深度附着和模板附着。

RBO(Render Buffer Object)即渲染緩衝區對象,是一個由應用程序分配的 2D 圖像緩衝區。渲染緩衝區能夠用於分配和存儲顏色、深度或者模板值,能夠用做 FBO 中的顏色、深度或者模板附着。

使用 FBO 做爲渲染目標時,首先須要爲 FBO 的附着添加鏈接對象,如顏色附着須要鏈接紋理或者渲染緩衝區對象的顏色緩衝區。

幀緩衝區對象,渲染緩衝區對象和紋理


TBO


紋理緩衝區對象,即 TBO(Texture Buffer Object),是 OpenGL ES 3.2 引入的概念,所以在使用時首先要檢查 OpenGL ES 的版本,Android 方面須要保證 API >= 24 。


TBO 須要配合緩衝區紋理(Buffer Texture)一塊兒使用,Buffer Texture 是一種一維紋理,其存儲數據來自紋理緩衝區對象(TBO),用於容許着色器訪問由緩衝區對象管理的大型內存表


在 GLSL 中,只能使用 texelFetch 函數訪問緩衝區紋理,緩衝區紋理的採樣器類型爲 samplerBuffer 。


生成一個 TBO 的方式跟 VBO 相似,只須要綁定到 GL_TEXTURE_BUFFER ,而生成緩衝區紋理的方式與普通的 2D 紋理同樣。


//生成一個 Buffer Texture 
glGenTextures(1, &m_TboTexId);

float *bigData = new float[BIG_DATA_SIZE];
for (int i = 0; i < BIG_DATA_SIZE; ++i) {
    bigData[i] = i * 1.0f;
}

//生成一個 TBO ,並將一個大的數組上傳至 TBO 
glGenBuffers(1, &m_TboId);
glBindBuffer(GL_TEXTURE_BUFFER, m_TboId);
glBufferData(GL_TEXTURE_BUFFER, sizeof(float) * BIG_DATA_SIZE, bigData, GL_STATIC_DRAW);

delete [] bigData;


使用紋理緩衝區的片斷着色器,須要引入擴展 texture buffer ,注意版本聲明爲 #version 320 es 。


#version 320 es
#extension GL_EXT_texture_buffer : require
in mediump vec2 v_texCoord;
layout(location = 0out mediump  vec4 outColor;
uniform mediump samplerBuffer u_buffer_tex;
uniform mediump sampler2D u_2d_texture;
uniform mediump int u_BufferSize;
void main()
{
    mediump int index = int((v_texCoord.x +v_texCoord.y) /2.0 * float(u_BufferSize - 1));
    mediump float value = texelFetch(u_buffer_tex, index).x;
    mediump vec4 lightColor = vec4(vec3(vec2(value / float(u_BufferSize - 1)), 0.0), 1.0);
    outColor = texture(u_2d_texture, v_texCoord) * lightColor;
}


繪製時如何使用緩衝區紋理和 TBO ?


glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, m_TboTexId);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_TboId);
GLUtils::setInt(m_ProgramObj, "u_buffer_tex"0);


跟普通紋理的使用方式大體同樣,只不過須要使用 glTexBuffer 綁定 TBO 到緩衝區紋理。


PBO

PBO (Pixel Buffer Object)是 OpenGL ES 3.0 的概念,稱爲像素緩衝區對象,主要被用於異步像素傳輸操做。PBO 僅用於執行像素傳輸,不鏈接到紋理,且與 FBO (幀緩衝區對象)無關。


PBO 相似於 VBO(頂點緩衝區對象),PBO 開闢的也是 GPU 緩存,而存儲的是圖像數據。


PBO 能夠在 GPU 的緩存間快速傳遞像素數據,不影響 CPU 時鐘週期,除此以外,PBO 還支持異步傳輸。


PBO 相似於「以空間換時間」策略,在使用一個 PBO 的狀況下,性能沒法有效地提高,一般須要多個 PBO 交替配合使用。



2 個 PBO read pixels

如上圖所示,利用 2 個 PBO 從幀緩衝區讀回圖像數據,使用 glReadPixels 通知 GPU 將圖像數據從幀緩衝區讀回到 PBO1 中,同時 CPU 能夠直接處理 PBO2 中的圖像數據。


關於 PBO 的詳細使用能夠參考文章:OpenGL ES 3.0 開發連載(22):PBO , 這裏再也不贅述。



-- END --


進技術交流羣,掃碼添加個人微信:Byte-Flow



獲取視頻教程和源碼




推薦:

字節流動 OpenGL ES 技術交流羣來啦

Android OpenGL 渲染圖像讀取哪家強?

FFmpeg + OpenGL ES 實現 3D 全景播放器

Android OpenGL ES 從入門到精通系統性學習教程

OpenGL ES 實現動態(水波紋)漣漪效果


以爲不錯,點個在看唄~


本文分享自微信公衆號 - 字節流動(google_developer)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索