Opengl 每种情况下的GLSL着色器

Opengl 每种情况下的GLSL着色器,opengl,glsl,Opengl,Glsl,在我的游戏中,我想为每种情况创建单独的GLSL着色器。例如,如果我有3个模型角色,闪亮剑和模糊鬼,我想将渲染阴影,动画阴影和灯光阴影设置到角色,然后渲染阴影,lightingShader和specularShader设置为闪亮剑,最后我想将renderShader、lightingShader和blurShader设置为模糊重影 renderShader应该将顶点的位置乘以投影、世界和其他矩阵,并且它的fragmet着色器应该简单地将纹理设置为模型 animationShader应该通过给定的骨

在我的游戏中,我想为每种情况创建单独的GLSL着色器。例如,如果我有3个模型
角色
闪亮剑
模糊鬼
,我想将
渲染阴影
动画阴影
灯光阴影
设置到
角色
,然后
渲染阴影
lightingShader
specularShader
设置为
闪亮剑
,最后我想将
renderShader
lightingShader
blurShader
设置为
模糊重影

renderShader
应该将顶点的位置乘以投影、世界和其他矩阵,并且它的fragmet着色器应该简单地将纹理设置为模型

animationShader
应该通过给定的骨骼变换来变换顶点

lightingShader
应进行照明,
specularLighting
应进行镜面照明

blurShader
应执行模糊效果

首先,我如何在不同的着色器上进行多顶点变换?因为
animationShader
应该计算顶点的动画位置,然后
renderShader
应该获得该位置并通过一些矩阵对其进行变换

其次,如何更改不同着色器上片段的颜色

基本思想是,我希望能够为每个场景/效果使用不同的着色器,但我不知道如何实现它


我需要知道我应该如何在opengl中使用这些着色器,以及我应该如何使用GLSL,以便所有着色器能够相互完成,并且着色器不会关心是否使用了其他着色器。

对于着色器阶段的每个组合,您必须创建一个单独的着色器程序。为了节省工作和冗余,您可以使用一些缓存结构为每个请求的组合只创建一次程序,并在请求时重用它

可以对着色器阶段执行类似操作。但是,着色器阶段不能从多个编译单元链接(然而,这是OpenGL开发中正在进行的工作,OpenGL-4的可分离着色器是其中的垫脚石)。但是可以从多个源编译着色器。因此,您需要将每个所需效果的函数写入单独的源代码中,然后在编译时将它们合并。再次使用缓存结构将源模块组合映射到着色器对象

因评论而更新 假设您想要一些模块化。为此,我们可以利用glShaderSource接受多个源字符串的事实,它只是连接。您可以编写许多着色器模块。进行逐顶点照明计算的人

uniform vec3 light_positions[N_LIGHT_SOURCES];
out vec3 light_directions[N_LIGHT_SOURCES];
out vec3 light_halfdirections[N_LIGHT_SOURCES];

void illum_calculation()
{
    for(int i = 0; i < N_LIGHT_SOURCES; i++) {
        light_directions[i] = ...;
        light_halfdirections[i] = ...;
    }
}
将其放入illum\u Skeleton\u anim.vs.glslmod中。然后你有一些共同的标题

#version 330
uniform ...;
in ...;
还有一些包含主函数的公共尾部,它调用所有不同的阶段

void main() {
    skeletal_animation();
    illum_calculation();
}
等等。现在,您可以按正确的顺序将所有这些模块加载到单个着色器阶段。可以对所有着色器阶段执行相同的操作。片段着色器非常特殊,因为它可以同时写入多个帧缓冲区目标(在足够大的OpenGL版本中)。从技术上讲,你可以通过很多阶段之间的变化。因此,可以在每个帧缓冲区目标的着色器阶段之间传递自己的一组变量。但是,几何体和变换的顶点位置对所有这些都是通用的。

您所要求的绝对不是琐碎的,而且对于您描述的数量相对有限的“着色器”类型来说,可能是极其过分的

做你想做的事情需要开发你自己的着色语言。它可能是GLSL的高度
#define
d版本,但您编写的着色器不是纯GLSL。它们将有专门的钩子,并以预期代码流入其他代码的方式编写

您需要有自己的方式来指定语言的输入和输出。如果要将着色器连接在一起,则必须说明谁的输出将转到哪个着色器的输入。某些输入可以来自实际着色器阶段输入,而其他输入则来自其他着色器。着色器写入的某些输出将是实际着色器阶段输出,而其他输出将馈送其他着色器

因此,需要来自另一个着色器的输入的着色器必须在该另一个着色器之后执行。您的系统必须计算出依赖关系图

一旦计算出特定着色器序列的所有输入和输出,就必须获取所有这些着色器文本文件,并根据需要将它们编译为GLSL。显然,这是一个不平凡的过程

您的着色器语言可能如下所示:

INPUT vec4 modelSpacePosition;
OUTPUT vec4 clipSpacePosition;

uniform mat4 modelToClipMatrix;

void main()
{
  clipSpacePosition = modelToClipMatrix * modelSpacePosition;
}
您的“编译器”需要对此进行文本转换,将对
modelSpacePosition
的引用转换为实际的顶点着色器输入或由另一着色器编写的变量(视情况而定)。类似地,如果要将
clipSpacePosition
写入
gl\u Position
,则需要将
clipSpacePosition
的所有用法转换为
gl\u Position
。此外,还需要删除显式输出声明

简言之,这将是大量的工作


如果要这样做,我强烈建议您避免尝试合并顶点着色器和片段着色器的概念。保持此着色器系统在定义良好的着色器阶段内工作。因此,“lightingShader”需要是顶点着色器或片段着色器。如果是碎片着色器,则顶点着色器中的一个着色器(该着色器以某种方式提供法线),或者需要碎片着色器组件通过某种机制计算法线。

必须为要渲染的每个模型提供不同的着色器程序。 你可以在不同的
INPUT vec4 modelSpacePosition;
OUTPUT vec4 clipSpacePosition;

uniform mat4 modelToClipMatrix;

void main()
{
  clipSpacePosition = modelToClipMatrix * modelSpacePosition;
}