OPenGL实例化绘制、普通绘制说明
OpenGL 實(shí)例化(Instancing)是一種只調(diào)用一次渲染函數(shù)就能繪制出不少物體的技術(shù),能夠?qū)崿F(xiàn)將數(shù)據(jù)一次性而不是多次發(fā)送給 GPU ,告訴 OpenGL使用一個(gè)繪制函數(shù),將這些數(shù)據(jù)繪制成多個(gè)物體。數(shù)組實(shí)例化(Instancing)避免了 CPU 屢次向 GPU 下達(dá)渲染命令(避免屢次調(diào)用 glDrawArrays 或 glDrawElements 等繪制函數(shù)),節(jié)省了繪制多個(gè)物體時(shí) CPU 與 GPU 之間的通訊時(shí)間,提高了渲染性能。?實(shí)例化是因?yàn)閂erilog等語(yǔ)言里面有實(shí)例化,相當(dāng)于提前定義好的一個(gè)模塊,在別的模塊中可以直接調(diào)用(即例化)。OpenGL的實(shí)例化與Verilog的例化很像,所以名字叫這個(gè),本質(zhì)上是對(duì)一個(gè)模塊的多次調(diào)用,和批量渲染是一個(gè)意思。
//普通渲染 glDrawArrays (GLenum mode, GLint first, GLsizei count);glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);//實(shí)例化渲染 glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount);glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);相對(duì)于普通繪制,實(shí)例化繪制多了一個(gè)參數(shù)instancecount,表示須要渲染的實(shí)例數(shù)量,調(diào)用完實(shí)例化繪制函數(shù)后,咱們便將繪制數(shù)據(jù)一次性發(fā)送給 GPU,而后告訴它該如何使用一個(gè)函數(shù)來(lái)繪制這些實(shí)例。編輯器
實(shí)例化(Instancing)的目標(biāo)并非實(shí)現(xiàn)將同一物體繪制屢次,而是能基于某一物體繪制出位置、大小、形狀或者顏色不一樣的多個(gè)物體。glDrawArraysInstanced函數(shù)內(nèi)部實(shí)現(xiàn)如下:
if ( mode or count is invalid )generate appropriate errorelse {for (int i = 0; i < primcount ; i++) {instanceID = i;glDrawArrays(mode, first, count);}instanceID = 0;}其中上訴偽代碼中的instanceID就是類(lèi)似于OpenGL? 著色器中有一個(gè)與實(shí)例化繪制相關(guān)的內(nèi)建變量?gl_InstanceID。性能
gl_InstanceID表示當(dāng)前正在繪制實(shí)例的 ID ,每一個(gè)實(shí)例對(duì)應(yīng)一個(gè)惟一的 ID ,經(jīng)過(guò)這個(gè) ID 能夠輕易實(shí)現(xiàn)基于一個(gè)物體而繪制出位置、大小、形狀或者顏色不一樣的多個(gè)物體(實(shí)例)。學(xué)習(xí)
利用內(nèi)建變量gl_InstanceID在 3D 空間繪制多個(gè)位于不一樣位置的立方體,利用?u_offsets[gl_InstanceID]對(duì)當(dāng)前實(shí)例的位置進(jìn)行偏移,對(duì)應(yīng)的著色器腳本如下:
// 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() {//經(jīng)過(guò) u_offsets[gl_InstanceID] 對(duì)當(dāng)前實(shí)例的位置進(jìn)行偏移 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 空間中產(chǎn)生 125 個(gè)偏移量(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;}} }對(duì)偏移量數(shù)組進(jìn)行賦值,而后進(jìn)行實(shí)例化繪制,繪制出 125 個(gè)不一樣位置的立方體:
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);利用內(nèi)建變量?gl_InstanceID和偏移數(shù)組進(jìn)行實(shí)例化繪制還存在一個(gè)問(wèn)題,那就是著色器中 uniform 類(lèi)型數(shù)據(jù)存在上限(為何存在上限,參考《OpenGL中的Uniform block size 的大小限制》博文),也就是 u_offsets 這個(gè)數(shù)組的大小有限制,最終致使咱們繪制的實(shí)例存在上限。
為了避免這個(gè)問(wèn)題,咱們可使用實(shí)例化數(shù)組(Instanced Array),它使用頂點(diǎn)屬性來(lái)定義,這樣就容許咱們使用更多的數(shù)據(jù),并且僅當(dāng)頂點(diǎn)著色器渲染一個(gè)新實(shí)例時(shí)它才會(huì)被更新。
這個(gè)時(shí)候咱們須要用到函數(shù)?glVertexAttribDivisor?。其在官方手冊(cè)中用法如下:
glVertexAttribDivisor?modifies the rate at which generic vertex attributes advance when rendering multiple instances of primitives in a single draw call. If?divisor?is zero, the attribute at slot?index?advances once per vertex. If?divisor?is non-zero, the attribute advances once per?divisor?instances of the set(s) of vertices being rendered. An attribute is referred to as instanced if its?GL_VERTEX_ATTRIB_ARRAY_DIVISOR?value is non-zero.
大概的意思是:當(dāng)單次調(diào)用繪制函數(shù)渲染多個(gè)圖元實(shí)例時(shí),glVertexAttribDivisor修改了通用頂點(diǎn)屬性向前的比例即每次繪制的步長(zhǎng),即是逐頂點(diǎn)還是每隔divisor 個(gè)實(shí)例渲染一次。如果參數(shù)divisor 是0,則glVertexAttribDivisor函數(shù)第一個(gè)參數(shù)index表示的頂點(diǎn)屬性每個(gè)頂點(diǎn)就執(zhí)行渲染一次,如果divisor 非0,則參數(shù)index表示的頂點(diǎn)屬性每隔divisor 實(shí)例就渲染一次。這就話(huà)的意思說(shuō)白了就是當(dāng)divisor 為0時(shí),則每個(gè)頂點(diǎn)中參數(shù)index表示的頂點(diǎn)屬性就渲染一次(逐頂點(diǎn)渲染,以點(diǎn)為單位渲染);而如果divisor 非0,則每divisor 實(shí)例,就渲染一次。例如:一次渲染100個(gè)三角形(這里每個(gè)三角形就是一個(gè)實(shí)例),如果divisor為0,則每個(gè)三角形都繪制(逐頂點(diǎn)渲染,以點(diǎn)為單位渲染,當(dāng)然,這還跟硬件的UFO有關(guān),參考《OpenGL中的Uniform block size 的大小限制》);如果divisor為1,則第0, 1, 2, 3,4,5.....個(gè)三角形會(huì)被繪制即每個(gè)三角形都會(huì)被繪制(逐實(shí)例渲染,以實(shí)例為單位渲染),也就是說(shuō)每1個(gè)三角形才繪制一次(相比前面的divisor為0結(jié)果相同,但效率要高很多);如果divisor為3,則第0, 3, 6, 9,12,15......個(gè)三角形會(huì)被繪制,而第1,2,4,5,7,8,9,10,11,13,14......不會(huì)繪制,直接丟棄,也就是說(shuō)每隔3個(gè)三角形才繪制一次,以此類(lèi)推。
?
glVertexAttribDivisor(GLuint index, GLuint divisor):每隔divisor個(gè)實(shí)例,就會(huì)往頂點(diǎn)著色器中傳入buffer中的一個(gè)新的屬性值,其中index為屬性在著色器中的位置索引值即下面代碼中的location = 0、location = 1、location = 2等。默認(rèn)的情況下本函數(shù)的第二個(gè)參數(shù)為0,即每次將指定index索引表示的所有屬性數(shù)據(jù)更新到著色器中,比如:一個(gè)模型由30萬(wàn)個(gè)三角形組成,則這30萬(wàn)個(gè)三角形共90萬(wàn)(假設(shè)每個(gè)三角形不共頂點(diǎn))個(gè)頂點(diǎn),一次性更新到著色器,這對(duì)性能及Uniform block size無(wú)疑是一場(chǎng)災(zāi)難。當(dāng)?shù)诙€(gè)參數(shù)為非0值時(shí),如:1,則表示每隔1個(gè)三角形即3個(gè)頂點(diǎn)下的index索引表示的屬性數(shù)據(jù)載入一次。即先載入更新第0個(gè)三角形數(shù)據(jù)到著色器,處理完后再更新第1個(gè)三角形數(shù)據(jù)到著色器,以此類(lèi)推,直到全部三角形都處理完。如:2,則表示每隔2個(gè)三角形index索引表示的屬性數(shù)據(jù)載入一次。即先載入更新第0個(gè)三角形數(shù)據(jù)到著色器,處理完后再更新第2個(gè)三角形數(shù)據(jù)到著色器,處理完后再更新第4個(gè)三角形數(shù)據(jù)到著色器,丟棄、不處理第1、3.....個(gè)三角形的數(shù)據(jù),以此類(lèi)推,直到全部三角形都處理完。
void glVertexAttribDivisor (GLuint index, GLuint divisor); // index 表示頂點(diǎn)屬性的索引 // divisor 表示每 divisor 個(gè)實(shí)例更新下頂點(diǎn)屬性到下個(gè)元素,默認(rèn)為 0利用頂點(diǎn)屬性來(lái)定義的實(shí)例化數(shù)組(Instanced Array) 在 3D 空間繪制多個(gè)位于不一樣位置的立方體,對(duì)應(yīng)的著色器腳本:
// 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); }設(shè)置 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);//利用頂點(diǎn)屬性來(lái)定義的實(shí)例化數(shù)組(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 的屬性為實(shí)例化數(shù)組,1 表示每繪制一個(gè)實(shí)例,更新一次數(shù)組中的元素 glVertexAttribDivisor(2, 1); // Tell OpenGL this is an instanced vertex attribute. glBindVertexArray(GL_NONE);其中g(shù)lVertexAttribDivisor(2, 1);是上述最重要的一步,用于指定 index = 2 的屬性為實(shí)例化數(shù)組,1 表示每繪制一個(gè)實(shí)例,更新一次數(shù)組中的元素。
利用頂點(diǎn)屬性來(lái)定義的實(shí)例化數(shù)組,而后繪制出 125 個(gè)不一樣位置的立方體:
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);另外《實(shí)例化》這篇文章也深刻闡述了實(shí)例化。
總結(jié)
以上是生活随笔為你收集整理的OPenGL实例化绘制、普通绘制说明的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 银行卡给别人走账后果 可能涉嫌违法
- 下一篇: 魔兽世界怀旧服玛拉顿钥匙在哪里获得 玛拉