0

我已经在这里问过类似但有点不清楚的问题,但这次我将非常具体和直截了当。

假设我有一个演员,它抓住了一个力量。他开始使用绽放着色器发光,并在 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);
}

您会看到为了附加不同的着色器需要编写多么不同的代码。现在如果我想重新附加默认着色器怎么办?(这是着色器的附加和分离必须在运行时发生,例如:演员收集电源)。

有什么想法我可以如何有效且轻松地实现它以允许模型在运行时更改着色器?我只是期待一个好的实施/想法。各位大佬会怎么处理上面的问题呢?

4

2 回答 2

1

在渲染对象之前,您可以使用预期的着色器程序调用glUseProgram(program)此处的规范)。您可能想使用_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);
    }
}
于 2013-07-04T07:00:40.393 回答
0

在我们进入细节之前,首先让我们排除一些心理障碍:OpenGL 不是一个场景图:你不给它一个场景,然后它会渲染整个模型或类似的东西。老实说,OpenGL 是在操作系统提供的纸上绘画的美化铅笔。

您真的应该将 OpenGL 视为某种程序控制的绘图工具,因为它就是这样。在你继续阅读之前,我建议你打开你最喜欢的图像处理程序(Photoshop、GIMP、Krita 等),并尝试画一张漂亮的图片。也许您复制一些图层,在其上应用一些过滤器,将其覆盖在原始图层上以获得所需的效果等等。

这就是你应该考虑编程 OpenGL 的方式,尤其是在处理着色器效果时。

现在让我们分解一下:

假设我有一个演员,它抓住了一个力量。

为此,您需要一个演员模型和一些动画。这将由艺术家使用Blender之类的工具来完成。

他开始使用绽放着色器发光

发光通常只是一个附加通道,它覆盖在原始模型上。让那个 Photoshop 模型回到你的脑海中。首先,您使用照明着色器绘制模型。假设您有一个Model类和一个PhongTechniquq类,它们派生自Technique类,该类提供了一个接口来馈送要绘制的模型:

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, /*...*/);
}

然后对于 Bloom 效果,我们有另一个技术类

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

并在 10 秒后恢复正常,再次附加默认着色器。

所以在你的游戏动画循环中你会遇到你的模型。其中附有一些动画数据。

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 代码路径。

于 2013-07-04T09:19:43.260 回答