天天看點

OpenGL ES 3.0 的 Transform Feedback 怎麼用?Transform Feedback 的使用

什麼是 Transform Feedback

Transform Feedback(變換回報)是在 OpenGLES3.0 渲染管線中,頂點處理階段結束之後,圖元裝配和光栅化之前的一個步驟。

Transform Feedback 可以重新捕獲即将裝配為圖元(點,線段,三角形)的頂點,然後你将它們的部分或者全部屬性傳遞到緩存對象。

Transform Feedback 的主要作用是可以将頂點着色器的處理結果輸出,并且可以有多個輸出,這樣可以将大量的向量或矩陣運算交給 GPU 并行處理,這是 OpenGLES 3.0 的新特性。

OpenGL ES 3.0 的 Transform Feedback 怎麼用?Transform Feedback 的使用

OpenGLES 3.0 圖形管線

每個頂點在傳遞到圖元裝配階段時,将所有需要捕獲的屬性資料記錄到一個或者多個緩存對象中,程式可以通過這些緩存讀出這些資料,可以将他們用于後續的渲染操作。

Transform Feedback 對象

Transform Feedback 所有狀态通過一個 Transform Feedback 對象管理,主要包括以下狀态:

  • 用于記錄頂點資料的緩存對象;
  • 用于辨別緩存對象的計數器;
  • 用于辨別 Transform Feedback 目前是否啟用的狀态量。

Transform Feedback 對象的建立綁定過程和一般的 OpenGLES 對象類似,如 VAO 。

生成和綁定 Transform Feedback 對象:

glGenTransformFeedbacks(1, &m_TransFeedbackObjId);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TransFeedbackObjId);
           

複制

Transform Feedback 緩存

Transform Feedback 主要用來管理将頂點捕捉到緩存對象的相關狀态。這個狀态中包含目前連接配接到的 Transform Feedback 緩存綁定點的緩存對象。

可以同時給 Transform Feedback 綁定多個緩存,也可以綁定緩存對象的多個子塊,甚至可以将同一個緩存對象不用子塊綁定到不同的 Transform Feedback 緩存綁定點上。

建立 Transform Feedback 緩存類似于建立 VBO 。

glGenBuffers(1, &m_TransFeedbackBufId);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_TransFeedbackBufId);
// 設定緩存的大小,輸出是一個 3 維向量和一個 2 維向量,一共 6 個頂點,大小為 (3 + 2) * 6 * sizeof(GLfloat)
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, (3 + 2) * 6 * sizeof(GLfloat), NULL, GL_STATIC_READ);
           

複制

接口 glBindBufferBase 将緩存綁定到目前 Transform Feedback 對象。

void glBindBufferBase(GLenum target, GLuint index, Gluint buffer);
           

複制

其中:

  • target 參數須設定為 GL_TRANSFORM_FEEDBACK_BUFFER;
  • index 必須是目前綁定的 transform feedback 對象的緩存綁定點索引;
  • buffer 表示被綁定的緩存對象的 ID 。

為 Transform Feedback 對象綁定緩沖區對象。

glGenTransformFeedbacks(1, &m_TransFeedbackObjId);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TransFeedbackObjId);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_TransFeedbackBufId); // Specify the index of the binding point within the array specified by target.
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
           

複制

Transform Feedback 變量

glTransformFeedbackVaryings 用于指定變換回報的變量,也就是頂點着色器需要輸出的變量。

聲明了 2 個變換回報變量的頂點着色器:

#version 300 es                            
layout(location = 0) in vec4 a_position;   
layout(location = 1) in vec2 a_texCoord;   
out vec2 v_texCoord;                       
out vec3 outPos;                           
out vec2 outTex;                           
void main()                                
{                                          
   gl_Position = a_position;               
   v_texCoord = a_texCoord;                
   outPos = vec3(a_position)*3.0; //将位置向量做一個簡單運算後輸出         
   outTex = a_texCoord * 3.0;     //将紋理坐标向量做一個簡單運算後輸出          
}                                          
           

複制

設定變換回報變量,需要注意的是 glTransformFeedbackVaryings 需要在 glLinkProgram 之前調用。

glAttachShader(program, vertexShaderHandle);
glAttachShader(program, fragShaderHandle);

GLchar const * varyings[] = {"outPos", "outTex"};
glTransformFeedbackVaryings(m_ProgramObj, sizeof(varyings)/ sizeof(varyings[0]), varyings, GL_INTERLEAVED_ATTRIBS);

glLinkProgram(program);
           

複制

Transform Feedback 捕獲啟動和停止

Transform Feedback 可以随時啟動、暫停和停止。

glBeginTransformFeedback 用于開始 Transform Feedback ,它的參數是用來設定将要記錄的圖元類型,如:GL_POINTS、GL_LINES 和 GL_TRIANGLES 。

glPuaseTransformFeedback 暫停 Transform Feedback 對變量的記錄,但 Transform Feedback 還是處于啟動狀态。如果 Transform Feedback 沒有啟動則 OpenGLES 産生錯誤。

