引擎渲染管道:使着色器通用


10

我正在尝试使用OpenGL ES 2.0(目前为iOS)制作2D游戏引擎。我已经在Objective C中编写了Application层,并在C ++中编写了一个独立的RendererGLES20。在渲染器外部不会进行GL特定的调用。运行良好。

但是在使用着色器时,我有一些设计问题。每个着色器都有自己的唯一属性和统一体,需要在主绘制调用之前进行设置(在本例中为glDrawArrays)。例如,为了绘制一些几何图形,我要做:

void RendererGLES20::render(Model * model)
{
    // Set a bunch of uniforms
    glUniformMatrix4fv(.......);
    // Enable specific attributes, can be many
    glEnableVertexAttribArray(......);
    // Set a bunch of vertex attribute pointers:
    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, m->pCoords);

    // Now actually Draw the geometry
    glDrawArrays(GL_TRIANGLES, 0, m->vertexCount);

    // After drawing, disable any vertex attributes:
    glDisableVertexAttribArray(.......);
}

如您所见,此代码非常严格。如果要使用另一个着色器(例如波纹效果),我将需要传递额外的制服,顶点属性等。换句话说,我将不得不更改RendererGLES20渲染源代码以仅合并新的着色器。

有什么方法可以使着色器对象完全通用吗?就像我只想更改着色器对象而不担心游戏源重新编译该怎么办?有什么方法可以使渲染器与制服和属性无关?即使我们需要将数据传递给制服,这样做的最佳位置是什么?模特班?模型类是否了解着色器特定的制服和属性?

以下显示演员类:

class Actor : public ISceneNode
{
    ModelController * model;
    AIController * AI;
};

模型控制器类:类ModelController {类IShader * shader; int textureId; vec4色调; 浮动字母 struct Vertex * vertexArray; };

着色器类仅包含着色器对象,编译和链接子例程等。

在游戏逻辑类中,我实际上是在渲染对象:

void GameLogic::update(float dt)
{
    IRenderer * renderer = g_application->GetRenderer();

    Actor * a = GetActor(id);
    renderer->render(a->model);
}

请注意,即使Actor扩展了ISceneNode,我仍未开始实现SceneGraph。解决此问题后,我会立即进行处理。

有什么想法可以改善这一点吗?相关的设计模式等?

感谢您阅读问题。



不知道那是否是完全相同的副本,但是我想这会影响您要尝试做的事情。
肖恩·米德迪奇

Answers:


12

可以使您的着色器系统更多地由数据驱动,以便您不必为统一和顶点格式放置太多着色器特定的代码,而可以根据附加到着色器的元数据以编程方式设置它们。

首先是免责声明:数据驱动的系统可以使添加新着色器变得更加容易,但是另一方面,由于系统复杂性的增加,它也带来了成本,这使得维护和调试变得更加困难。因此,最好仔细考虑多少数据驱动对您有利(对于一个小型项目,答案很可能是“无”),并且不要尝试构建过于笼统的系统。

好吧,让我们先谈谈顶点格式(属性)。您可以通过构造一个包含要传递到的数据的结构来创建数据描述glVertexAttribPointer -单个属性的索引,类型,大小等,并使用这些结构的数组来表示整个顶点格式。有了这些信息,您就可以以编程方式设置与顶点属性相关的所有GL状态。

填充此描述的数据从何而来?从概念上讲,我认为最干净的方法是使其属于着色器。当构建网格的顶点数据时,您将查找网格上使用的着色器,找到该着色器所需的顶点格式,并相应地构建顶点缓冲区。您只需为每个着色器指定某种方式即可指定所需的顶点格式。

有多种方法可以做到这一点。例如,您可以为着色器中的属性设置一组标准名称(“ attrPosition”,“ attrNormal”等),以及一些硬编码的规则,例如“ position is 3 floats”。然后你用glGetAttribLocation或类似方法查询着色器使用的属性,并应用规则以构建顶点格式。另一种方法是使用XML代码段定义格式,将其嵌入到着色器源中的注释中,然后由您的工具或类似方式提取。

对于制服,如果可以使用OpenGL 3.1或更高版本,则最好使用统一缓冲区对象(与D3D的常量缓冲区等效的OpenGL)。,GL ES 2.0没有这些,因此制服必须单独处理。一种方法是创建一个包含要设置的每个参数的统一位置的结构-相机矩阵,镜面反射功率,世界矩阵等。采样器位置也可以在此处。此方法取决于所有着色器之间共享一组标准参数。并非每个着色器都必须使用每个单独的参数,但是所有参数都必须在此结构中。

每个着色器都具有该结构的一个实例,并且在加载着色器时,会使用glGetUniformLocation和标准化名称向其查询所有参数的位置。然后,每当需要从代码设置统一时,就可以检查它是否存在于该着色器中,并只需查找其位置并进行设置即可。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.