主程序中的多个着色器程序
Multiple shader programs in main program
我使用着色器在 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 风格)我会有两个 classes 继承自一个抽象的 GLAbbrev class(不要问我为什么这么称呼它;我想不出另一个名字!)其中将有两个已实现的 classes,因此:
`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`
因此,每个 class 处理一个着色器程序并相应地渲染结果。然后,在我的应用程序 class 中,我有一个指向 GLAbbrev
的指针向量,它管理 GLAbbrev。 (这不是个好主意;它真的应该由另一个 class 管理,但请耐心等待)
Application::init()
{
glAbbrevs.push_back(new GLAbbrev_TextLight());
glAbbrevs.push_back(new GLAbbrev_Skybox());
}
然后我简单地将所有函数作为一个调用,例如:
Application::render()
{
for (auto i : glAbbrevs)
i.render();
}
以及更新等。关于 glViewport
和拥有相机,我认为在抽象 class 中实现这些是合乎逻辑的。因此,调整大小函数将在 GLAbbrev 中,并且 class 也将有一个指向 Camera
的受保护指针,因为我有多个版本的相机。因此,我可以在应用程序中声明 Camera 并将其传递给 glAbbrevs,例如:
for (auto i : glAbbrevs)
i.setCamera(camera);
这可能会有问题,但暂时可以解决。如您所见,这是我准备采用的一种设计方法,但不确定它是否是最有效的方法。其他图形程序员的意见是什么?着色器程序是否应该分布在各自的 classes 中来做一件事?或者应该有一个着色器程序,所有代码都在它们的顶点、片段和几何着色器中;或者应该只有一个 class 继承自 GLAbbrev,但具有呈现所需输出所需的所有着色器程序?
今天制作精良的游戏都使用多个着色器,每个着色器都有不同的目的和不同的实现。例如,您可以为天空盒使用一个着色器,为树木使用另一个着色器,这很好用,但它仍然不是最好的实现,在我们到达那里之前,我想澄清一下,使用这种方法有两种类型的着色器,一种绘制到屏幕(灯光等)和绘制到帧缓冲区(阴影贴图等)的。
现在的方法是将阴影贴图和其他贴图渲染到帧缓冲区(基本上是内存中可编辑的纹理。)渲染完所有贴图后,有一个大着色器可以处理光照和添加阴影贴图,所有这些,但问题是着色器变得非常大,因此开发人员创建自定义着色器读取器。他们从文件中读取着色器,并在读取时找到自己的自定义关键字(例如,他们可以使用诸如 'import' 或 '#include' 之类的关键字,因为它是预处理器。)和用 action 替换它们,因此可能有一个关键字“#include”,然后是一个文件,所以它将是“#include res/shaders/lights.fs”,然后他们将用 [ 中的着色器代码替换该行=30=],当然他们也会检查 lights.fs 中的关键字,所以在一天结束时,他们最终得到一个由许多小着色器组成的大型着色器,并使用该大型着色器处理所有帧缓冲区之前制作的(这是通过简单地将它们作为 sampler2D 传递来完成的。)以及所有的灯光和奇特的效果。
我建议查看 youtube 上的 thebennybox,尤其是他的 3D 游戏引擎系列,这将对您有很大帮助,虽然前半部分在 java 他后来切换到 c++,所有的 c++ 代码都可以在 github.
上找到
播放列表: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函数一个shader参数,先绑定再渲染,最后解绑定。例如
void render(Shader s) {
shader.bind();
this.mesh.render(); //Render here
shader.unbind();
}
如果你是守旧派,你可能会用一个又大又笨重的着色器来做所有事情,是的。
然而,许多人更喜欢所谓的 deferred
渲染。这是一种策略,您可以通过该策略将最终帧划分为多个层,然后以特定方式组合这些层。 Read more about it here.
我个人更喜欢延迟的方式,因为它提供了更简洁的方法、更少的意大利面条式代码和简洁的设计。
希望对您有所帮助!
我使用着色器在 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 风格)我会有两个 classes 继承自一个抽象的 GLAbbrev class(不要问我为什么这么称呼它;我想不出另一个名字!)其中将有两个已实现的 classes,因此:
`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`
因此,每个 class 处理一个着色器程序并相应地渲染结果。然后,在我的应用程序 class 中,我有一个指向 GLAbbrev
的指针向量,它管理 GLAbbrev。 (这不是个好主意;它真的应该由另一个 class 管理,但请耐心等待)
Application::init()
{
glAbbrevs.push_back(new GLAbbrev_TextLight());
glAbbrevs.push_back(new GLAbbrev_Skybox());
}
然后我简单地将所有函数作为一个调用,例如:
Application::render()
{
for (auto i : glAbbrevs)
i.render();
}
以及更新等。关于 glViewport
和拥有相机,我认为在抽象 class 中实现这些是合乎逻辑的。因此,调整大小函数将在 GLAbbrev 中,并且 class 也将有一个指向 Camera
的受保护指针,因为我有多个版本的相机。因此,我可以在应用程序中声明 Camera 并将其传递给 glAbbrevs,例如:
for (auto i : glAbbrevs)
i.setCamera(camera);
这可能会有问题,但暂时可以解决。如您所见,这是我准备采用的一种设计方法,但不确定它是否是最有效的方法。其他图形程序员的意见是什么?着色器程序是否应该分布在各自的 classes 中来做一件事?或者应该有一个着色器程序,所有代码都在它们的顶点、片段和几何着色器中;或者应该只有一个 class 继承自 GLAbbrev,但具有呈现所需输出所需的所有着色器程序?
今天制作精良的游戏都使用多个着色器,每个着色器都有不同的目的和不同的实现。例如,您可以为天空盒使用一个着色器,为树木使用另一个着色器,这很好用,但它仍然不是最好的实现,在我们到达那里之前,我想澄清一下,使用这种方法有两种类型的着色器,一种绘制到屏幕(灯光等)和绘制到帧缓冲区(阴影贴图等)的。
现在的方法是将阴影贴图和其他贴图渲染到帧缓冲区(基本上是内存中可编辑的纹理。)渲染完所有贴图后,有一个大着色器可以处理光照和添加阴影贴图,所有这些,但问题是着色器变得非常大,因此开发人员创建自定义着色器读取器。他们从文件中读取着色器,并在读取时找到自己的自定义关键字(例如,他们可以使用诸如 'import' 或 '#include' 之类的关键字,因为它是预处理器。)和用 action 替换它们,因此可能有一个关键字“#include”,然后是一个文件,所以它将是“#include res/shaders/lights.fs”,然后他们将用 [ 中的着色器代码替换该行=30=],当然他们也会检查 lights.fs 中的关键字,所以在一天结束时,他们最终得到一个由许多小着色器组成的大型着色器,并使用该大型着色器处理所有帧缓冲区之前制作的(这是通过简单地将它们作为 sampler2D 传递来完成的。)以及所有的灯光和奇特的效果。
我建议查看 youtube 上的 thebennybox,尤其是他的 3D 游戏引擎系列,这将对您有很大帮助,虽然前半部分在 java 他后来切换到 c++,所有的 c++ 代码都可以在 github.
上找到播放列表: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函数一个shader参数,先绑定再渲染,最后解绑定。例如
void render(Shader s) {
shader.bind();
this.mesh.render(); //Render here
shader.unbind();
}
如果你是守旧派,你可能会用一个又大又笨重的着色器来做所有事情,是的。
然而,许多人更喜欢所谓的 deferred
渲染。这是一种策略,您可以通过该策略将最终帧划分为多个层,然后以特定方式组合这些层。 Read more about it here.
我个人更喜欢延迟的方式,因为它提供了更简洁的方法、更少的意大利面条式代码和简洁的设计。
希望对您有所帮助!