Fragment Out Variable Not Binding

I am binding all of my fragment out variables iteratively for my shader program before linking via…


glBindFragDataLocation()

To test to make sure they were bound correctly, after linking, I then call iteratively…


glGetFragDataLocation

If it returns -1, then we know the fragment out variable wasn’t bound.

My problem is that neither ZMapOut[0] or ZMapOut[1] out variable reports having bound successfully, based on the above method.

I reasoned that either this is a bug in the driver (unlikely), the driver compiler optimized out those variables because it determined they weren’t used (but my shader appears to write to them unconditionally), or lastly, glGetFragDataLocation might be unreliable in certain circumstances.

Vertex shader:


// We want at least GLSL 1.50...
#version 150

// Input variables...

    // Grid location...
    in vec2         GridLocation;

// Uniform variables...

    // Dimensions...
    uniform uint    Width;
    uniform uint    Height;

// Variables for the fragment shader, none of which need be interpolated...

    // Normalized location cooresponding to grid location...
    out vec2        NormalizedLocation;
    
    // Texture location cooresponding to grid location...
    out vec2        TextureLocation;

// Entry point...
void main()
{
    // Calculate texture location which has components in [0, 1] range...
    TextureLocation = GridLocation / vec2(Width, Height); 

    // Calculate normalized location which has components in [-1, 1] range...
    NormalizedLocation = TextureLocation * vec2(2.0, 2.0) - vec2(1.0, 1.0);

    // Generate a fragment...
    gl_Position = vec4(NormalizedLocation, 0.0, 1.0);
}

Fragment shader:


// Preprocessor directives...

    // Select language version...
    #version 150
    
    // Useful for debugging...
    #pragma optimize(off)
    #pragma debug(on)

// Input variables...

    // Normalized location cooresponding to grid location...
    in vec2             NormalizedLocation;
    
    // Texture location cooresponding to grid location...
    in vec2             TextureLocation;

// Uniform variables...

    // Dimensions...
    uniform uint        Width;
    uniform uint        Height;

    // Source buffer index...
    uniform int         SourceBufferIndex;

    // Grid spacing between vertices...
    uniform float       DistanceBetweenVertices;
    
    // Pre-computed equation constants...
    uniform float       CachedConstant1;
    uniform float       CachedConstant2;
    uniform float       CachedConstant3;
    
    // Z-displacement maps...
    uniform sampler2D   ZMapIn[2];

// Output variables...

    // Z-displacement output buffers...
    out float           ZMapOut[2];
    
    // Normal...
    out vec3            Normal;
    
    // Tangent...
    out vec3            Tangent;

// Take a grid location and transform to a normalized texture coordinate...
vec2 GridToTexture(const uvec2 Location)
{
    // Transform to [0..1] and return it...
    return (Location / vec2(Width, Height));
}

// Take coordinate normalized in [-1..1] and transform to [0..Width or Height]...
uvec2 NormalizedPointToGrid(const vec2 Normalized)
{
    // Transform and return it... p = (d/2)(p'+1)
    return uvec2((vec2(Width, Height) * vec2(0.5, 0.5)) * (Normalized + vec2(1.0, 1.0)));
}

// Transform coordinate in [-1..1] to [0..1]
vec2 NormalizedPointToTexture(const vec2 Normalized)
{
    // Transform normalized form to texture coordinate... p_t = (p' + <1,1>) / 2
    return (Normalized + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
}

// Entry point...
void main()
{
    // Get location on grid...
    uvec2 GridLocation = NormalizedPointToGrid(NormalizedLocation);
    
        // We assume the outer edge of fluid surface is fixed. This also makes
        //  querying neighbours easier since we can assume there always will be
        //  exactly eight all around it after this, though we don't affect the
        //  diagonal neighbours...

        if(GridLocation.x == 0u || GridLocation.x == (Width - 1u) ||
           GridLocation.y == 0u || GridLocation.y == (Height - 1u))
            discard;

    // The previous buffer's index...
    int PreviousBufferIndex = int(!bool(SourceBufferIndex));

    // Lookup displacement of current location...
    float CurrentZ      = texture(ZMapIn[SourceBufferIndex], TextureLocation).r;
    
    // Lookup displacement current location's neighbours...
    float AboveCurrentZ = texture(ZMapIn[SourceBufferIndex], GridToTexture(GridLocation + uvec2( 0,  1))).r;
    float BelowCurrentZ = texture(ZMapIn[SourceBufferIndex], GridToTexture(GridLocation + uvec2( 0, -1))).r;
    float LeftCurrentZ  = texture(ZMapIn[SourceBufferIndex], GridToTexture(GridLocation + uvec2(-1,  0))).r;
    float RightCurrentZ = texture(ZMapIn[SourceBufferIndex], GridToTexture(GridLocation + uvec2( 1,  0))).r;
    
    // Lookup displacement of last passes displacement at current location...
    float PreviousZ     = texture(ZMapIn[PreviousBufferIndex], TextureLocation).r;

    // Update previous buffer's displacement here using equation 12.25, p.335, 
    //  of Mathematics for 3D Game Programming and Computer Graphics. */
    PreviousZ = CachedConstant1 * CurrentZ +                        /* first term */
                CachedConstant2 * PreviousZ +                       /* second term */
                CachedConstant3 * (RightCurrentZ + LeftCurrentZ +   /* third term */
                                   AboveCurrentZ + BelowCurrentZ);

    // Write out displacement to destination buffer...
    ZMapOut[PreviousBufferIndex] = PreviousZ;
}

I posted the vertex shader as well, just in case the problem originated in there somewhere.

Kip

I think you’re confusing OpenGL by attempting to do something that’s impossible: conditionally write to one of two buffers.

A fragment shader will write something to all output variables, whether you do so explicitly in the shader or not.

I get the impression that you’re trying to ping-pong between rendering to multiple buffers without changing programs or other kinds of state. Clever, but it won’t work.

What would you suggest? A second framebuffer object that I alternate between?

A second framebuffer object that I alternate between?

Yes.

Another possibility would be writing to both ZMaps. The one I wish to change, I write as such, and the other, I write what was read from it via the sampler2D?

Kip

How about separate blend funcs ?
Or the layerID thing from a geom. shader (haven’t used it yet, but might be useful for you)

Separate blend functions might work. I’ll have to do some more reading on them. I don’t have any experience with the geometry shader, and if I can avoid having to write another one, I would be well.

Kip

How about separate blend funcs ?

Wouldn’t that require ARB_draw_buffers_blend? That extension is only available on ATI hardware.

Yes :frowning:
But now I looked at his code…
“uniform int SourceBufferIndex;”
XD
He just needs to change glDrawBuffers() params >_> of the same FBO; no in-shader athletics required.

Yes, using glDrawBuffers() each frame would work as well. But can you do that to a framebuffer object that’s already been initialized and bound?