該原創文章首發於微信公衆號:字節流動
什麼是 Transform Feedback
Transform Feedback(變換反饋)是在 OpenGLES3.0 渲染管線中,頂點處理階段結束之後,圖元裝配和光柵化之前的一個步驟。 Transform Feedback 可以重新捕獲即將裝配爲圖元(點,線段,三角形)的頂點,然後你將它們的部分或者全部屬性傳遞到緩存對象。
Transform Feedback 的主要作用是可以將頂點着色器的處理結果輸出,並且可以有多個輸出,這樣可以將大量的向量或矩陣運算交給 GPU 並行處理,這是 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 的一般使用流程:
- 設置變換反饋變量;
- 創建 Transform Feedback 緩衝區;
- 創建 Transform Feedback 對象,並綁定緩衝區;
- 啓動變換反饋,在繪製結束後停止變換反饋;
- 讀取 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
聯繫與交流
微信公衆號
個人微信