主程序中的多个着色器程序

时间:2015-04-09 00:13:44

标签: c++ opengl

我使用着色器在OpenGL中编程。据我所知,每个着色器程序只能有一种类型的着色器;例如顶点,片段,几何等等。所以,这是我所关注的问题以及解决它的解决方案:

在任何游戏中,都会出现纹理&照明,环境贴图,阴影和各种时髦的功能;但这些只是一个着色器程序吗?或者它们是否分散在各种着色器程序中?我的猜测是第二个,所以如果我有:

`ShaderProgram *TextLightProgram;
   -> Compiles and links TextLight.vert
   -> Compiles and links TextLight.frag`
   -> Handles uniforms pertaining to said program

`ShaderProgram *SkyboxProgram;
   -> Compiles and links Skybox.vert
   -> Compiles and links Skybox.frag
   -> Handles uniforms pertaining to said program`

然后,这是否适用于我的主要C ++程序?着色器的两组(着色器程序)各自做自己的事情。

因为我不想显然把它们放在main.cpp中(我正在做这个OOP风格)我会有两个继承自抽象GLAbbrev类的类(不要问我为什么称它为那个;我想不出另一个名字!)其中会有两个实施的类,因此:

`GLAbbrev -> abstract class for handling ShaderPrograms

 GLAbbrev_TextLight 
    -> Inherits from GLAbbrev
    -> Implements functions and has ShaderProgram *TextLightProgram`

 GLAbbrev_Skybox
    -> Inherits from GLAbbrev
    -> Implements functions and has ShaderProgram *Skybox`

因此,每个类处理一个着色器程序并相应地呈现结果。然后,在我的Application类中,我有一个指向GLAbbrev的指针向量,用于管理GLAbbrevs。 (不是一个好主意;它应该真的由另一个班级管理,但在这里忍受我)

Application::init()
{
    glAbbrevs.push_back(new GLAbbrev_TextLight());
    glAbbrevs.push_back(new GLAbbrev_Skybox());
}

然后我只需将所有功能称为一个,例如:

Application::render()
{
    for (auto i : glAbbrevs)
        i.render();
}

除了更新等。关于glViewport和有摄像头,我认为在抽象类中已经实现这些是合乎逻辑的。因此,resize函数将在GLAbbrev中,并且该类还将具有指向Camera的受保护指针,因为我有多个版本的摄像头。因此,我可以在应用程序中声明Camera并将其传递给glAbbrevs,如:

for (auto i : glAbbrevs)
    i.setCamera(camera);

这可能会有问题,但它现在会成功。正如您所看到的,这是我准备采取的一种设计方法,但我不确定它是否是最有效的。其他图形程序员有什么看法?着色器程序是否应该分散在各自的类中以做一件事?或者应该有一个着色器程序,其顶部,frag和几何着色器中包含所有代码;或者应该只有一个继承自GLAbbrev的类,但是具有渲染所需输出所需的所有着色器程序?

2 个答案:

答案 0 :(得分:1)

今天每个精心制作的游戏都使用多个着色器,每个着色器具有不同的目的和不同的实现。例如,你可以使用一个着色器用于天空盒,另一个用于树的着色器,这很好用,但它仍然不是最好的实现,在我们到达那里之前我想澄清一下,使用这种方法有两种类型的着色器,一种绘制到屏幕(灯光等)和绘制到帧缓冲区(阴影贴图等)的那些。

这些天来完成的方法是将阴影贴图和其他贴图渲染到帧缓冲区(在内存中基本上是可编辑的纹理。)在渲染完所有贴图之后,有一个大的着色器处理光照并添加阴影贴图和所有这些,但问题是着色器变得非常大,所以开发人员创建自定义着色器读取器。他们从文件中读取着色器,在阅读时,他们会找到自己的自定义关键字(例如,他们可以使用'import'或'#include'这样的关键字更多可能'#include'scince它是预处理器。)并替换它们有了动作,所以可能有一个关键字'#include'然后是一个文件,所以它将是'#include res / shaders / lights.fs'然后他们将用lights.fs中的着色器代码替换该行。当然,他们也会检查lights.fs中的关键字,所以在一天结束时他们最终得到一个由许多小着色器组成的大型着色器,并且使用那个大的着色器处理之前制作的所有帧缓冲区(这是通过简单地将它们作为sampler2D传递完成。)以及所有灯光和奇特效果。

我建议你查看youtube上的thebennybox,特别是他的3D游戏引擎系列它会对你有很大的帮助,虽然上半部分是在java中,他后来切换到c ++,所有的c ++代码都可以在github上找到。 / p>

播放列表:https://www.youtube.com/playlist?list=PLEETnX-uPtBXP_B2yupUKlflXBznWIlL5

Thebennybox:https://www.youtube.com/user/thebennybox/featured

Github:https://github.com/BennyQBD/3DEngineCpp

PS。调用render的更好方法是为render函数提供一个着色器参数,该参数被绑定然后呈现并最终解除绑定。例如

void render(Shader s) {
   shader.bind();
   this.mesh.render(); //Render here
   shader.unbind();
}

答案 1 :(得分:0)

如果你是老派,你可以用一个笨重的着色器做任何事情,是的。 然而,许多人更喜欢所谓的deferred渲染。这是一种策略,您可以通过该策略在多个图层上划分最终帧,然后您将以特定方式合并。 Read more about it here

我个人更喜欢推迟的方式,因为它提供了更简洁的方法,更少的意大利面条代码和简洁的设计。

希望有所帮助!