7
\$\begingroup\$

I've been arming my engine with error checking code. I'll try to describe this situation to my best abilities. Whenever i load a shader and there's an error (file doesn't exist, compilation error, anything that could go wrong) i delete the shader and set the pointer to 0 (can't think of annother way to make sure i don't try to attach or delete trash by accident). I don't want to stop the program if this happens, because somebody might delete or modify a single file by accident in the game folder and don't want to render the whole game unusable because of that.

Now the thing is, is there a default behavior in the situation where i attach shaders to a program that have the pointer equal to 0?

In my program, if i attach a vertex shader equal 0 and a fragment shader equal 0, then OpenGL will use the default program (will display everything white):

enter image description here

This is a rotated cube that's dimensions are in clip space already, rendered without perspective projection or depth checking. It's loaded from an .obj file.

Now, if i attach a proper fragment shader, but the vertex shader will be 0, i get this:

enter image description here

Which is what i would expect, all the fragment shader does is outputs a vec4(1.0, 0.3, 0.7, 1.0), there's no vertex shader so OpenGL uses the default one (is this a correct assumption?).

Now, however, if i attach a correct vertex shader, but i attach a fragment shader equal to 0 (or i won't attach a fragment shader at all) i'll get this mess:

enter image description here

Something you can't see is that this model is contantly being painted different colors, so on my screen it looks like an acid trip with black horizontal lines going through.

I'm confused a bit, because i would expect the cube to just be white. I'd like to point out that my shader code is bare bones, i'm not exchanging any variables between vertex and fragment shaders. Checking if the pointer is not equal to 0 doesn't change anything, the linked program acts the same as if i gave it a pointer equal to 0. The weirdest thing to me is that it's always nicely shaded, it's not just trash.

EDIT: Maybe the default fragment shader expects a gl_ value from the vertex that i'm ommiting to set? That would kind of explain why the surface is nicely shaded, the fragment shader expects a smooth in, but that value is just trash, because i didnt' set it.

Now is this an "undefined behavior" or is this how it's suppoused to work?

\$\endgroup\$
3
  • \$\begingroup\$ I feel like noting that wanting to protect against "someone might delete a file" is not super productive. They're just as likely to delete your .exe, replace a file with a totally incompatible one, or so on. Defaulted shaders, materials, meshes, and so on are useful for debugging and testing, and you absolutely want them, but don't stress too much about getting things working perfectly when required data has been foolishly destroyed by silly end users. \$\endgroup\$ May 9, 2012 at 20:37
  • \$\begingroup\$ @seanmiddleditch That's one way of looking at it. I guess i can move the error checking code to #ifdef _DEBUG? \$\endgroup\$
    – dreta
    May 10, 2012 at 13:28
  • \$\begingroup\$ My point was more not to write tons of fancy self-preservation code, not that such code should only be in debug mode. There's a great 3 part series of articles on the bitsquid.se blog abou handling errors in games, bitsquid.blogspot.com/2012/01/… \$\endgroup\$ May 10, 2012 at 16:51

1 Answer 1

5
\$\begingroup\$

Check section 2.11 of the OpenGL 4.2 specification:

The executable code for an individual shader stage is taken from the current program for that stage. If there is a current program object established by UseProgram, that program is considered current for all stages. Otherwise, if there is a bound program pipeline object (see section 2.11.4), the program bound to the appropriate stage of the pipeline object is considered current. If there is no current program object or bound program pipeline object, no program is current for any stage. The current program for a stage is considered active if it contains executable code for that stage; otherwise, no program is considered active for that stage. If there is no active program for the vertex or fragment shader stages, the results of vertex and/or fragment processing will be undefined. However, this is not an error. If there is no active program for the tessellation control, tessellation evaluation, or geometry shader stages, those stages are ignored.

This is "undefined behavior" that acts differently per implementation.

As for the solution to not being able to find a shader, I've found it's best to hardcode both a vertex and fragment shader and load them before anything else, then use it in place of any missing shaders. That way a lot of the uniforms I define (transformation matrices) will still go through and the result will look half-decent. For example, here's how I do it (C#):

string defaultVertShaderText = 
    @"#version 120

    attribute vec4 in_vertex;

    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;

    void main()
    {
        gl_Position = projection * view * model * in_vertex;
    }";

string defaultFragShaderText =
    @"#version 120

    void main()
    {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }";

Also hardcoding a default texture is a good idea.

\$\endgroup\$
7
  • \$\begingroup\$ Thanks for the answer. And i was thinking about the same way to deal with empty shaders, just that i do it on a program manager level (if program's wrong i use a default program), so all i have to do is also use my default shaders on the program compilation stage and it'll work, yeah. Thanks again. \$\endgroup\$
    – dreta
    May 9, 2012 at 7:36
  • \$\begingroup\$ @Robert: -1: Please do not pass arrays of strings like this to OpenGL. glShaderSource does take arrays, but this is not how that functionality is meant to be used. Each string is not meant to be a separate line. \$\endgroup\$ Aug 7, 2013 at 23:17
  • \$\begingroup\$ @NicolBolas I mentioned that this is how I'm doing it in my game in C#. OpenTK provides an overloads of regular GL calls to interact with C# types in the way that you'd naturally expect. As far as I've tested (ATI, AMD, NVIDIA, Intel, Apple, and Mesa between Windows, Linux, and OS X), the shaders compile, link, and operate properly and without error. I guess if you prefer, I could remove "here's how I do it (C#)" and replace it with the C sytnax for the same thing. \$\endgroup\$ Aug 8, 2013 at 0:58
  • \$\begingroup\$ @RobertRouhani: This has nothing to do with the "C Syntax". It's about the fact that you're building an array. It should be a single string, not an array of strings (or an array of one string). Yes, it does "compile, link, and operate properly and without error". But it's still the wrong way to use the fact that glShaderSource takes an array of strings. \$\endgroup\$ Aug 8, 2013 at 1:11
  • 2
    \$\begingroup\$ @RobertRouhani: It's wrong because that's not what the array is for. It takes an array of strings so that you can effectively build a compiled shader from pieces that reference each other. And so that you can put together header prefixes for shaders, so that they can work with different versions by using #ifdef. It's not there so that you can send each line as a separate string. Yes, it works, but it's horrible coding style. It's like building a .cpp file by #includeing each line individually. Yes, it'll compile, but that's not how you use #include. \$\endgroup\$ Aug 8, 2013 at 1:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .