2013-07-04 19 views
0

我已經問過類似但有點不清楚的問題here但這次我會非常具體並且重點突出。在運行時在同一模型上使用不同的着色器

假設我有一個演員,抓住了權力。他開始使用bloom着色器進行發光,並在10秒後恢復正常,再次連接默認着色器。這個問題基本歸結爲:

如何在運行時在同一模型上使用不同的着色器?

考慮跟進很簡單的例子:

默認着色器:

attribute vec4 Position; 
uniform mat4 ModelViewProjMatrix; 

void main(void) 
{ 
    gl_Position = ModelViewProjMatrix * Position; 
} 

渲染內RendererGLES20代碼將是:

void RendererGLES20::render(Model * model) 
{ 
    glUniformMatrix4fv(mvpUniform, 1, 0, &mvpMatrix); 
    GLuint positionSlot = glGetAttribLocation(_program, "Position"); 
    glEnableVertexAttribArray(positionSlot); 

    // interleaved data, But for now we are ONLY using the positions, ignoring texture, normals and colours. 
    const GLvoid* pCoords = &(model->vertexArray[0].Position[0]); 
    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, pCoords); 

    glDrawArrays(GL_TRIANGLES, 0, model->vertexCount); 

    glDisableVertexAttribArray(positionSlot); 
} 

夠簡單了!現在想象一下,演員有一些電和下瘋狂的着色器應用:

瘋狂的着色器:

attribute vec4 Position; 
attribute vec4 SourceColor; 
attribute vec2 Texture; 
attribute vec4 Normal; 
attribute vec2 tempAttrib0; 
attribute vec2 tempAttrib1; 

// A bunch of varying but we don't need to worry about these for now           
varying vec4 .........; 
varying .........; 

uniform mat4 MVPMatrix; 
uniform vec2 BloomAmount; 
uniform vec2 BloomQuality; 
uniform vec2 BloomSize; 
uniform vec2 RippleSize; 
uniform vec2 RippleAmmount; 
uniform vec2 RippleLocation; 
uniform vec2 deltaTime; 
uniform vec2 RippleMaxIterations; 

void main(void) 
{ 
    // Some crazy voodoo source code here... 
    // ......... 
    gl_Position = ..............; 
} 

正如你可以清楚地看到,爲了這個着色器連接到模型,我需要修改實際渲染器的源代碼到以下幾點:

void RendererGLES20::render(Model * model) 
{ 
    glUniformMatrix4fv(mvpUniform, 1, 0, ....); 
    glUniformMatrix4fv(bloomAmountUniform, 1, 0, ....); 
    glUniformMatrix4fv(bloomQualityUniform, 1, 0, ....); 
    glUniformMatrix4fv(bloomSizeUniform, 1, 0, ....); 
    glUniformMatrix4fv(rippleSizeUniform, 1, 0, ....); 
    glUniformMatrix4fv(rippleAmountUniform, 1, 0, ....); 
    glUniformMatrix4fv(rippleLocationUniform, 1, 0, ....); 
    glUniformMatrix4fv(rippleMaxIterationsUniform, 1, 0, ....); 
    glUniformMatrix4fv(deltaTimeUniform, 1, 0, ....); 

    GLuint positionSlot = glGetAttribLocation(_program, "Position"); 
    GLuint sourceColorSlot = glGetAttribLocation(_program, "SourceColor"); 
    GLuint textureSlot = glGetAttribLocation(_program, "Texture"); 
    GLuint normalSlot = glGetAttribLocation(_program, "Normal"); 
    GLuint tempAttrib0Slot = glGetAttribLocation(_program, "TempAttrib0"); 
    GLuint tempAttrib1Slot = glGetAttribLocation(_program, "TempAttrib1"); 

    glEnableVertexAttribArray(positionSlot); 
    glEnableVertexAttribArray(sourceColorSlot); 
    glEnableVertexAttribArray(textureSlot); 
    glEnableVertexAttribArray(normalSlot); 
    glEnableVertexAttribArray(tempAttrib0Slot); 
    glEnableVertexAttribArray(tempAttrib1Slot); 

    // interleaved data 
    const GLvoid* pCoords = &(model->vertexArray[0].Position[0]); 
    const GLvoid* sCoords = &(model->vertexArray[0].SourceColor[0]); 
    const GLvoid* tCoords = &(model->vertexArray[0].Texture[0]); 
    const GLvoid* nCoords = &(model->vertexArray[0].Normal[0]); 
    const GLvoid* t0Coords = &(model->vertexArray[0].TempAttrib0[0]); 
    const GLvoid* t1Coords = &(model->vertexArray[0].TempAttrib1[0]); 

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords); 
    glVertexAttribPointer(sourceColorSlot, 4, GL_FLOAT, GL_FALSE, stride, sCoords); 
    glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, stride, tCoords); 
    glVertexAttribPointer(normalSlot, 4, GL_FLOAT, GL_FALSE, stride, nCoords); 
    glVertexAttribPointer(tempAttrib0Slot, 3, GL_FLOAT, GL_FALSE, stride, t0Coords); 
    glVertexAttribPointer(tempAttrib1Slot, 2, GL_FLOAT, GL_FALSE, stride, t1Coords); 

    glDrawArrays(GL_TRIANGLES, 0, model->vertexCount); 

    glDisableVertexAttribArray(positionSlot); 
    glDisableVertexAttribArray(sourceColorSlot); 
    glDisableVertexAttribArray(textureSlot); 
    glDisableVertexAttribArray(normalSlot); 
    glDisableVertexAttribArray(tempAttrib0Slot); 
    glDisableVertexAttribArray(tempAttrib1Slot); 
} 

