【问题标题】:Writing a dynamic Shader Pipe using OpenGL ES for a graphics engine使用 OpenGL ES 为图形引擎编写动态着色器管道
【发布时间】:2012-05-05 06:29:03
【问题描述】:

我不确定这是否是正确的术语,但我相信我正在尝试做的是为 OpenGL ES 2.0 上的 GLSL 着色器编写一个“管道”。这个想法是,如果你愿意,这将是一个方便的组件,它将进入我引擎的图形管道。我的目标平台是安卓。我想对我背后的设计方法提出一些批评。简而言之,我似乎面临着要么创建一个动态处理管道,我只能简单地指定一些设置并为其添加值,要么自动化程度最低,但仍然保持相对较低的水平写了多少代码,以及代码在做什么。

详情

到目前为止,我的设计方法如下:

  • 着色器文件存储在 assets 文件夹中
  • Java 充当前端,获取所有着色器文件,并将每个文件存储在一个 Shader 对象中,该对象包含有关着色器类型和原始着色器源的信息。
  • Java 然后将这些自定义着色器对象数组中的着色器数据发送到 C++(即,不是 OpenGL 着色器对象 - 尚未),然后由 JNI 解析。
  • 之后,JNI 将通过 Java 获得的着色器源发送到 ShaderHandler 类单例,该类为每种类型的着色器(一个用于顶点、一个用于片段、一个用于纹理等)保存一个多维 std::vector< std::vector< GLuint > >
    • ShaderHandler 类将着色器对象编译和链接到可编程接口中,一旦完成,它将通过将其功能扩展到充当 ShaderVariable 处理程序的组件类来引导大部分繁重的工作,方法是将值分配给它们是动态的。例如,once 可以简单地指定一个值/向量/矩阵/whatever 和要修改的索引数量,并将其传递给 ShaderVariable 处理程序。一旦 SVH 获得,它就会将其存储在 std::map 中,使用变量的名称作为其标识符。
  • 当提示时,这些 SVH 对象(存储在位于 ShaderHandler 类中的向量中)本质上是动态绑定其 std::map 中存在的任何值的属性和统一。

分配的一些内存是通过指针完成的,但不可否认,其中大部分是由堆栈组成的。我想知道我是否应该为此考虑更多的动态内存分配,因为在 Android 手机上,我认为堆栈溢出可能非常非常容易实现,对于具有良好图形等的雄心勃勃的游戏来说,这种方法是非常非常容易实现的。

想法?

【问题讨论】:

  • 你打算用这个解决什么问题?
  • 我的目标是创建一个更高级别的抽象接口,这将在未来为我节省 很多 代码。我的想法是我希望能够使用我创建的组件来简单地指定一些参数,并让其余的由解析器完成。例如,如果我想渲染 3 个球体并让它们相互碰撞,这样的系统将减少所需的代码。这主要是为了成为 Android NDK 的图形引擎。

标签: android c++ android-ndk opengl-es-2.0


【解决方案1】:

好的,所以我做了一些非常与我工作的图形引擎类似的事情。主要区别是我在 C++ 端做所有事情,而在 Java 端什么都不做(不是一件事),这消除了 JNI 障碍。

另外,我抽象了一些东西,就像我对 OpenGL 和 DX9 做的一样。

一些提示:

  • 顶点和片段着色器是链接到着色器“程序”的单独文件。按文件夹指定程序,然后在文件夹中搜索“gles2.vert”和“gles2.frag”。以后搜索“gles3.vert”/“gles3.frag”或“dx11.vert”/“dx11.frag”等。
  • 在您的源代码中嵌入了“默认”着色器。这允许您用默认值替换丢失的顶点/片段着色器。你不会相信顶点着色器是默认着色器的次数
  • 在链接之前,绑定属性位置。有一个预定义的属性名称/类型列表。着色器不使用属性也没关系。
  • 链接后,获取统一位置并将它们存储在您的 SVH 中。 std::map 将允许您指定一个不区分大小写的 Functor。此外,在运行时很容易推导出统一类型,以便在设置值时进行简单的类型检查。
  • 另外,在链接后使用 glUniform1i 设置采样器位置。这是为了使texN[0..N] 制服指向正确的采样器。
  • SVH 将具有默认值,或者您需要一个标志来确定用户是否设置了值。
  • 为程序保留一个状态。该程序可能有一个 std::map,但您需要有一个用户状态用于用户设置制服。这种状态使您可以使用具有不同统一值的相同着色器程序。用户状态不需要统一位置,因为它们将由程序 svh 映射解决。这让您无需绘制调用即可聚合统一值。

绘图

  • 显式绑定和取消绑定采样器,不要自动执行。很高兴能够在 sampler[1] 上加载 color_lut 并运行 sampler[0] 作为多个不同调用的纹理。
  • 在绑定程序之后,但在绘制之前,使用程序状态中的位置从用户状态解析和设置制服。

保持着色器程序、采样器和几何体彼此不知道。这种不可知的方法可以让您执行一些很酷的效果,例如我之前提到的 color_lut 着色器可以工作,因为几何体没有明确设置着色器并且不会消除采样器状态。这是一个可堆叠的材料系统。

我执行了一项检查,即在绑定着色器程序之前查看是否设置了 sampler0,如果是(并且没有明确设置着色器),那么我将设置一个默认纹理着色器。如果不存在纹理(并且如果设置为默认纹理着色器),我将回退到一个简单的颜色着色器。如果着色器程序不是 [null, defaultTex] 那么我不会搞砸它。

不要害怕提出任何其他问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多