OpenGL 每网格 material(着色器)

OpenGL per-mesh material (shader)

所以,我正在使用 C++ 和 OpenGL 4 开发一个简单的游戏引擎。现在我正在努力渲染导入的模型。

我正在使用 FBX sdk 导入 fbx 模型,使用一种非常简单的方法:基本上我访问 fbx 的每个节点并将网格数据附加到一个大结构,稍后用于渲染。但是,我希望能够为模型使用的每个 material 指定不同的片段着色器(例如,为汽车轮辋和灯光指定不同的着色器)。

作为参考,UE4 有一个 material 系统,允许用户使用类似蓝图的编辑器定义简单的着色器。

我想将类似的概念应用于我的引擎,允许创建一个 material 对象,该对象指定一段片段着色器代码和一组要使用的纹理。

我面临的问题是:

  1. 很明显,我必须为每个使用不同 material 的模型部件分开绘制调用,因为我不能在绘制调用中间交换程序(可以吗?):此时, 是每个部分有一个单独的 vao/vbo/ebo 还是一个单独的 vao/vbo/ebo 更好,并跟踪一个部分结束和下一个部分开始的位置? (我想这是最好的选择)
  2. 仅预编译着色器片段并将其动态附加到当前程序(即 glAttach + glLinkProgram + glUseProgram)是一个好习惯,还是预link 整个每个 material 的程序,考虑到顶点着色器总是相同的?
  1. 不,您不能在绘制调用的过程中更改程序。根据您的数据布局,对于 GPU 将如何执行有不同的意见和测试。我的经验是,如果您在第一次上传网格数据后不打算修改它们,最有效的方法是使用一个 VAO,两个 VBO:一个用于索引,一个用于其余数据。发出绘制调用时,您可以根据网格数据(您应该跟踪)偏移索引缓冲区,并偏移着色器属性的配置。由于内存块是连续的,因此这种方法允许对缓存更加友好和高效的内存访问。然而,正如我所提到的,在某些情况下这不是最有效的方法(尽管我相信它仍然足够有效)。这取决于您的硬件和驱动程序。

  2. 在启动渲染循环之前预编译和link所有程序。这是最有效的方法

另外,我建议您研究一下 UBER 着色器技术。此方法基于为不同的可能输入创建着色器,并创建一组 defines 或子例程体系结构,允许您编译同一着色器的不同版本(例如,您可能有一个带有法线纹理,您可能想要应用凹凸贴图,但其他模型可能没有此纹理,因此执行完全相同的着色器将导致未定义的行为或崩溃)。