你看你需要的代碼如何千差萬別,以連接不同的着色器來寫。現在,如果我想重新添加默認着色器,該怎麼辦? (這是着色器的附着和分離必須在運行時發生,例如:演員收集電源)。

任何想法如何有效和輕鬆地實現這個允許模型在運行時更改着色器?我只是期待着一個很好的實現/想法。你們會如何處理上述問題?

回答

1

在渲染對象之前,您可以用預期的着色器程序調用glUseProgram(program)specifications here)。您可能想要使用已有的_program變量。

然後,您可以根據所使用的着色器更改設置的變量(統一/陣列)。

我不確定「附着和分離着色器」,但爲了回答你的效率問題,大多數人傾向於根據它們的着色器對他們的「模型」進行分組,以儘量減少對glUseProgram()的調用。這也意味着你只需要在每幀中設置一次像bloomQualityUniform這樣的校服,而不是每個使用該着色器的模型一次。

編輯:

下面是一個例如(根據你的例子),這將讓你使用一個枚舉

enum MyShaderEnum { DEFAULT, CRAZY} 

void RendererGLES20::render(Model * model, MyShaderEnum shaderType) 
{ 
    if (shaderType == DEFAULT) 
    { 
     glUseProgram(defaultShaderProgram); 
     glUniformMatrix4fv(mvpUniform, 1, 0, &mvpMatrix); 
     GLuint positionSlot = glGetAttribLocation(_program, "Position"); 
     glEnableVertexAttribArray(positionSlot); 

     // interleaved data, But for now we are ONLY using the positions, ignoring texture, normals and colours. 
     const GLvoid* pCoords = &(model->vertexArray[0].Position[0]); 
     glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, pCoords); 

     glDrawArrays(GL_TRIANGLES, 0, model->vertexCount); 

     glDisableVertexAttribArray(positionSlot); 
    } 
    else if(shaderType == CRAZY) 
    { 
     glUseProgram(crazyShaderProgram); 
     glUniformMatrix4fv(mvpUniform, 1, 0, ....); 
     glUniformMatrix4fv(bloomAmountUniform, 1, 0, ....); 
     glUniformMatrix4fv(bloomQualityUniform, 1, 0, ....); 
     glUniformMatrix4fv(bloomSizeUniform, 1, 0, ....); 
     glUniformMatrix4fv(rippleSizeUniform, 1, 0, ....); 
     glUniformMatrix4fv(rippleAmountUniform, 1, 0, ....); 
     glUniformMatrix4fv(rippleLocationUniform, 1, 0, ....); 
     glUniformMatrix4fv(rippleMaxIterationsUniform, 1, 0, ....); 
     glUniformMatrix4fv(deltaTimeUniform, 1, 0, ....); 

     GLuint positionSlot = glGetAttribLocation(_program, "Position"); 
     GLuint sourceColorSlot = glGetAttribLocation(_program, "SourceColor"); 
     GLuint textureSlot = glGetAttribLocation(_program, "Texture"); 
     GLuint normalSlot = glGetAttribLocation(_program, "Normal"); 
     GLuint tempAttrib0Slot = glGetAttribLocation(_program, "TempAttrib0"); 
     GLuint tempAttrib1Slot = glGetAttribLocation(_program, "TempAttrib1"); 

     glEnableVertexAttribArray(positionSlot); 
     glEnableVertexAttribArray(sourceColorSlot); 
     glEnableVertexAttribArray(textureSlot); 
     glEnableVertexAttribArray(normalSlot); 
     glEnableVertexAttribArray(tempAttrib0Slot); 
     glEnableVertexAttribArray(tempAttrib1Slot); 

     // interleaved data 
     const GLvoid* pCoords = &(model->vertexArray[0].Position[0]); 
     const GLvoid* sCoords = &(model->vertexArray[0].SourceColor[0]); 
     const GLvoid* tCoords = &(model->vertexArray[0].Texture[0]); 
     const GLvoid* nCoords = &(model->vertexArray[0].Normal[0]); 
     const GLvoid* t0Coords = &(model->vertexArray[0].TempAttrib0[0]); 
     const GLvoid* t1Coords = &(model->vertexArray[0].TempAttrib1[0]); 

     glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords); 
     glVertexAttribPointer(sourceColorSlot, 4, GL_FLOAT, GL_FALSE, stride, sCoords); 
     glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, stride, tCoords); 
     glVertexAttribPointer(normalSlot, 4, GL_FLOAT, GL_FALSE, stride, nCoords); 
     glVertexAttribPointer(tempAttrib0Slot, 3, GL_FLOAT, GL_FALSE, stride, t0Coords); 
     glVertexAttribPointer(tempAttrib1Slot, 2, GL_FLOAT, GL_FALSE, stride, t1Coords); 

     glDrawArrays(GL_TRIANGLES, 0, model->vertexCount); 

     glDisableVertexAttribArray(positionSlot); 
     glDisableVertexAttribArray(sourceColorSlot); 
     glDisableVertexAttribArray(textureSlot); 
     glDisableVertexAttribArray(normalSlot); 
     glDisableVertexAttribArray(tempAttrib0Slot); 
     glDisableVertexAttribArray(tempAttrib1Slot); 
    } 
} 
+0

