OpenGL与OOP程序结构 我已经用OpenGL和C++完成了各种演示项目,但是它们都涉及到简单渲染一个立方体(或者类似的简单网格),并有一些有趣的效果。对于这样一个简单的场景,立方体的顶点数据可以存储在一个不雅观的全局数组中。我现在正在研究如何使用不同类型的多个对象渲染更复杂的场景

OpenGL与OOP程序结构 我已经用OpenGL和C++完成了各种演示项目,但是它们都涉及到简单渲染一个立方体(或者类似的简单网格),并有一些有趣的效果。对于这样一个简单的场景,立方体的顶点数据可以存储在一个不雅观的全局数组中。我现在正在研究如何使用不同类型的多个对象渲染更复杂的场景,c++,oop,opengl,architecture,game-engine,C++,Oop,Opengl,Architecture,Game Engine,我认为对不同类型的对象使用不同的类是有意义的(Rock,Tree,Character,等等),但我想知道如何清晰地分解场景中对象的数据和渲染功能。每个类都将存储自己的顶点位置、纹理坐标、法线等数组。但是我不确定将OpenGL调用放在哪里。我想我将有一个循环(在世界或场景类中),它迭代场景中的所有对象并渲染它们 渲染应该包括在每个对象中调用渲染方法(Rock::render(),Tree::render(),…),还是调用将对象作为参数的单个渲染方法(render(Rock),render(Tre

我认为对不同类型的对象使用不同的类是有意义的(
Rock
Tree
Character
,等等),但我想知道如何清晰地分解场景中对象的数据和渲染功能。每个类都将存储自己的顶点位置、纹理坐标、法线等数组。但是我不确定将OpenGL调用放在哪里。我想我将有一个循环(在
世界
场景
类中),它迭代场景中的所有对象并渲染它们

渲染应该包括在每个对象中调用渲染方法
(Rock::render(),Tree::render(),…)
,还是调用将对象作为参数的单个渲染方法
(render(Rock),render(Tree),…)
?后者看起来更干净,因为我不会在每个类中都有重复的代码(尽管可以通过从单个
RenderableObject
类继承来减轻这一问题),并且如果我以后想将render()方法移植到DirectX,它允许轻松地替换render()。另一方面,我不确定是否可以将它们分开,因为我可能需要存储在对象中的OpenGL特定类型(例如顶点缓冲区)。此外,将渲染功能与对象分离似乎有点麻烦,因为它必须调用大量的
Get()
方法才能从对象获取数据。最后,我不确定该系统将如何处理必须以不同方式绘制的对象(不同的着色器、传递到着色器的不同变量等)


这些设计中的一个明显比另一个好吗?我可以通过哪些方式改进它们,以保持代码井然有序和高效?

我认为这是一种答案。看一看它和它的名字。它应该为您提供一些有趣的见解,如何使用OpenGL、C++和OOP。p> 首先,现在甚至不必担心平台独立性。等到你对你的架构有了更好的了解

进行大量绘图调用/状态更改很慢。在引擎中执行此操作的方式是,您通常希望有一个可以绘制自身的可渲染类。此可渲染文件将与它需要的任何缓冲区(例如顶点缓冲区)和其他信息(例如顶点格式、拓扑、索引缓冲区等)关联。着色器输入布局可以与顶点格式相关联

您将需要一些基本的地理类,但将任何复杂的内容都推迟到处理索引tris的某种类型的网格类。对于performant应用程序,您需要在着色管道中对类似输入类型的调用(以及可能的数据)进行批处理,以最小化不必要的状态更改和管道刷新

着色器参数和纹理通常通过与可渲染对象关联的某些材质类进行控制

场景中的每个可渲染对象本身通常是分层场景图中节点的一个组件,其中每个节点通常通过某种机制继承其祖先的变换。您可能需要一个场景消隐器,该消隐器使用空间分区方案来快速确定可见性,并避免视图之外的事物的绘制调用开销

大多数交互式3d应用程序的脚本/行为部分都与场景图节点框架和事件/消息系统紧密连接或挂钩

这一切都在一个高级循环中结合在一起,您可以根据时间更新每个子系统,并在当前帧绘制场景

显然,有很多小细节被遗漏了,但这可能会变得非常复杂,这取决于你想要的通用性和性能,以及你想要的视觉复杂性

在确定所有零件的装配方式之前,您的
draw(renderable)
,vs
renderable.draw()
问题或多或少是不相关的

[Update]在这个空间工作了一段时间后,一些人增加了洞察力:

话虽如此,在商业引擎中,它通常更像是
draw(renderBatch)
,其中每个渲染批处理都是以某种有意义的方式对GPU进行同质化的对象的聚合,因为它迭代异构对象(通过多态性在“纯”OOP场景图中)并调用
obj.draw()
one-by-one具有可怕的缓存位置,通常是对GPU资源的低效使用。采用面向数据的方法来设计引擎如何以尽可能最有效的方式与其底层图形API进行对话是非常有用的,这样可以在不负面影响代码结构/可读性的情况下尽可能多地进行批处理


一个实用的建议是使用一种简单/纯粹的方法编写第一个引擎,以便真正熟悉领域空间。然后在第二遍(或者可能是重写)中,关注硬件:内存表示、缓存位置、管道状态、带宽、批处理和并行性。一旦你真的开始考虑这些事情,你就会意识到你最初的设计大部分都已经过时了。很有趣。

这里是我为物理模拟实现的,它工作得很好,抽象性很好。首先,我将功能分为以下几个类:

  • 对象—保存所有必需对象信息的容器
  • AssetManager—加载模型和纹理,拥有它们(唯一),返回指向对象资源的原始指针
  • 渲染器-处理所有OpenGL调用等,在GPU和