我正在学习OpenGL并试图掌握最佳实践。我正在研究一个简单的C ++演示项目,然而它的目标是比我在网上看到的大多数教程更通用,结构更好(不是只放在main()
中)。我想使用现代OpenGL方式,这意味着VAO和着色器。我最关心的是VAO和着色器程序的关系。也许我在这里遗漏了一些东西。
我现在正在考虑最好的设计。请考虑以下情形:
这引出了我设计的基本三个组成部分:
ShaderProgram
class - 每个实例包含一个顶点着色器和片段着色器(从给定的字符串初始化)Object
class - 具有变换矩阵和对形状实例的引用Shape
基类 - 以及派生类,例如BoxShape
,SphereShape
;每个派生类都知道如何生成其网格并将其转换为缓冲区以及如何将其映射到顶点属性,换句话说,它将初始化自己的VAO;它还知道用于自我渲染的glDraw...
函数在渲染场景时,我会调用glUseProgram(rgbaShaderProgram)
。然后我将浏览所有可以使用此程序渲染并渲染它们的对象。然后我将切换到glUseProgram(textureShaderProgram)
并浏览所有纹理对象。
渲染单个对象时:
1)我将调用glUniformMatrix4fv()
来设置单个变换矩阵(当然包括投影矩阵等)。
2)然后我将调用与渲染相关联的形状
3)当形状被渲染时,它将绑定其VAO,调用其特定的glDraw...()
函数然后解除绑定VAO
在我的设计中,我想要解除Shape
和ShaderProgram
之间的依赖关系,因为它们在理论上可以互换。但仍然存在一些依赖性。当在特定的...Shape
类中生成顶点并为它们设置缓冲区时,我已经需要知道我需要为每个顶点生成纹理坐标而不是RGBA组件。当设置顶点属性指针glVertexAttribPointer
时,我必须知道着色器程序将使用例如浮点数而不是整数(否则我将不得不调用glVertexAttribIPointer
)。我还需要知道哪个属性将在着色器程序中的哪个位置。换句话说,我正在混合唯一形状几何的责任和关于它将如何呈现的先验知识。因此,我无法使用与其不兼容的着色器程序渲染形状。
最后我的问题是:如何改进我的设计以实现目标(渲染场景)并同时保持多功能性(着色器和形状的可互换性),强制正确使用(不允许混合错误的形状与不兼容的着色器),具有最佳性能(避免不必要的程序或上下文切换)并保持良好的设计原则(一类 - 一个责任)。
答案 0 :(得分:0)
我会做的是准备ShaderProgram(s)模板,我适应VAO属性。 毕竟,着色器程序最初是文本。您可能要做的是编写顶点和片段程序的主要函数,并根据绑定的VAO将“标题”附加到它们。在着色器中使用的变量使用标准化名称可能很有用,例如InPos,InNor,InTan,InTex。 这样,您可以扫描VAO中缺少的元素,但在着色器中使用,只需将它们作为const值附加到标题中,并使用一些默认设置。 我通过ShaderManager执行此操作,使用RequestShader(模板,VAO)类型的函数,该模板使模板适应VAO,并缓存已编译的着色器以供以后使用。 如果另一个VAO具有相同的属性,并且需要相同的模板,则返回预编译的缓存版本以避免相同的适应过程。