GLSL/C++:自定义着色器的默认行为

Posted

技术标签:

【中文标题】GLSL/C++:自定义着色器的默认行为【英文标题】:GLSL/C++: Default behavior from custom shader 【发布时间】:2011-11-12 00:09:54 【问题描述】:

我有一些 GLSL 着色器正在正确编译,并成功地附加到正确链接的程序。我已经确认其他着色器在我的程序中正常工作。奇怪的是,我看到的结果似乎与我从默认着色器(颜色,无光照)中得到的行为相同。

我将粘贴顶点和片段着色器的源代码,以及我的 C++ 源代码中与着色器相关的所有摘录。我将在 cmets 中解释我的程序独有的任何函数或类。

我的顶点着色器(包含在“dirlight.vs”中)

uniform int lightcount;
uniform vec4 lightdirs[];
uniform vec4 lightdifs[];
uniform vec4 lightambs[];
uniform vec4 lightposs[];

void main()

    vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
    vec4 diffuse = vec4(0.0, 0.0, 0.0, 0.0);
    for(int i = 0; i < lightcount; i++)
    
        //Adding the diffuse term multiplied by gl_Color for each light.
        //There should be no color (0,0,0,0) if the lights are null
        diffuse += lightdifs[i] * max(dot(normal, normalize(lightdirs[i])), 0.0) * gl_Color;
    

    gl_FrontColor = diffuse;
    gl_Position = ftransform();

我的片段着色器(包含在“dirlight.fs”中)

void main()

    gl_FragColor = gl_Color;

摘自 C++ main 中的初始化...

//class program manages shaders
Program shaders = Program();
//attach a vertex shader, compiled from source in dirlight.vs
shaders.addShaderFile(GL_VERTEX_SHADER, "dirlight.vs");
//attach a fragment shader compiled from source in dirlight.fs
shaders.addShaderFile(GL_FRAGMENT_SHADER, "dirlight.fs");
//link program
shaders.link();
//use program
shaders.use();

//Program::getUniformLoc(const char* name) grabs the location
//of the uniform specified
GLint sTime = shaders.getUniformLoc("time");
GLint lightcount = shaders.getUniformLoc("lightcount");
GLint lightdir = shaders.getUniformLoc("lightdirs");
GLint lightdif = shaders.getUniformLoc("lightdifs");
GLint lightamb = shaders.getUniformLoc("lightambs");

glUniform1i(lightcount, 2);
GLfloat lightdirs[] = -1.f, 1.f, 1.f, 1.f,
                       1.f, 1.f, -1.f, 1.f;
glUniform4fv(lightdir, 2, lightdirs);
GLfloat lightdifs[] = 1.f, 1.f, 1.f, 1.f,
                       1.f, 1.f, 1.f, 1.f;
glUniform4fv(lightdif, 2, lightdifs);
glUniform4f(lightamb, 0.4f, 0.4f, 0.4f, 1.f);

摘自 C++ main 中的主循环...

glUniform1f(sTime, newTime);
//This should cause the light directions to rotate around the origin
GLfloat lightdirs[] = sinf(newTime * 2.f), 1.f, cosf(newTime * 2.0f), 1.f,
                       cosf(newTime * 2.f), 1.f, sinf(newTime * 2.0f), 1.f;
glUniform4fv(lightdir, 2, lightdirs);

【问题讨论】:

我明白了,您正在创建程序对象您创建并使其成为当前 OpenGL 渲染上下文,并在其后初始化扩展,也是吗?到目前为止 Programm::addShaderFile 是不透明的,有源代码,而 Program::getUniformLoc 可能会有所帮助。 下次你问GLSL问题时,指定OpenGL版本和GLSL版本。您没有提供着色器编译/加载例程的源代码,因此根据墨菲定律,我将假设根本没有错误检查。您应该使用相应的 API 函数 - glGetProgramiv 和 glGetShaderiv 检查着色器编译状态和程序链接状态。在链接/编译后,还要提取和读取着色器和程序的信息日志。出于调试目的,您可以在每次 OpenGL 调用后添加 glGetError - 是否与着色器相关。 正如我的问题所述,我已经通过加载其他自定义着色器确认加载着色器的方式存在零问题。问题要么在于着色器本身,要么在于我处理制服/属性的方式。 【参考方案1】:
uniform vec4 lightdirs[];

这不是合法的 GLSL。我无法解释为什么你的编译器允许编译(我猜它是一个 NVIDIA 编译器,对吗?那当然假设你正在检查编译/链接状态),但是统一数组 必须在着色器中明确定义的长度。如果你想拥有可变长度的数组,你必须选择一个最大长度并将其烘焙到着色器中。着色器可以读取更少的值(长度基于您传入的制服),但仅此而已。

【讨论】:

谢谢!设置最大长度有效!真棒。奇怪的是它没有给我任何编译错误。不知道是不是 NVIDIA 编译器,但我有一个 NVIDIA 显卡,如果是这样编译的... @MilesRufat-Latre 是的,GLSL 编译器是图形驱动程序的一部分。 既然您使用的是 NVidia,请查看NVEmulate。其中,它允许您将 GLSL 编译器设置为“严格”,以便它停止接受 CUDA 构造。那里还有其他有趣的功能。

以上是关于GLSL/C++:自定义着色器的默认行为的主要内容,如果未能解决你的问题,请参考以下文章

unity能改默认的shader吗

使用像素着色器的JavaFX自定义效果

将属性传递给 OpenGL 顶点着色器的行为很奇怪

自定义着色器不接收光线

如何在片段着色器中进行自定义模板测试

传递给片段着色器的纹理坐标全部为 0