I am writing a renderer for an old game. Lately I came across some very odd behavior in glsl.
#ifdef BINDLESSTEXTURES
layout(std140) uniform TextureHandles
{
sampler2D Textures[NUMTEXTURES];
};
#endif
uniform sampler2D Texture0; //Base Texture
uniform sampler2D Texture1; //DetailTexture
layout(std140) uniform DistanceFogParams
{
vec4 DistanceFogColor; // Fog color
vec4 DistanceFogValues;
};
uniform float AlphaThreshold;
uniform vec4 DrawColor;
uniform uint PolyFlags;
uniform bool bHitTesting;
uniform float Gamma;
uniform bool DrawGouraudFog;//VertexFog
// Texture UV's
uniform vec2 DrawGouraudTexUV;
uniform vec3 DrawGouraudDetailTexUV;
in vec3 vCoords;
in vec4 vEyeSpacePos;
in vec4 vLightColor;
in vec4 vFogColor; //VertexFog
in vec2 vTexCoords;
flat in uint vTexNum[2];
out vec4 FragColor;
float getFogFactor(vec4 Values, float FogCoord)
{
// DistanceFogValues.x = FogStart
// DistanceFogValues.y = FogEnd
// DistanceFogValues.z = FogDensity
// DistanceFogValues.w = FogMode
float FogResult = 0.0;
if(Values.w == 0.0 && (Values.y > Values.x) )
FogResult = (Values.y-FogCoord)/(Values.y-Values.x);
else if(Values.w == 1.0 && (Values.z > 0.0 ))
FogResult = exp(-Values.z*FogCoord);
else if(Values.w == 2.0 && (Values.z > 0.0 ))
FogResult = exp(-pow(Values.z*FogCoord, 2.0));
else FogResult = 1.0;
FogResult = 1.0-clamp(FogResult, 0.0, 1.0);
return FogResult;
}
vec3 rgb2hsv(vec3 c)
{
// some nice stuff from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main(void)
{
vec4 TotalColor;
vec4 Color;
#ifdef BINDLESSTEXTURES
if (vTexNum[0] > 0u)
Color = texture(Textures[vTexNum[0]], (vTexCoords*DrawGouraudTexUV));
else Color = texture(Texture0, (vTexCoords*DrawGouraudTexUV));
#else
Color = texture(Texture0, (vTexCoords*DrawGouraudTexUV));
#endif
// Handle PF_Masked.
if ( (PolyFlags&PF_Masked) == PF_Masked )
{
if(Color.a < 0.5)
discard;
else Color.rgb /= Color.a;
}
else if ( (PolyFlags&PF_AlphaBlend) == PF_AlphaBlend && Color.a < 0.01 )
discard;
// Handle PF_RenderFog.
if ((PolyFlags&PF_RenderFog) == PF_RenderFog)
{
// Handle PF_RenderFog|PF_Modulated.
if ( (PolyFlags&PF_Modulated)== PF_Modulated )
{
// Compute delta to modulation identity.
vec3 Delta = vec3(0.5) - Color.xyz;
// Reduce delta by (squared) fog intensity.
//Delta *= 1.0 - sqrt(vFogColor.a);
Delta *= 1.0 - vFogColor.a;
Delta *= vec3(1.0) - vFogColor.rgb;
TotalColor = vec4(vec3(0.5)-Delta,Color.a);
}
// Normal.
else
{
Color*=vLightColor;
//TotalColor=mix(Color, vec4(vFogColor.xyz,1.0), vFogColor.w);
TotalColor.rgb = Color.rgb * (1.0-vFogColor.a) + vFogColor.rgb;
TotalColor.a = Color.a;
}
}
// No Fog.
else if((PolyFlags & PF_Modulated) == PF_Modulated)
{
TotalColor = Color;
}
else
{
TotalColor = Color * vec4(vLightColor.rgb,1.0);
}
float bNear = clamp(1.0-(vCoords.z/380.0),0.0,1.0);
if(DrawGouraudDetailTexUV.z > 0.0 && bNear > 0.0)
{
vec4 DetailTexColor;
#ifdef BINDLESSTEXTURES
if (vTexNum[1] > 0u)
Color = texture(Textures[vTexNum[1]], (vTexCoords*DrawGouraudDetailTexUV.xy));
else
Color = texture(Texture1, (vTexCoords*DrawGouraudDetailTexUV.xy)); // DetailTexture
#else
Color = texture(Texture1, (vTexCoords*DrawGouraudDetailTexUV.xy)); // DetailTexture
#endif
vec3 hsvDetailTex = rgb2hsv(DetailTexColor.rgb);
hsvDetailTex.b += (DetailTexColor.r - 0.1);
hsvDetailTex = hsv2rgb(hsvDetailTex);
DetailTexColor=vec4(hsvDetailTex,0.0);
DetailTexColor = mix(vec4(1.0,1.0,1.0,1.0), DetailTexColor, bNear); //fading out.
TotalColor*=DetailTexColor;
}
// Add DistanceFog
if ((DistanceFogValues.w == 0.0 && (DistanceFogValues.y > DistanceFogValues.x)) || (DistanceFogValues.w > 0.0 && (DistanceFogValues.z > 0.0 )))
{
vec4 FogColor = DistanceFogColor;
if ( (PolyFlags&PF_Modulated) == PF_Modulated )
FogColor = vec4(0.5,0.5,0.5,0.0);
else if ( (PolyFlags&PF_Translucent) == PF_Translucent && (PolyFlags&PF_Environment) != PF_Environment)
FogColor = vec4(0.0,0.0,0.0,0.0);
float FogCoord = abs(vEyeSpacePos.z/vEyeSpacePos.w);
TotalColor = mix(TotalColor, FogColor, getFogFactor(DistanceFogValues, FogCoord));
}
if((PolyFlags & PF_Modulated)!=PF_Modulated)
{
// Gamma
#ifdef GL_ES
// 1.055*pow(x,(1.0 / 2.4) ) - 0.055
// FixMe: ugly rough srgb to linear conversion.
TotalColor.r=(1.055*pow(TotalColor.r,(1.0-Gamma / 2.4))-0.055);
TotalColor.g=(1.055*pow(TotalColor.g,(1.0-Gamma / 2.4))-0.055);
TotalColor.b=(1.055*pow(TotalColor.b,(1.0-Gamma / 2.4))-0.055);
#else
TotalColor.r=pow(TotalColor.r,2.7-Gamma*1.7);
TotalColor.g=pow(TotalColor.g,2.7-Gamma*1.7);
TotalColor.b=pow(TotalColor.b,2.7-Gamma*1.7);
#endif
}
#ifdef EDITOR
// Editor support.
if ( (PolyFlags&PF_Selected) == PF_Selected )
{
TotalColor.r = (TotalColor.r*0.125);
TotalColor.g = (TotalColor.g*0.125);
TotalColor.b = (TotalColor.b*0.125);
TotalColor = clamp(TotalColor,0.0,1.0);
if(TotalColor.a < 0.5)
TotalColor.a = 0.51;
}
// HitSelection, Zoneview etc.
if (bHitTesting)
TotalColor = DrawColor; // Use DrawColor.
#endif
// Texture.Alpha support.
if ( (PolyFlags&PF_AlphaBlend) == PF_AlphaBlend && DrawColor.a > 0.0 )
TotalColor.a *= DrawColor.a;
FragColor = TotalColor;
}
In the section “// Add DistanceFog” there is a line being executed although it never should be called:
TotalColor = mix(TotalColor, FogColor, getFogFactor(DistanceFogValues, FogCoord));
It doesn’t matter what I do, it causes weird visual distortions. I tried many different variants, even arbitrary values for the check like
if (DistanceFogValues.w == 13248.9876)
are not changing this behavior.
It only stops if I make something like
if (0>0)
or if I am commenting this line, so I am absolutely certain it is the cause.
However, the function itself IS working perfectly fine if being really in some fog zone and other values than 0 are passed. Currently I am updating via UBO, before I used glUniforms for that.
The problem occurs only in Windows, not on the same machine in Linux and not on an AMD based graphics card.
It’s a GTX960 card, but also happens on another machine with a GTX970. Driver in Windows is currently 382.33, in Linux it’s currently 375.66. No idea if it matters, it’s Windows 10Pro, 64bit.
Seems to happen with both > 3.3 Core profile or 3.0 ES profile.
If you require more information, please let me know.