使用 pow() 时的 GLSL 问题

Posted

技术标签:

【中文标题】使用 pow() 时的 GLSL 问题【英文标题】:GLSL Issue when using pow() 【发布时间】:2014-01-23 12:37:58 【问题描述】:

我目前正在实现一个着色器来做一些定向光。我正在关注 Lighthouse 3d (http://www.lighthouse3d.com/tutorials/glsl-core-tutorial/directional-lights-per-pixel/) 上的教程

我遇到的问题在于以下几行:

vec3 h  = normalize(l_dir + e); 
float intSpec  = max(dot(h, n), 0.0);
float specularPower = pow(intSpec, shininess);

如果我将“float specularPower”行留在 - 那么着色器不再“工作”......我在引号中说“工作”,因为我从着色器日志中没有得到任何输出或错误,但是,现在我的全部返回 -1 的统一位置,我无法设置我的纹理位置等。

如果我删除它,着色器的其余部分将按预期工作(但由于缺少高光功率而产生不正确的结果)。

更奇怪的是,如果我连接了 nVidia Nsight 调试器,那么我会在屏幕上看到输出并且它似乎“工作”,但如果我只是在调试模式下使用 VS2012,我在屏幕上什么也没有显示,而且以下错误信息:

First-chance exception at 0x000000005EB066E6 (nvoglv64.dll) in application.exe: 0xC0000005: Access violation reading location 0x0000000000000008.
First-chance exception at 0x000000005EB066E6 (nvoglv64.dll) in application.exe: 0xC0000005: Access violation reading location 0x0000000000000008.

在 GTX 480 和 GTX 560 上都出现了这种行为 - 在运行 Windows 8 64 位的 PC 上。

我知道 -1 作为统一位置返回意味着名称错误,或者编译器已对其进行了优化,但是这对于添加一行没有意义,其结果根本不会被使用然后。当 NSight 调试器附加或不附加时,行为不同就更没有意义了

我可能做错了什么?

编辑:

下面是我可以创建的最简单的顶点/片段着色器,它可以复制问题,同时仍然是一个“真实世界”着色器。

顶点着色器:

// Default Vertex Shader

#version 430 core

uniform mat4 projMatrix;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;

in vec4 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoords;

out vec2 pass_TexCoords;

out vec3 v;

out Data 
    vec3 normal;
    vec4 eye;
 DataOut;

void main(void)

    DataOut.normal = normalize(in_Normal);
    DataOut.eye = (viewMatrix * modelMatrix) * in_Position;

    pass_TexCoords = in_TexCoords;

    v = vec3(in_Position);

    gl_Position = projMatrix * viewMatrix * modelMatrix * in_Position;

片段着色器:

// Default Fragment Shader
#version 430 core

uniform sampler2D material[3];

in vec2 pass_TexCoords;


in Data 
    vec3 normal;
    vec4 eye;
 DataIn;

layout (location = 0) out vec4 out_Colour;

void main(void)

    float shininess         = 0.5;

    vec3 l_dir              = vec3(0.0, 0.0, 1.0);
    vec3 n                  = normalize(DataIn.normal);
    vec3 e                  = normalize(vec3(DataIn.eye));

    float intensity         = max(dot(n, l_dir), 0.0);
    float specularPower;

    if(intensity > 0.0) 
        vec3 h              = normalize(l_dir + e);     

        float dHN           = dot(h, n);
        float intSpec       = max(dHN, 0.0);

        float specularPower = pow(intSpec, shininess);
     else 
        specularPower       = 1.0;
    

    vec4 diffuse_Colour     = texture(material[0], pass_TexCoords);
    out_Colour              = diffuse_Colour * specularPower;

我还检查了程序信息日志,它没有返回任何错误。再一次,使用这些着色器,在通过 VS2012 运行时它会失败(统一返回 -1),但在附加 nVidia Nsight 调试器时会“工作”。

【问题讨论】:

光泽度如何表示?是制服吗? @NathanMonteleone:没关系。 Uniforms 在链接时初始化为零,如果没有设置,specularPower 应该简单地为 1,如果 shininess 是一个uniform,它不能处于非活动状态,除非编译器确定specularPower 本身没有被使用并因此消除完整的表达式。 shininess 是一个浮点数,目前只是硬编码到着色器中为“float shininess = 0.05;” @AndyEsser:你能发布完整的着色器吗? 如果你只将硬编码的值传递给 pow()? 【参考方案1】:

在这个着色器中发生了一些有趣的事情,它们都与这一行有关:

float specularPower = pow(intSpec, shininess);

在最终编译的着色器中绝对不做任何事情。您在函数范围内隐藏了一个同名变量。

这是您的着色器实际执行的操作(如所写):

// Default Fragment Shader
#version 430 core

uniform sampler2D material[3];

in vec2 pass_TexCoords;

in Data 
    vec3 normal;
    vec4 eye;
 DataIn;

layout (location = 0) out vec4 out_Colour;

void main(void)

    vec3 l_dir              = vec3(0.0, 0.0, 1.0);
    vec3 n                  = normalize(DataIn.normal);

    float intensity         = max(dot(n, l_dir), 0.0);
    float specularPower;

    if(intensity > 0.0) 
      // This branch did not affect anything outside of this scope, so it is gone...

      // specularPower is uninitialized if this branch is triggered
     else 
        specularPower       = 1.0;
    

    vec4 diffuse_Colour     = texture(material[0], pass_TexCoords);
    out_Colour              = diffuse_Colour * specularPower;

如果你用这个替换我提到的第一条语句:

specularPower = pow(intSpec, shininess);

您的着色器实际上会在 if (intensity > 0.0) ... 分支中执行某些操作。

由于shininess 在此程序中是一个常量,您也可以像这样替换该语句:

specularPower = sqrt (intSpec);

【讨论】:

+1:有福了死代码消除! :D 仍然非常奇怪的是驱动程序崩溃了。 非常感谢,我开始认为这可能是死代码的问题,尤其是当光泽是统一的时候,事情似乎可以工作。我认为驱动程序“崩溃”的原因是由于对 specularPower 使用了未初始化的值? 实际上,如果它与创建一个与外部作用域同名的变量有关,我不会感到惊讶。很难说确切的原因是什么,但是 GLSL 编译器不像 C 或 C++ 那样防弹;许多愚蠢的事情可以彻底混淆他们。如果编译器输出类似“内部编译器错误......”而不是崩溃,那就太好了:)

以上是关于使用 pow() 时的 GLSL 问题的主要内容,如果未能解决你的问题,请参考以下文章

运行时的 glsl 着色器编译问题

光线行进时的 GLSL 伪影

超过一个字符指针时的GLSL编译错误(glShaderSource)

pow(1, inf) == 南?

OpenGL和GLSL中的glTexParameter和过滤?

使用 cmath 的 fmod (C++) 时的不一致