View Full Version : Fragment Out Variable Not Binding

Kip Warner
02-13-2010, 09:19 PM
I am binding all of my fragment out variables iteratively for my shader program before linking via...


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


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))

// 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.


Alfonse Reinheart
02-13-2010, 10:04 PM
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.

Kip Warner
02-13-2010, 11:00 PM
What would you suggest? A second framebuffer object that I alternate between?

Alfonse Reinheart
02-13-2010, 11:16 PM
A second framebuffer object that I alternate between?


Kip Warner
02-14-2010, 12:25 AM
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?


Ilian Dinev
02-14-2010, 12:32 AM
How about separate blend funcs ?
Or the layerID thing from a geom. shader (haven't used it yet, but might be useful for you)

Kip Warner
02-14-2010, 12:39 AM
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.


Alfonse Reinheart
02-14-2010, 01:14 AM
How about separate blend funcs ?

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

Ilian Dinev
02-14-2010, 02:13 AM
Yes :(
But now I looked at his code...
"uniform int SourceBufferIndex;"
He just needs to change glDrawBuffers() params >_> of the same FBO; no in-shader athletics required.

Kip Warner
02-14-2010, 11:10 AM
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?