OpenGL/glsl:特定指令的预处理器问题

OpenGL/glsl : Preprocessor issue with specific directive

我的 glsl 着色器中有一个非常具体的指令有问题。

我想使用这样的指令:

#if defined(numDirectionalLights) && (numDirectionalLights > 0)
struct DirectionalLight
{
vec3 color;
vec3 direction;
};
uniform DirectionalLight u_dirLights[numDirectionalLights];
#endif

当我编译 glsl 程序时,我得到这个错误:

ERROR: 0:6: '' : syntax error: incorrect preprocessor directive
WARNING: 0:6: unexpected tokens following the preprocessor directive - expected a newline

你可以看到这个错误here

这很奇怪,因为在 C 语言中,这种语法是允许的,但更奇怪的是,LibGDX 框架使用这种语法 here 并且与 LibGDX 一起工作!我首先想到的是编码错误,但我的着色器是 UTF-8,这不是问题。

你知道如何使这个语法有效吗?

WebGL 使用 GLSL ES 1.0。它的specification在第3.4节中规定:

#if, #ifdef, #ifndef, #else, #elif, and #endif are defined to operate as for C++ except for the following:

...

• Undefined identifiers not consumed by the defined operator do not default to '0'. Use of such identifiers causes an error.

问题是 numDirectionalLightsdefined 运算符未使用的未定义标识符。在 C++(或 C)中,它的值为 0。在 GLSL ES 中,它的使用是错误的。规范提供了短路评估,但编译器在解析该行时犹豫不决,甚至在评估您的行的 #if defined 部分之前。

要么将你的测试分成两部分:

#if defined(numDirectionalLights) 
#if (numDirectionalLights > 0)

... 或者明确定义 numDirectionalLights0.

基于 LibGDX 为全脂 OpenGL 构建的假设,the spec 中有一个细微的区别:在 ES 下,程序员在一行中使用未定义的标识符是错误的预处理器访问。在全尺寸 OpenGL 中,让预处理器评估包含未定义标识符的 #if 子句只是一个错误。考虑到短路评估,这就是 #if defined(x) && ... 模式在那里起作用的原因。

这是 LibGDX 着色器工作的原因:

LibGDX 前置 #define here.

如果renderable.environment不为空,则添加#define numDirectionalLights XX(可能为0)。

如果 renderable.environment 为 null ,则 #define lightingFlag 不会添加到 shader 并且 #if defined(numDirectionalLights) && (numDirectionalLights > 0) 不会在着色器中调用。效果很好。

所以我们可以看到,在LibGDX着色器中,应该只有#if numDirectionalLights > 0,因为numDirectionalLights必须存在。