天天看點

NDK OpenGL ES 3.0 開發(十三):執行個體化(Instancing)OpenGL ES 執行個體化(Instancing)聯系與交流

作者:位元組流動

來源:

https://blog.csdn.net/Kennethdroid/article/details/102770813

OpenGL ES 執行個體化(Instancing)

NDK OpenGL ES 3.0 開發(十三):執行個體化(Instancing)OpenGL ES 執行個體化(Instancing)聯系與交流

OpenGL ES 執行個體化(Instancing)是一種隻調用一次渲染函數就能繪制出很多物體的技術,可以實作将資料一次性發送給 GPU ,告訴 OpenGL ES 使用一個繪制函數,将這些資料繪制成多個物體。

執行個體化(Instancing)避免了 CPU 多次向 GPU 下達渲染指令(避免多次調用 glDrawArrays 或 glDrawElements 等繪制函數),節省了繪制多個物體時 CPU 與 GPU 之間的通信時間,提升了渲染性能。

使用執行個體化渲染需要使用的繪制接口:

//普通渲染
glDrawArrays (GLenum mode, GLint first, GLsizei count);

glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);

//執行個體化渲染
glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount);

glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);           

相對于普通繪制,執行個體化繪制多了一個參數

instancecount

,表示需要渲染的執行個體數量,調用完執行個體化繪制函數後,我們便将繪制資料一次性發送給 GPU,然後告訴它該如何使用一個函數來繪制這些執行個體。

執行個體化(Instancing)的目标并不是實作将同一物體繪制多次,而是能基于某一物體繪制出位置、大小、形狀或者顔色不同的多個物體。OpenGL ES 着色器中有一個與執行個體化繪制相關的内建變量

gl_InstanceID

gl_InstanceID

表示目前正在繪制執行個體的 ID ,每個執行個體對應一個唯一的 ID ,通過這個 ID 可以輕易實作基于一個物體而繪制出位置、大小、形狀或者顔色不同的多個物體(執行個體)。

利用内建變量

gl_InstanceID

在 3D 空間繪制多個位于不同位置的立方體,利用

u_offsets[gl_InstanceID]

對目前執行個體的位置進行偏移,對應的着色器腳本:

// vertex shader GLSL
#version 300 es                            
layout(location = 0) in vec4 a_position;   
layout(location = 1) in vec2 a_texCoord;   
out vec2 v_texCoord;                       
uniform mat4 u_MVPMatrix;   
uniform vec3 u_offsets[125];               
void main()                                
{
   //通過 u_offsets[gl_InstanceID] 對目前執行個體的位置進行偏移                                          
   gl_Position = u_MVPMatrix * (a_position + vec4(u_offsets[gl_InstanceID], 1.0));
   v_texCoord = a_texCoord;                
}   

// fragment shader GLSL
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;
void main()
{
    outColor = texture(s_TextureMap, v_texCoord);
}                                                

在 3D 空間中産生 125 個偏移量(offset):

glm::vec3 translations[125];
int index = 0;
GLfloat offset = 0.2f;
for(GLint y = -10; y < 10; y += 4)
{
    for(GLint x = -10; x < 10; x += 4)
    {
        for(GLint z = -10; z < 10; z += 4)
        {
            glm::vec3 translation;
            translation.x = (GLfloat)x / 10.0f + offset;
            translation.y = (GLfloat)y / 10.0f + offset;
            translation.z = (GLfloat)z / 10.0f + offset;
            translations[index++] = translation;
        }
    }
}           
glm::vec3 translations[125];
int index = 0;
GLfloat offset = 0.2f;
for(GLint y = -10; y < 10; y += 4)
{
    for(GLint x = -10; x < 10; x += 4)
    {
        for(GLint z = -10; z < 10; z += 4)
        {
            glm::vec3 translation;
            translation.x = (GLfloat)x / 10.0f + offset;
            translation.y = (GLfloat)y / 10.0f + offset;
            translation.z = (GLfloat)z / 10.0f + offset;
            translations[index++] = translation;
        }
    }
}           

