NDK OpenGL ES 3.0 開發(七):Transform Feedback

該原創文章首發於微信公衆號:字節流動

什麼是 Transform Feedback

Transform Feedback(變換反饋)是在 OpenGLES3.0 渲染管線中,頂點處理階段結束之後,圖元裝配和光柵化之前的一個步驟。 Transform Feedback 可以重新捕獲即將裝配爲圖元(點,線段,三角形)的頂點,然後你將它們的部分或者全部屬性傳遞到緩存對象。

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

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

聯繫與交流

微信公衆號
我的公衆號
個人微信
我的微信

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