t2-mapper/src/file.vert

113 lines
4 KiB
GLSL
Raw Normal View History

#ifdef USE_FOG
// Check runtime fog enabled uniform - allows toggling without shader recompilation
if (fogEnabled) {
// Fog disabled at runtime, skip all fog calculations
} else {
float dist = vFogDepth;
// Discard fragments at or beyond visible distance - matches Torque's behavior
// where objects beyond visibleDistance are not rendered at all.
// This prevents fully-fogged geometry from showing as silhouettes against
// the sky's fog-to-sky gradient.
if (dist >= fogFar) {
discard;
}
// Step 1: Calculate distance-based haze (quadratic falloff)
// Since we discard at fogFar, haze never reaches 1.0 here
float haze = 0.0;
if (dist > fogNear) {
float fogScale = 1.0 / (fogFar - fogNear);
float distFactor = (dist - fogNear) * fogScale - 1.0;
haze = 1.0 - distFactor * distFactor;
}
// Step 2: Calculate fog volume contributions
// Note: Per-volume colors are NOT used in Tribes 2 ($specialFog defaults to false)
// All fog uses the global fogColor - see Tribes2_Fog_System.md for details
float volumeFog = 0.0;
#ifdef USE_VOLUMETRIC_FOG
{
#ifdef USE_FOG_WORLD_POSITION
float fragmentHeight = vFogWorldPosition.y;
#else
float fragmentHeight = cameraHeight;
#endif
float deltaY = fragmentHeight - cameraHeight;
float absDeltaY = abs(deltaY);
// Determine if we're going up (positive) or down (negative)
if (absDeltaY > 0.01) {
// Non-horizontal ray: ray-march through fog volumes
for (int i = 0; i < 3; i++) {
int offset = i * 4;
float volVisDist = allFogVolumes[offset + 0];
float volMinH = allFogVolumes[offset + 1];
float volMaxH = allFogVolumes[offset + 2];
float volPct = allFogVolumes[offset + 3];
// Skip inactive volumes (visibleDistance = 0)
if (volVisDist <= 0.0) continue;
// Calculate fog factor for this volume
// From Torque: factor = (1 / (volumeVisDist * visFactor)) * percentage
// where visFactor is smVisibleDistanceMod (a user quality pref, default 1.0)
// Since we don't have quality settings, we use visFactor = 1.0
float factor = (1.0 / volVisDist) * volPct;
// Find ray intersection with this volume's height range
float rayMinY = min(cameraHeight, fragmentHeight);
float rayMaxY = max(cameraHeight, fragmentHeight);
// Check if ray intersects volume height range
if (rayMinY < volMaxH && rayMaxY > volMinH) {
float intersectMin = max(rayMinY, volMinH);
float intersectMax = min(rayMaxY, volMaxH);
float intersectHeight = intersectMax - intersectMin;
// Calculate distance traveled through this volume using similar triangles:
// subDist / dist = intersectHeight / absDeltaY
float subDist = dist * (intersectHeight / absDeltaY);
// Accumulate fog: fog += subDist * factor
volumeFog += subDist * factor;
}
}
} else {
// Near-horizontal ray: if camera is inside a volume, apply full fog for that volume
for (int i = 0; i < 3; i++) {
int offset = i * 4;
float volVisDist = allFogVolumes[offset + 0];
float volMinH = allFogVolumes[offset + 1];
float volMaxH = allFogVolumes[offset + 2];
float volPct = allFogVolumes[offset + 3];
if (volVisDist <= 0.0) continue;
// If camera is inside this volume, apply fog for full distance
if (cameraHeight >= volMinH && cameraHeight <= volMaxH) {
float factor = (1.0 / volVisDist) * volPct;
volumeFog += dist * factor;
}
}
}
}
#endif
// Step 3: Combine haze and volume fog
// Torque's clamping: if (bandPct + hazePct > 1) hazePct = 1 - bandPct
// This gives fog volumes priority over haze
float volPct = min(volumeFog, 1.0);
float hazePct = haze;
if (volPct + hazePct > 1.0) {
hazePct = 1.0 - volPct;
}
float fogFactor = hazePct + volPct;
// Apply fog using global fogColor (per-volume colors not used in Tribes 2)
gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogFactor);
}
#endif