Nimbin[12]?Games / Entropie / entropie/assets/shaders/scene.frag

Entropie git · main

Entropie festival game; first steps

sdl c game entropie cmake · first commit 2026-10-10 · last commit 2026-11-02 (19 hours ago) · synced 10 hours ago

C 88.5% Markdown 9.1% GLSL 2.3%
git clone https://git.christianimmanuel.de/games/Entropie.gitwget https://git.christianimmanuel.de/games/Entropie/archive/Entropie.tar.gz
entropie/assets/shaders/scene.frag 8.2 KB · 161 lines raw
in vec3  WorldPos;
in vec3  Normal;
in vec3  ViewNormal;
in vec3  Albedo;
in vec4  LightSpacePos;
in float ViewDepth;
in float Wear;
in vec4  Wet;

layout (location = 0) out vec4 OutColor;
layout (location = 1) out vec4 OutNormalDepth; // read by the ink pass

uniform vec3  viewPos;
uniform vec3  lightDirection;
uniform vec3  lightColor;
uniform vec3  shadowColor;
uniform vec3  skyTop;
uniform vec3  skyHorizon;
uniform float shadowStrength;
uniform float boilTime;
uniform float terrainSize;
uniform float decalSize;
uniform float grass;
uniform float rainWet; // 0..1 everything wet from rain: darker, the ground damp
uniform sampler2D shadowMap;
uniform sampler2D waterMap;
uniform sampler2D surfaceMap; // asphalt, concrete, gravel, bare earth (Site_surface)
uniform vec4  holes[8];    // holes in the ground (basements): middle xz, x axis (cos, sin) ...
uniform vec2  holeSize[8]; // ... half sizes
uniform int   holeCount;
uniform vec4  tint;   // rgb: darkens (charred wood), a: glows like embers
uniform float puff;   // > 0: smoke - soft and see-through, this thick (edges fading out)

const vec3  PATH_EARTH  = vec3(0.58, 0.44, 0.28);
const vec3  MUD         = vec3(0.31, 0.23, 0.15);
const vec3  TAR         = vec3(0.27, 0.27, 0.30);
const vec3  CONCRETE    = vec3(0.72, 0.71, 0.68);
const vec3  STONES_DARK = vec3(0.48, 0.46, 0.42);
const vec3  STONES_PALE = vec3(0.76, 0.71, 0.61);

// What the ground is made of, painted: tar with grit and patches, concrete,
// gravel of mixed stones, soil. Part weights (a grassy middle strip, frayed
// edges) become patches with ragged borders, not a blend.
vec3 surfaceColor(vec3 albedo, vec4 s, vec3 p, float patches) {
    float sum = dot(s, vec4(1.0));
    if (sum < 0.004) return albedo;
    float mottle = valueNoise(vec3(p.xz * 0.35, 2.0));
    float grit   = valueNoise(vec3(p.xz * 13.0, 4.0));
    float pebble = valueNoise(vec3(p.xz * 17.0, 5.0)) * 0.6 + valueNoise(vec3(p.xz * 41.0, 6.0)) * 0.4;
    vec3 tar      = TAR * (0.82 + 0.3 * mottle) + (grit - 0.5) * 0.06;
    vec3 concrete = CONCRETE * (0.86 + 0.22 * mottle) + (grit - 0.5) * 0.05;
    vec3 gravel   = mix(STONES_DARK, STONES_PALE, 0.25 + 0.5 * smoothstep(0.25, 0.75, pebble)) * (0.88 + 0.2 * mottle);
    vec3 earth    = PATH_EARTH * (0.78 + 0.32 * mottle) * (0.92 + 0.16 * grit);
    vec3 ground   = (tar * s.r + concrete * s.g + gravel * s.b + earth * s.a) / sum;
    float edge = valueNoise(vec3(p.xz * 2.5, 7.0)) * 0.75 + grit * 0.25;   // ragged, painted borders
    float cover = smoothstep(edge - 0.12, edge + 0.12, sum * 1.25 - 0.1);
    return mix(albedo, ground * (0.9 + 0.2 * patches), cover);
}
const float WATER_SHOWN = 25.0; // mm of standing water at a full texture value (wetness.c)

