最实用的方法似乎是忽略大部分不直接适用的 OpenGL 功能(或者速度很慢,或者没有硬件加速,或者不再适合硬件)。
OOP 与否,要渲染一些场景,这些是您通常拥有的各种类型和实体:
几何(网格)。大多数情况下,这是一个顶点数组和索引数组(即每个三角形三个索引,也称为“三角形列表”)。顶点可以是某种任意格式(例如,只有 float3 位置;float3 位置 + float3 normal;float3 位置 + float3 normal + float2 texcoord;等等)。因此,要定义一个几何图形,您需要:
- 定义它的顶点格式(可以是位掩码,格式列表中的枚举;...),
- 具有顶点数组,其组件交错(“交错数组”)
- 有三角形数组。
如果您在 OOP 领域,您可以将此类称为Mesh。
材料- 定义如何渲染某些几何图形的东西。例如,在最简单的情况下,这可以是对象的颜色。或者是否应该应用照明。或者对象是否应该进行 alpha 混合。或要使用的纹理(或纹理列表)。或要使用的顶点/片段着色器。等等,可能性是无穷无尽的。首先将您需要的东西放入材料中。在 OOP 领域,该类可以称为(惊喜!)Material。
场景- 你有几何图形,材料集合,时间来定义场景中的内容。在一个简单的情况下,场景中的每个对象可以通过以下方式定义: - 它使用什么几何体(指向网格的指针), - 它应该如何渲染(指向材质的指针), - 它的位置。这可以是 4x4 变换矩阵,或 4x3 变换矩阵,或向量(位置)、四元数(方向)和另一个向量(比例)。让我们称其为OOP 领域中的节点。
相机。好吧,相机只不过是“它的放置位置”(同样,一个 4x4 或 4x3 矩阵,或位置和方向),加上一些投影参数(视野、纵横比……)。
所以基本上就是这样!您有一个场景,它是一堆引用网格和材质的节点,并且您有一个定义查看器位置的相机。
现在,将实际的 OpenGL 调用放在哪里只是一个设计问题。我会说,不要将 OpenGL 调用放入 Node 或 Mesh 或 Material 类中。相反,制作类似OpenGLRenderer的东西,它可以遍历场景并发出所有调用。或者,更好的是,制作独立于 OpenGL 的遍历场景的东西,并将较低级别的调用放入依赖于 OpenGL 的类中。
所以是的,以上所有内容都与平台无关。这么下去,你会发现glRotate、glTranslate、gluLookAt和朋友们都挺没用的。您已经拥有所有矩阵,只需将它们传递给 OpenGL。无论如何,这就是真实游戏/应用程序中大多数真实代码的工作方式。
当然,上述内容可能会因更复杂的要求而变得复杂。特别是,材料可能非常复杂。网格通常需要支持许多不同的顶点格式(例如,为了提高效率而打包法线)。场景节点可能需要按层次结构组织(这很容易 - 只需向节点添加父/子指针)。蒙皮网格和动画通常会增加复杂性。等等。
但主要思想很简单:有几何,有材质,场景中有物体。然后一小段代码就可以渲染它们。
在 OpenGL 的情况下,设置网格很可能会创建/激活/修改 VBO 对象。在渲染任何节点之前,需要设置矩阵。设置材质会触及大部分剩余的 OpenGL 状态(混合、纹理、照明、组合器、着色器……)。