包装 opengl 模型的最佳方式

best way to wrap opengl models

简而言之:什么是 "preferred" 包装 OpenGL 缓冲区、着色器的方法 and/or 更高级别所需的矩阵 "model"对象?

我正在尝试用基于核心 OpenGL 3.3 的 C++ 编写这个微型图形引擎,我想实现一个尽可能干净的解决方案来包装更高级别的 "model" 对象,该对象将包含其顶点缓冲区、全局 position/rotation、纹理(也许还有着色器?)和其他潜在信息。

我已经研究了 this open source engine, called GamePlay3D 并且不太同意它解决这个问题的许多方面。有什么好的资源可以讨论现代 OpenGL 的这个主题吗?还是有一些简单干净的方法来做到这一点?

这在很大程度上取决于您希望使用引擎执行的操作。另请注意,这些概念与 DirectX(或任何其他图形 API)相同,因此不要过多地关注 OpenGL 的搜索。以下是 3D 引擎中非常常见的几点(名称可能不同):

网格: 一个网格包含子网格,每个子网格包含一个顶点缓冲区和一个索引缓冲区。这个想法是每个子网格将使用不同的 material(例如,在角色的网格中,body 可能有一个子网格,衣服可能有一个子网格。)

实例: 一个实例(或网格实例)引用一个网格,一个 material 的列表(一个用于网格中的每个子网格),并包含 "per instance" 着色器制服(世界矩阵等),通常分组在一个统一的缓冲区。

Material:(这部分根据引擎的复杂程度变化很大)。一个基本版本将包含一些纹理、一些渲染状态(混合状态、深度状态)、一个着色器程序和一些所有实例通用的着色器制服(例如颜色,但也可能在实例中,具体取决于什么你想做。)

更复杂的版本通常将 material 分隔在 通道 中(或者有时 技术 包含 passes) 包含上一段中的所有内容。您可以检查 Ogre3D documentation for more info about that and to take a look at one possible implementation. There's also a very good article called Designing a Data-Driven Renderer in GPU PRO 3,它描述了一个基于相同想法(但也更复杂)的更灵活的系统。

场景:(我在这里称它为场景,但它真的可以被称为任何东西)。它提供来自环境的着色器参数和纹理(光照值、环境贴图等)。

我认为这就是基础知识。考虑到这一点,如果您需要实现细节,您应该能够找到任何 open-source 3D 引擎代码的方法。

这是 Jerem 出色回答的补充。

在底层,没有"model"这样的东西,只有缓冲数据和用来处理它的代码。在高层次上,"model" 的概念因应用程序而异。国际象棋游戏的每个棋子都有一个静态网格,具有共享的纹理和材质,但第一人称射击游戏可能有复杂的模型,包括多个部分、可互换的皮肤、命中框、绑定、动画等等。

案例研究:国际象棋

国际象棋有六颗棋子,两种颜色。让我们对图形引擎进行过度设计,以展示如果您需要在同一屏幕上同时绘制数千个国际象棋游戏,而不是只绘制一个游戏,它是如何完成的。以下是您的操作方法。

  • 将所有模型存储在一个大缓冲区中。该缓冲区将所有六个模型的所有顶点和索引数据聚集在一起。这意味着您在绘图时无需切换缓冲区/VAO。此外,此缓冲区永远不会更改,除非用户进入设置并为棋子选择不同的样式。

  • 创建另一个缓冲区,其中包含游戏中每个棋子的当前位置、每个棋子的颜色以及对该棋子模型的引用。此缓冲区每帧更新一次。

  • 加载必要的纹理。也许法线会在一个纹理中,而漫反射贴图会是一个数组纹理,其中一层用于白色,另一层用于黑色。纹理经过精心设计,因此您在绘制棋子时无需更改它们。

  • 绘制所有的碎片,你只需要更新一个缓冲区,然后调用glMultiDrawElementsIndirect()...每帧一次,并且它绘制了所有的棋子。如果那不可用,您可以回退到 glDrawElements() 或其他。

分析

您会发现这种设计并不适用于所有情况。

  • 如果您必须将新模型流式传输到内存中并删除旧模型怎么办?

  • 如果模型有不同大小的纹理怎么办?

  • 如果模型更复杂,有动画或正向运动学怎么办?

  • 半透明模型呢?

  • 命中框和物理数据呢?

  • 不同的LOD怎么办?

这里的问题是您的解决方案,甚至是 "model" 的概念,都会根据您的需求而大不相同。