void main() {
    if (terrainSize > 0.0) {
        for (int i = 0; i < holeCount; i++) {
            vec2 d = WorldPos.xz - holes[i].xy;
            vec2 uv = vec2(dot(d, holes[i].zw), dot(d, vec2(-holes[i].w, holes[i].z)));
            if (all(lessThanEqual(abs(uv), holeSize[i]))) discard;
        }
    }
    vec3 n = normalize(Normal);
    vec3 l = normalize(lightDirection);

    // Glowing colors (flames) are stored 2 higher: no light, no shade
    if (Albedo.r > 1.5) {
        vec3 glow = (Albedo - 2.0) * (0.85 + 0.3 * valueNoise(WorldPos * 6.0 + boilTime * 3.0));
        OutColor       = vec4(applyFog(glow, length(viewPos - WorldPos), skyHorizon), 1.0);
        OutNormalDepth = vec4(normalize(ViewNormal), ViewDepth);
        return;
    }

    vec4 paint  = paintAlbedo(Albedo, WorldPos, boilTime);
    vec3 albedo = paint.rgb * tint.rgb;

    // Meadow wear: grass bends pale, then gives way to a bare earth path
    if (terrainSize > 0.0) {
        float bent = smoothstep(0.08, 0.40, Wear + (paint.w - 0.5) * 0.1);
        float bare = smoothstep(0.45, 0.85, Wear + (paint.w - 0.5) * 0.15);
        albedo = mix(albedo, albedo * vec3(1.12, 1.05, 0.70) + vec3(0.07, 0.05, 0.0), bent);
        albedo = mix(albedo, PATH_EARTH * (0.85 + 0.3 * paint.w), bare);
        vec4 surface = texture(surfaceMap, WorldPos.xz / terrainSize + 0.5);
        albedo = surfaceColor(albedo, surface, WorldPos, paint.w);
        n = normalize(mix(n, vec3(0.0, 1.0, 0.0), 0.8 * min(surface.r + surface.g, 1.0))); // tar and concrete lie smooth
    }

    // Wet ground: damp soil darkens, pee stains, mud, and standing water on top.
    // The cells are blurred and pushed around by noise: ragged, painted edges.
    vec4  wet  = vec4(0.0);
    float edge = 0.0;
    if (terrainSize > 0.0 || (decalSize > 0.0 && n.y > 0.7)) {
        float size = max(terrainSize, decalSize);
        wet = texture(waterMap, WorldPos.xz / size + 0.5);
        if (dot(wet, vec4(1.0)) > 0.0) { // (dry almost everywhere: no noise there)
            vec2 bend = (vec2(valueNoise(vec3(WorldPos.xz * 2.2, 3.1)), valueNoise(vec3(WorldPos.zx * 2.2, 8.7))) - 0.5) * 0.3;
            wet  = texture(waterMap, (WorldPos.xz + bend) / size + 0.5);
            edge = valueNoise(vec3(WorldPos.xz * 7.0, 1.7)) - 0.5;
        }
    } else if (grass > 0.0) {
        wet = Wet;
    }
    float depth  = wet.g * wet.g * WATER_SHOWN;
    vec4  some   = step(vec4(0.004), wet); // dry ground stays exactly as it was
    float puddle = smoothstep(0.9, 1.4, depth + edge * 1.2 * some.g);
    float damp   = smoothstep(0.06, 0.3, wet.r + edge * 0.15 * some.r) * (0.55 + 0.45 * wet.r);
    bool  ground = terrainSize > 0.0 || grass > 0.0 || (decalSize > 0.0 && n.y > 0.7);
    damp = max(damp, rainWet * (ground ? 0.8 : 0.0));
    if (!ground) albedo *= 1.0 - 0.18 * rainWet; // (walls, roofs, people: a little darker wet)
    float mud    = smoothstep(0.15, 0.35, wet.a + edge * 0.25 * some.a) * (0.6 + 0.4 * wet.a);
    float stain  = smoothstep(0.1, 0.4, wet.b + edge * 0.2 * some.b);
    albedo *= mix(1.0, 0.58, damp);
    albedo  = mix(albedo, albedo * vec3(1.25, 1.08, 0.45), stain * 0.6);
    albedo  = mix(albedo, MUD * (0.8 + 0.45 * paint.w) * mix(1.0, 0.75, wet.r), mud);
    albedo *= mix(1.0, 0.55, puddle); // the bottom under the water

    float visible = mix(1.0, shadowVisibility(shadowMap, LightSpacePos, n, l), shadowStrength);
    vec3  color   = paintLight(albedo, n, l, visible, WorldPos, boilTime,
                               lightColor, shadowColor, skyTop, 0.0);
    color += fireGlow(albedo, n, WorldPos);
    if (tint.a > 0.0) { // embers: glowing cracks that flicker
        float crack = valueNoise(WorldPos * 14.0 + boilTime * 0.7);
        vec3 ember = vec3(1.0, 0.32, 0.06) * (0.6 + 0.8 * crack);
        color = mix(color, ember, tint.a * smoothstep(0.25, 0.65, crack + tint.a * 0.3));
    }
    if (puddle > 0.0 || mud > 0.0) {
        // Water mirrors the sky in flat painted tones, with lighter streaks and a
        // darker wet rim; the sun glints. Wet mud shines a little.
        vec3  v      = normalize(viewPos - WorldPos);
        vec2  ripple = vec2(valueNoise(vec3(WorldPos.xz * 3.0, boilTime * 0.6)),
                            valueNoise(vec3(WorldPos.zx * 3.0 + 7.0, boilTime * 0.6))) - 0.5;
        vec3  wn     = normalize(vec3(ripple.x * 0.12, 1.0, ripple.y * 0.12));
        vec3  r      = reflect(-v, wn);
        float fres   = pow(1.0 - max(dot(wn, v), 0.0), 4.0);
        vec3  sky    = mix(skyHorizon, skyTop, smoothstep(0.0, 0.6, r.y));
        float streak = smoothstep(0.62, 0.7, valueNoise(vec3(WorldPos.x * 1.5 + WorldPos.z * 4.0, WorldPos.z * 1.5, boilTime * 0.3)));
        float glint  = smoothstep(0.985, 0.995, dot(r, l)) * visible;
        float mirror = clamp(0.25 + 0.6 * fres + 0.3 * streak, 0.0, 0.9);
        vec3  water  = mix(color * vec3(0.75, 0.85, 0.95), mix(sky, skyHorizon, 0.35) * 0.9, mirror);
        water  = mix(water, water * vec3(1.12, 1.0, 0.55), stain * 0.45); // pee
        water *= 1.0 - 0.35 * smoothstep(0.0, 0.5, puddle) * (1.0 - smoothstep(0.5, 1.0, puddle)); // wet rim
        color  = mix(color, water + lightColor * glint, puddle);
        color += lightColor * glint * mud * wet.r * 0.4;
    }
    color = applyFog(color, length(viewPos - WorldPos), skyHorizon);

    float alpha = 1.0;
    if (puff > 0.0) { // smoke: thickest where it faces you, fading to nothing at the rim
        float facing = abs(normalize(ViewNormal).z);
        alpha = puff * smoothstep(0.05, 0.85, facing);
        color = mix(color, skyHorizon, 0.15);
    }
    OutColor       = vec4(color, alpha);
    OutNormalDepth = vec4(normalize(ViewNormal), ViewDepth);
}