Entropie git · main
Entropie festival game; first steps
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.gzentropie/assets/shaders/scene.frag 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);
}