對偏移量數組進行指派,然後進行執行個體化繪制,繪制出 125 個不同位置的立方體:

glUseProgram(m_ProgramObj);
glBindVertexArray(m_VaoId);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glUniform1i(m_SamplerLoc, 0);

for(GLuint i = 0; i < 125; i++)
{
    stringstream ss;
    string index;
    ss << i;
    index = ss.str();
    GLint location = glGetUniformLocation(m_ProgramObj, ("u_offsets[" + index + "]").c_str())
    glUniform2f(location, translations[i].x, translations[i].y, translations[i].z);
}
glDrawArraysInstanced(GL_TRIANGLES, 0, 36, 125);
glBindVertexArray(0);           

效果圖:

NDK OpenGL ES 3.0 開發(十三):執行個體化(Instancing)OpenGL ES 執行個體化(Instancing)聯系與交流

gl_InstanceID

和偏移數組進行執行個體化繪制還存在一個問題,那就是着色器中 uniform 類型資料存在上限,也就是 u_offsets 這個數組的大小有限制,最終導緻我們繪制的執行個體存在上限。

為了避免這個問題,我們可以使用執行個體化數組(Instanced Array),它使用頂點屬性來定義,這樣就允許我們使用更多的資料,而且僅當頂點着色器渲染一個新執行個體時它才會被更新。

這個時候我們需要用到函數

glVertexAttribDivisor

,它表示 OpenGL ES 什麼時候去更新頂點屬性的内容到下個元素。

void glVertexAttribDivisor (GLuint index, GLuint divisor);
// index 表示頂點屬性的索引
// divisor 表示每 divisor 個執行個體更新下頂點屬性到下個元素,預設為 0           

利用頂點屬性來定義的執行個體化數組(Instanced Array) 在 3D 空間繪制多個位于不同位置的立方體,對應的着色器腳本:

// vertex shader GLSL
#version 300 es                            
layout(location = 0) in vec4 a_position;   
layout(location = 1) in vec2 a_texCoord;  
layout(location = 2) in vec2 a_offset;
out vec2 v_texCoord;                       
uniform mat4 u_MVPMatrix;   
void main()                                
{
   gl_Position = u_MVPMatrix * (a_position + vec4(a_offset, 1.0));
   v_texCoord = a_texCoord;                
}   

// fragment shader GLSL
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_TextureMap;
void main()
{
    outColor = texture(s_TextureMap, v_texCoord);
}           

設定 VAO 和 VBO :

// Generate VBO Ids and load the VBOs with data
glGenBuffers(2, m_VboIds);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * 125, &translations[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

// Generate VAO Id
glGenVertexArrays(1, &m_VaoId);

glBindVertexArray(m_VaoId);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) (3* sizeof(GLfloat)));
glEnableVertexAttribArray(2);

//利用頂點屬性來定義的執行個體化數組(Instanced Array)
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//指定 index=2 的屬性為執行個體化數組,1 表示每繪制一個執行個體,更新一次數組中的元素
glVertexAttribDivisor(2, 1); // Tell OpenGL this is an instanced vertex attribute.
glBindVertexArray(GL_NONE);           

其中

glVertexAttribDivisor(2, 1)

;是上述最重要的一步,用于指定 index = 2 的屬性為執行個體化數組,1 表示每繪制一個執行個體,更新一次數組中的元素。

利用頂點屬性來定義的執行個體化數組,然後繪制出 125 個不同位置的立方體:

glUseProgram(m_ProgramObj);
glBindVertexArray(m_VaoId);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glUniform1i(m_SamplerLoc, 0);

glDrawArraysInstanced(GL_TRIANGLES, 0, 36, 125);
glBindVertexArray(0);           

聯系與交流

技術交流/擷取源碼可以添加我的微信:Byte-Flow

「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
NDK OpenGL ES 3.0 開發(十三):執行個體化(Instancing)OpenGL ES 執行個體化(Instancing)聯系與交流

繼續閱讀