答案有點模糊(可能不正確)。你能更具體一點嗎? :) – fakhir

+0

我不是GLES專業版,但我認爲你的應用程序中的某個地方設置了使用哪個着色器。 'glUseProgram()'是什麼。如果你調用'glUseProgram()'並指定一個不同的程序,那麼你會得到不同的'效果'。如果有什麼阻止你這樣做,請將其添加到問題 – MadcoreTom

+0

那麼,上述代碼只是算法,而不是'確切'的代碼。假定從某處使用glUseProgram()設置着色器。它可以位於渲染函數中,也可以位於外部世界中,也可以位於Geometry類中......無論調用何處,都可以保證着色器在調用渲染代碼之前被正確綁定。 – fakhir

0

選擇在運行時着色器在我們進入細節之前,首先我們可以避開一些心理障礙:OpenGL不是一個場景圖:你不會給它一個場景,它會渲染整個模型或類似的東西。讓我們誠實地說,OpenGL是通過優化的鉛筆在操作系統提供的紙張上繪製的。

你應該真的認爲OpenGL是某種程序控制的繪圖工具,因爲它就是這樣。在閱讀之前,我建議你打開你最喜歡的圖像處理程序(Photoshop,GIMP,Krita等),並嘗試畫出一幅不錯的圖像。也許你會複製一些圖層,在其上應用一些濾鏡,將它覆蓋在原始圖層上以獲得所需的效果等等。

這就是您應該考慮編寫OpenGL的方式,尤其是在進行着色器效果時。

現在,讓我們打破這:

假設我有抓住一個功率可達演員。

爲此,您需要演員和一些動畫的模型。這是由一位藝術家用Blender這樣的工具完成的。

他開始使用華着色

煥發的預熱通常只是一個額外的傳球,這被覆蓋在原始模型。把你的photoshop模型放回你的腦海。首先,您使用照明着色器繪製模型。讓我們假設你有一個型號類和PhongTechniquq類,從技術類,它提供了一個接口派生要養活一個模型可以得出:

class Model; 
class ModelState; 

class Technique { 
    drawModel(Model const *Model, ModelState const *state, /*...*/); 
}; 

/* technique that renders models using a phong illumination model */ 
class PhongTechnique { 
    drawModel(Model const *Model, ModelState const *state, /*...*/); 
} 

再來說布魯姆影響我們有另一種技術類

/* technique that renders models using a bloom */ 
class BloomTechnique { 
    drawModel(Model const *Model, ModelState const *state, /*...*/); 
} 

和10秒回正常再附着的默認着色器之後。

所以在你的遊戲animaiton循環中,你會遇到你的模型。附有一些動畫數據。

class AnimationElement { 
    float timeStart(); 
    float timeStop(); 
    float X(float T); 
} 

class Model { 
    vector<AnimationElement> animation_elements; 
    ModelState animate(float T); 
} 

並且在模型狀態中,我們有一些使用效果的標誌。因此,在您的整體繪圖功能

drawscene(float T) 
{ 
    PhongTechnique phong; 
    BloomTechnique bloom; 

    foreach(m in models) { 
     ModelState mstate = m.animate(T); 

     if(mstate.phong_pass) 
      phong.drawModel(m, mstate, ...); 

     if(mstate.bloom_pass) 
      bloom.drawModel(m, mstate, ...); 

    } 
} 

現在不同的技術類實現內切換到合適的着色器,頂點設置屬性數據等和渲染模型。或者確切地說:您將填寫繪圖批次列表,您稍後將重新排序以優化繪圖過程。

如果你想看看一個真實的遊戲引擎:Id Software確實發佈了Doom3和Doom3-BFG引擎的全部源代碼,後者有一個現代的OpenGL-3代碼路徑。

相關問題