Hello,
I'm developing on a laptop with an integrated Radeon X1250 GPU. I'm writing a fragment shader to do bilinear interpolation manually (it's a long story, but absolutely necessary). This involves a fair amount of slow computations and four texture samples per fragment. The shader compiles, the log contains no errors, but the linker fails with this message:
Fragment shader(s) failed to link, vertex shader(s) linked. |
Below is the complete shader. If I remove a single fetch, it links. Likewise, if I use constant texel coordinates for one of the fetches, like so:
c[0] = FetchTexel(vec2(0.0,0.0),fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
It works as well. I've also tried manually inlining the FetchTexel() function to no avail. The program works fine on a desktop Nvidia machine. I'd like this code to run on lower spec hardware so I'm really hoping I'm not bumping into any sort of weird limitations here.
Thank you!
Code:
/*
* fragment.glsl
*
* Fragment shader.
*/
#version 120
// Global uniforms
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec3 polygonState; // .x=not defined, .y=not defined, .z=alpha processing enable
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value
varying float fsLightIntensity; // lighting intensity
/*
* FetchTexel():
*
* Samples a single Real3D texel from the selected texture, taking into
* account wrapping behavior.
*
* Computing normalized OpenGL texture coordinates (0 to 1) within the
* Real3D texture sheet:
*
* If the texture is not mirrored, we simply have to clamp the
* coordinates to fit within the texture dimensions, add the texture
* X, Y position to select the appropriate one, and normalize by 2048
* (the dimensions of the Real3D texture sheet).
*
* = [(u,v)%(w,h)+(x,y)]/(2048,2048)
*
* If mirroring is enabled, textures are mirrored every odd multiple of
* the original texture. To detect whether we are in an odd multiple,
* simply divide the coordinate by the texture dimension and check
* whether the result is odd. Then, clamp the coordinates as before but
* subtract from the dimension to mirror them:
*
* = [M*((w,h)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)
* where M is 1.0 if the texture must be mirrored.
*/
vec4 FetchTexel(vec2 texCoord, vec2 texOffset, vec2 texSize, vec2 mirrorEnable)
{
vec2 clampedCoord, mirror, glTexCoord;
clampedCoord = mod(texCoord.st,texSize); // clamp coordinates to within texture size
mirror = mirrorEnable * mod(floor(texCoord.st/texSize),2.0); // whether this texel needs to be mirrored
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec2(1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
return texture2D(textureMap, glTexCoord);
}
void main(void)
{
vec4 c[4];
vec2 uv[4], r;
if (fsTexParams.x == 0.0)
gl_FragColor = gl_Color; // if textures disabled, use color
else
{
/*
* Bilinear Filtering
*/
// Compute fractional blending factor, r, and lower left corner of texel 0
uv[0] = gl_TexCoord[0].st-vec2(0.5,0.5);
r = uv[0]-floor(uv[0]);
uv[0] = floor(uv[0]);
// Compute texel coordinates
uv[0] = uv[0] + vec2(0.5,0.5); // offset to within center of pixel
uv[1] = uv[0] + vec2(1.0,0.0);
uv[2] = uv[0] + vec2(0.0,1.0);
uv[3] = uv[0] + vec2(1.0,1.0);
// Fetch texels
c[0] = FetchTexel(uv[0],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
c[1] = FetchTexel(uv[1],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
c[2] = FetchTexel(uv[2],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
c[3] = FetchTexel(uv[3],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
gl_FragColor = c[0]*(1-r.s)*(1-r.t) + c[1]*r.s*(1-r.t) + c[2]*(1-r.s)*r.t + c[3]*r.s*r.t;
/*
* T1RGB5:
*
* The transparency bit determines whether to discard pixels (if set).
* What is unknown is how this bit behaves when interpolated. OpenGL
* processes it as an alpha value, so it might concievably be blended
* with neighbors. Here, an arbitrary threshold is chosen.
*/
if (fsTexParams.y >= 0.0)
{
if (gl_FragColor.a > 0.0)
discard;
}
}
// Apply translucency
if (fsTransLevel < 1.0)
gl_FragColor.a = fsTransLevel;
// Apply ambient lighting
gl_FragColor.rgb *= fsLightIntensity;
}