这个学期,我在我的大学学习了计算机图形学课程.目前,我们开始研究一些更先进的东西,如高度图,平均法线,细分等.
我来自面向对象的背景,所以我试图将我们所做的一切都放到可重用的类中.我在创建相机类方面取得了很大的成功,因为它主要取决于对gluLookAt()的一次调用,它几乎独立于OpenGL状态机的其余部分.
但是,我在其他方面遇到了一些麻烦.使用对象来表示原语并没有真正成功.这是因为实际的渲染调用依赖于很多外部事物,比如当前绑定的纹理等.如果您突然想要从特定类的表面法线更改为顶点法线,则会导致严重的问题.
我开始怀疑OO原则是否适用于OpenGL编码.至少,我认为我应该让我的课程不那么精细.
什么是堆栈溢出社区对此的看法?您的OpenGL编码最佳实践是什么?
最实用的方法似乎是忽略大多数不直接适用的OpenGL功能(或者很慢,或者不是硬件加速,或者不再是硬件的良好匹配).
是否OOP,渲染一些场景,这些场景是您通常拥有的各种类型和实体:
几何(网格).大多数情况下,这是一个顶点数组和索引数组(即每个三角形的三个索引,又名"三角形列表").顶点可以是某种任意格式(例如,只有float3位置; float3位置+ float3正常; float3位置+ float3 normal + float2 texcoord;依此类推).因此,要定义一个您需要的几何体:
定义它的顶点格式(可以是位掩码,来自格式列表的枚举; ...),
有顶点数组,它们的组件是交错的("交错数组")
有三角形的数组.
如果你在OOP的土地上,你可以称这个类为Mesh.
材质 - 定义渲染某些几何体的方式.在最简单的情况下,例如,这可以是对象的颜色.或者是否应该应用照明.或者对象是否应该进行alpha混合.或者使用纹理(或纹理列表).或者要使用的顶点/片段着色器.等等,可能性是无穷无尽的.首先将您需要的东西放入材料中.在OOP土地上,这个类可以被称为(惊喜!)一个材料.
场景 - 您拥有几何图形,材料集合,时间来定义场景中的内容.在一个简单的例子中,场景中的每个对象都可以通过以下方式定义: - 它使用什么几何(指向网格的指针), - 应该如何渲染(指向材质的指针), - 它所在的位置.这可以是4×4变换矩阵,或4×3变换矩阵,或矢量(位置),四元数(方向)和另一个矢量(比例).我们称之为OOP土地中的节点.
相机.嗯,相机只不过是"它放置的位置"(再次,4x4或4x3矩阵,或位置和方向),加上一些投影参数(视野,纵横比......).
所以基本就是这样!你有一个场景,它是一组引用网格物体和材质的节点,你有一个定义观察者所在位置的相机.
现在,在哪里放置实际的OpenGL调用只是一个设计问题.我会说,不要将OpenGL调用放入Node或Mesh或Material类.而是制作类似OpenGLRenderer的东西,它可以遍历场景并发出所有调用.或者,更好的是,创建一个独立于OpenGL遍历场景的东西,并将较低级别的调用放入OpenGL依赖类中.
所以,是的,以上所有内容都与平台无关.走这条路,你会发现glRotate,glTranslate,gluLookAt和朋友都没用.你已经拥有了所有的矩阵,只需将它们传递给OpenGL即可.这就是真实游戏/应用程序中大多数实际代码的工作方式.
当然,上述情况可能因更复杂的要求而变得复杂.特别是,材料可能非常复杂.网格通常需要支持许多不同的顶点格式(例如,打包法线以提高效率).可能需要在层次结构中组织场景节点(这可能很容易 - 只需添加指向节点的父/子指针).皮肤网格和动画通常会增加复杂性.等等.
但主要思路很简单:有几何,有材质,场景中有物体.然后一些小代码能够呈现它们.
在OpenGL的情况下,设置网格很可能会创建/激活/修改VBO对象.在渲染任何节点之前,需要设置矩阵.设置材料将触及大部分剩余的OpenGL状态(混合,纹理,光照,合成器,着色器......).