Files
settled-reach/client/shaders/fog.gdshader
T
jpmschweitzerandClaude Opus 4.6 8bf7f6e610 fix(client): entity lerp, fog shader safety, type fixes
Entity renderer: add framerate-independent position lerping so entities
slide between tiles instead of snapping. Tuned for Sprint snappiness
and Walk/Careful fluidity.

Fog shader: set ColorRect to transparent fallback so a shader load
failure doesn't paint solid white over the world. Track camera position
(not player position) so fog stays synced during smooth camera pan.
Restructure GLSL to avoid early return (some GPU drivers miscompile it).

Minor type fixes: typed Array[Vector2] in cursor_renderer tick drawing,
untyped Array in inventory_grid to avoid Godot typed-array cast issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:26:23 +01:00

74 lines
3.6 KiB
Plaintext

shader_type canvas_item;
// D-059: 5-layer fog shader. Composites over world content (layers 0-4).
// Layer 1: Clear (vision cone) — transparent, soft gradient edge
// Layer 2: Light fog (peripheral) — desaturated + dim + animated noise, 8-10s cycle
// Layer 3: Deep fog (explored) — near-monochrome + zone tint + breathing, 15-20s cycle
// Layer 4: Unexplored + maps — wireframe (Sprint 6: deferred, treated as Layer 5)
// Layer 5: Unexplored, no maps — solid near-black #12141a
uniform sampler2D visibility_tex : filter_linear, repeat_disable;
uniform sampler2D exploration_tex : filter_linear, repeat_disable;
uniform sampler2D zone_tint_tex : filter_nearest, repeat_disable;
uniform sampler2D noise_tex : filter_linear, repeat_enable;
uniform vec2 rect_pos; // World-space position of the ColorRect (pixels)
uniform vec2 rect_sz; // World-space size of the ColorRect (pixels)
uniform vec2 map_offset; // map_bounds.position (tiles)
uniform vec2 map_size; // map_bounds.size (tiles)
uniform float tile_size; // Pixels per sim tile
uniform float time; // Seconds since start
// D-059 fog layer colors
const vec3 UNEXPLORED_COLOR = vec3(0.071, 0.078, 0.102); // #12141a
const vec3 DARK_OVERLAY = vec3(0.02, 0.02, 0.05);
// D-059 thresholds (after bilinear filtering)
// Forward tiles = 1.0, Peripheral = 0.706 (180/255), not-visible = 0.0
const float CLEAR_THRESHOLD = 0.85; // Above this: fully clear
const float PERIPHERAL_LOW = 0.55; // Below this: transition to deep/unexplored
void fragment() {
// Map UV (0-1 across ColorRect) to world pixels, then to tile coordinates
vec2 world_px = rect_pos + UV * rect_sz;
vec2 tile = world_px / tile_size;
// Map tile coordinate to texture UV
vec2 tex_uv = (tile - map_offset) / map_size;
// Outside known map → unexplored
if (tex_uv.x < 0.0 || tex_uv.x > 1.0 || tex_uv.y < 0.0 || tex_uv.y > 1.0) {
COLOR = vec4(UNEXPLORED_COLOR, 1.0);
} else {
float vis = texture(visibility_tex, tex_uv).r;
float explored = texture(exploration_tex, tex_uv).r;
if (vis > PERIPHERAL_LOW) {
// In or near vision cone
if (vis > CLEAR_THRESHOLD) {
// Layer 1: Clear — soft edge gradient
float edge = smoothstep(CLEAR_THRESHOLD, 1.0, vis);
COLOR = vec4(0.0, 0.0, 0.0, 1.0 - edge);
} else {
// Layer 2: Light fog (peripheral + forward edge)
// D-059: animated Perlin noise, 8-10s cycle
float noise_val = texture(noise_tex, tile * 0.03 + vec2(time * 0.11, time * 0.07)).r;
float coverage = smoothstep(PERIPHERAL_LOW, CLEAR_THRESHOLD, vis);
// Blend from heavy fog (alpha ~0.55) to lighter fog near clear edge
float alpha = mix(0.55, 0.25, coverage) + noise_val * 0.1;
COLOR = vec4(DARK_OVERLAY, alpha);
}
} else if (explored > 0.3) {
// Layer 3: Deep fog (previously explored, no longer in LOS)
// D-059: near-monochrome, ~10% zone temperature tint, 15-20s breathing cycle
vec3 zone_tint = texture(zone_tint_tex, tex_uv).rgb;
float noise_val = texture(noise_tex, tile * 0.015 + vec2(time * 0.045, time * 0.03)).r;
vec3 tint_color = mix(vec3(0.04), zone_tint, 0.1);
float alpha = mix(0.78, 0.90, noise_val); // Fog breathes
COLOR = vec4(tint_color, alpha);
} else {
// Layer 5: Unexplored, no maps — information zero
COLOR = vec4(UNEXPLORED_COLOR, 1.0);
}
}
}