我的 GLSL 着色器程序链接正常,但是当我尝试使用它时出错——我该如何调试它?

Posted

技术标签:

【中文标题】我的 GLSL 着色器程序链接正常,但是当我尝试使用它时出错——我该如何调试它?【英文标题】:My GLSL shader program links fine, but errors when I try to use it -- how do I debug this? 【发布时间】:2012-01-08 20:13:43 【问题描述】:

如果我的 GLSL 中有语法错误,它会在链接时失败,不是吗?

无论如何,我尝试在 glslDevil 中运行我的程序(以前从未使用过此程序),它只是不断重复 wglGetCurrentContext()。我不确定它是否应该这样做,或者因为它是一个 CLR 应用程序而感到困惑。

有没有更简单的方法来检查我的 GLSL 代码中的语法错误?

这似乎是导致问题的片段着色器......如果需要,这里是代码:

#version 330

const int MAX_POINT_LIGHTS = 2;

in vec2 TexCoord0;
in vec3 Normal0;
in vec3 WorldPos0;

out vec4 FragColor;

struct BaseLight

    vec3 Color;
    float AmbientIntensity;
    float DiffuseIntensity;
;

struct DirectionalLight

    struct BaseLight Base;
    vec3 Direction;
;

struct Attenuation

    float Constant;
    float Linear;
    float Exp;
;

struct PointLight

    struct BaseLight Base;
    vec3 Position;
    Attenuation Atten;
;

uniform int gNumPointLights;
uniform DirectionalLight gDirectionalLight;
uniform PointLight gPointLights[MAX_POINT_LIGHTS];
uniform sampler2D gSampler;
uniform vec3 gEyeWorldPos;
uniform float gMatSpecularIntensity;
uniform float gSpecularPower;

vec4 CalcLightInternal(struct BaseLight Light, vec3 LightDirection, vec3 Normal)

    vec4 AmbientColor = vec4(Light.Color, 1.0f) * Light.AmbientIntensity;
    float DiffuseFactor = dot(Normal, -LightDirection);

    vec4 DiffuseColor  = vec4(0, 0, 0, 0);
    vec4 SpecularColor = vec4(0, 0, 0, 0);

    if (DiffuseFactor > 0) 
        DiffuseColor = vec4(Light.Color, 1.0f) * Light.DiffuseIntensity * DiffuseFactor;

        vec3 VertexToEye = normalize(gEyeWorldPos - WorldPos0);
        vec3 LightReflect = normalize(reflect(LightDirection, Normal));
        float SpecularFactor = dot(VertexToEye, LightReflect);
        SpecularFactor = pow(SpecularFactor, gSpecularPower);
        if (SpecularFactor > 0) 
            SpecularColor = vec4(Light.Color, 1.0f) *
                            gMatSpecularIntensity * SpecularFactor;
        
    

    return (AmbientColor + DiffuseColor + SpecularColor);


vec4 CalcDirectionalLight(vec3 Normal)

    return CalcLightInternal(gDirectionalLight.Base, gDirectionalLight.Direction, Normal);


vec4 CalcPointLight(int Index, vec3 Normal)

    vec3 LightDirection = WorldPos0 - gPointLights[Index].Position;
    float Distance = length(LightDirection);
    LightDirection = normalize(LightDirection);

    vec4 Color = CalcLightInternal(gPointLights[Index].Base, LightDirection, Normal);
    float Attenuation =  gPointLights[Index].Atten.Constant +
                         gPointLights[Index].Atten.Linear * Distance +
                         gPointLights[Index].Atten.Exp * Distance * Distance;

    return Color / Attenuation;


void main()

    vec3 Normal = normalize(Normal0);
    vec4 TotalLight = CalcDirectionalLight(Normal);

    for (int i = 0 ; i < gNumPointLights ; i++) 
        TotalLight += CalcPointLight(i, Normal);
    

    FragColor = texture2D(gSampler, TexCoord0.xy) * TotalLight;


编辑: 具体来说,当我调用glUseProgram 时,我会得到GL_INVALID_OPERATION。文档说:

如果程序不是程序对象,则生成GL_INVALID_OPERATION。

如果程序无法成为其中的一部分,则会生成 GL_INVALID_OPERATION 当前状态。

GL_INVALID_OPERATION 如果 glUseProgram 在 glBegin 的执行和 glEnd 的相应执行。

但我不认为这是第一种或最后一种情况,因为在我调整着色器之前程序运行良好。 “不能成为当前状态的一部分”并没有给我太多继续。

【问题讨论】:

