2013-07-04 86 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代码路径。

相关问题