glResumeTransformFeedback 重新開啟一個之前通過 glPuaseTransformFeedback 暫停的變換回報過程,如果 Transform Feedback 沒有啟動,或者沒有被處于活動狀态,則産生OpenGL錯誤。

glEndTransformFeedback 用來結束 Transform Feedback 過程。

Transform Feedback 緩沖區讀取

Transform Feedback 過程結束後,通過 glMapBufferRange 讀取緩沖區資料。

//綁定要讀取的緩沖區對象
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_TransFeedbackBufId);

//讀取緩沖區資料
void* rawData = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,  (3 + 2) * 6 * sizeof(GLfloat), GL_MAP_READ_BIT);

float *p = (float*)rawData;
for(int i= 0; i< 6; i++)
{
    LOGCATE("TransformFeedbackSample::Draw() read feedback buffer outPos[%d] = [%f, %f, %f], outTex[%d] = [%f, %f]", i, p[i * 5], p[i * 5 + 1], p[i * 5 + 2], i, p[i * 5 + 3], p[i * 5 + 4]);
}

//解綁
glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
           

複制

Transform Feedback 的使用

Transform Feedback 的一般使用流程:

  1. 設定變換回報變量;
  2. 建立 Transform Feedback 緩沖區;
  3. 建立 Transform Feedback 對象,并綁定緩沖區;
  4. 啟動變換回報,在繪制結束後停止變換回報;
  5. 讀取 Transform Feedback 緩沖區資料。

總體實作代碼:

//1. 設定變換回報變量;
glAttachShader(program, vertexShaderHandle);
glAttachShader(program, fragShaderHandle);

GLchar const * varyings[] = {"outPos", "outTex"};
glTransformFeedbackVaryings(m_ProgramObj, sizeof(varyings)/ sizeof(varyings[0]), varyings, GL_INTERLEAVED_ATTRIBS);

glLinkProgram(program);

//2. 建立 Transform Feedback 緩沖區;
glGenBuffers(1, &m_TransFeedbackBufId);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_TransFeedbackBufId);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, (3 + 2) * 6 * sizeof(GLfloat), NULL, GL_STATIC_READ);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);

//3. 建立 Transform Feedback 對象,并綁定緩沖區;
glGenTransformFeedbacks(1, &m_TransFeedbackObjId);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TransFeedbackObjId);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_TransFeedbackBufId);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);

//4. 啟動變換回報,在繪制結束後停止變換回報;
glViewport(0, 0, screenW, screenH);
glUseProgram(m_ProgramObj);
glBindVertexArray(m_VaoId);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
glUniform1i(m_SamplerLoc, 0);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_TransFeedbackObjId);
glBeginTransformFeedback(GL_TRIANGLES);
glDrawArrays(GL_TRIANGLES, 0, 6);
glEndTransformFeedback();
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
glBindVertexArray(GL_NONE);

//5. 讀取 Transform Feedback 緩沖區資料。
// Read feedback buffer
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_TransFeedbackBufId);
void* rawData = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,  (3 + 2) * 6 * sizeof(GLfloat), GL_MAP_READ_BIT);
float *p = (float*)rawData;
for(int i= 0; i< 6; i++)
{
    LOGCATE("TransformFeedbackSample::Draw() read feedback buffer outPos[%d] = [%f, %f, %f], outTex[%d] = [%f, %f]", i, p[i * 5], p[i * 5 + 1], p[i * 5 + 2], i, p[i * 5 + 3], p[i * 5 + 4]);
}
glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
           

複制

代碼執行後讀取 Transform Feedback 緩沖區的資料:

E/ByteFlow: TransformFeedbackSample::Draw() read feedback buffer outPos[0] = [-3.000000, -1.500000, 0.000000], outTex[0] = [0.000000, 3.000000]
E/ByteFlow: TransformFeedbackSample::Draw() read feedback buffer outPos[1] = [3.000000, -1.500000, 0.000000], outTex[1] = [3.000000, 3.000000]
E/ByteFlow: TransformFeedbackSample::Draw() read feedback buffer outPos[2] = [-3.000000, 1.500000, 0.000000], outTex[2] = [0.000000, 0.000000]
E/ByteFlow: TransformFeedbackSample::Draw() read feedback buffer outPos[3] = [3.000000, -1.500000, 0.000000], outTex[3] = [3.000000, 3.000000]
E/ByteFlow: TransformFeedbackSample::Draw() read feedback buffer outPos[4] = [3.000000, 1.500000, 0.000000], outTex[4] = [3.000000, 0.000000]
E/ByteFlow: TransformFeedbackSample::Draw() read feedback buffer outPos[5] = [-3.000000, 1.500000, 0.000000], outTex[5] = [0.000000, 0.000000]
           

複制

Transform Feedback 實作代碼路徑:

https://github.com/githubhaohao/NDK_OpenGLES_3_0