我注意到一些“1f”和“1.0f”(不允许)和“0”。一般来说,总是使用“1.0”和“0.0”格式是最安全的。至于验证你的着色器,我想我已经读过一些人推荐使用 RenderMonkey。如果你运行的是 Windows。编辑:请原谅我,显然“1f”在较新的着色器版本上有效,但为了更好的兼容性(尤其是 = ATI 卡),我认为我认为无论如何最好坚持使用“1.0”。 @harism:好吧,不是 f 杀死了它,而且我一辈子都不知道如何在 RenderMonkey 中打开着色器编辑器。 认为可能是这些结构的定义。在 DirectionalLight 中,我有 struct BaseLight,但这不是声明,而是用法。我从教程中得到了这个,但我觉得这看起来很有趣.... 编辑: 哈!是的。 所以编译 (glGetShaderInfoLog()) 和链接 (glGetProgramInfoLog()) 日志中什么都没有? 【参考方案1】:

我认为一种可能的解释是代码中的 for 循环。

根据我的拙见,应该始终避免使用 for 语句。

考虑到 OpenGL ES 仅支持固定数量的迭代,并且在您的情况下,由于您是基于统一的,因此我会对其进行调查。

此外,考虑到许多驱动程序展开 for 语句以优化生成的代码。

换句话说,我会专注于更简单的着色器版本,如下所示:

if (gNumPointLights == 1) 
TotalLight += CalcPointLight(1, Normal);
 else if (gNumPointLights ==  2) 
TotalLight += CalcPointLight(1, Normal);
TotalLight += CalcPointLight(2, Normal);
 else if (gNumPointLights ==  3) 
TotalLight += CalcPointLight(1, Normal);
TotalLight += CalcPointLight(2, Normal);
TotalLight += CalcPointLight(3, Normal);

显然,代码的样式和效率可以优化,但它是一个快速的起点。

无论如何,实际上,您不会同时拥有数千盏灯 :)

干杯

【讨论】:

实际上,我计划支持数百个......我的计算机可以处理的数量。是为了游戏,会有很多光源。我不确定为什么要手动展开该循环,除非它特别导致错误。特别是如果,如你所说,司机会为我做这件事。它会更难维护。 老实说,我想建议您避免在代码中使用大量灯光,因为根据代码的复杂性,它实际上会影响引擎的性能。考虑到固定线 OpenGL 默认支持 8 个灯,因为在普通 OpenGL 场景中它是非常多的灯。现在,您处于可编程管道中,但概念和所需的计算能力非常相似,我会避免这种方法。 那其他游戏是怎么做的呢?手电筒、灯光、车灯、手电筒、发光的方块或盔甲、施法的魔法……这一切都是伪造的还是什么?无论如何,我都会进行性能测试并找出哪些有效,哪些无效。感谢您的建议。 @Mark:他们使用了一种称为延迟着色的技术。您渲染几何图形以输出材质数据(闪亮、漫反射等)以及法线和世界位置到多个缓冲区,然后将每个灯光“渲染”为与该灯光的体积相对应的几何图形。然后,灯光着色器从先前的缓冲区中读取并渲染颜色输出,并使用加法混合。因此,照明计算仅针对实际可见和为此光源照明的像素进行。使用深度剪刀限制深度空间中受影响的像素,而不仅仅是屏幕空间。 @Macke:感谢您的解释。当我回到这个问题时,我将不得不进一步研究延迟着色。【参考方案2】:

问题是这样的:

struct PointLight

    struct BaseLight Base;
    vec3 Position;
    Attenuation Atten;
;

应该只是BaseLight Base,不是struct

还有这个:

vec4 CalcLightInternal(struct BaseLight Light, vec3 LightDirection, vec3 Normal)

同样的问题。

觉得它看起来很有趣,所以我改变了它,它开始工作了。

【讨论】:

【参考方案3】:

当程序对象链接成功后,调用glUseProgram可以使程序对象成为当前状态的一部分。所以在使用程序之前应该调用程序链接

【讨论】:

【参考方案4】:

就我而言,我在一行的末尾有一个额外的刻度线。胖拇指打字。

 gl_FragColor = vec4(0.6, 0.6, 0.6, 1.0);`

去掉勾号解决了这个问题。

奇怪的是,它一直通过编译,链接并在尝试使用程序时抛出错误。

【讨论】:

以上是关于我的 GLSL 着色器程序链接正常,但是当我尝试使用它时出错——我该如何调试它?的主要内容,如果未能解决你的问题,请参考以下文章

用于片段着色器的 OpenGL GLSL 绑定采样器

片段着色器中设置的颜色未显示 GLSL 1.30

带和不带着色器的 VBO OpenGL C++

OpenGL/GLSL 颜色附件范围

第二篇: WebGL 着色器和GLSL

glsl 着色器 - 颜色混合,正常模式(如在 Photoshop 中)