ludic/packages/ludic.render3d/shaders/sky.frag
Orkuncakilkaya f25289db20
Some checks failed
ci / build-and-test (push) Waiting to run
commit-lint / conventional-commits (push) Waiting to run
bootstrap / cfree-fixpoint (push) Has been cancelled
docs / build-and-deploy (push) Successful in 34s
feat(gl): OpenGL 4.1 and the ludic.render3d renderer
`Gl.*` binds the whole OpenGL 4.1 core API — every entry point of the
platform gl3.h with every GL_* constant, generated by `ludic-dev glgen`
with per-call ABI thunks. Windowed builds get an NSOpenGLContext on the
existing window at Retina resolution; headless builds render into an
offscreen CGL context, so a program that uses Gl.* renders and
screenshots identically under the test harness. It links gl.ll, the
thunks and OpenGL.framework only when used; every other build stays
byte-identical.

packages/ludic.render3d is a physically based renderer written on that
surface: HDRI image-based lighting, GPU-generated terrain with scanned
PBR materials, CDLOD, cascaded shadows, glTF with skinning, instanced
vegetation with impostors, procedural grass, water, SSAO, and an HDR
pipeline with bloom, auto-exposure and ACES.

It also carries this session's work on it: the terrain at half its cost
(10.3 -> 5.4 ms of frame), the streaming hitch that got worse the longer
you played, a resize that emptied the world, and the packaging that lets
a game use the renderer from its own repository — `ludic assets`, the
material manifest shipping with the package, and shader lookup falling
back to the install root. See changes/ for each, with its numbers.

The camping game that drove all of it has moved out to its own
repository, Maroon Lake; examples/rendering/smooth.ludic stays as the
renderer's example here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 03:31:12 +03:00

40 lines
1.7 KiB
GLSL

in vec2 v_uv;
out vec4 o_color;
uniform sampler2D u_sky;
uniform mat4 u_inv_vp;
uniform float u_sky_gain;
uniform float u_sky_sat;
// a star: one hash per cell of the direction, a few of them bright, a slow twinkle
float starField(vec3 dir, float t) {
vec3 p = dir * 230.0;
vec3 c = floor(p);
vec3 f = p - c - 0.5;
float h = fract(sin(dot(c, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
float h2 = fract(h * 91.7);
float bright = smoothstep(0.972, 1.0, h);
float disc = smoothstep(0.42, 0.0, length(f));
float twinkle = 0.7 + 0.3 * sin(t * (1.5 + 3.0 * h2) + h2 * 40.0);
return bright * disc * twinkle * (0.5 + h2);
}
void main() {
vec4 a = u_inv_vp * vec4(v_uv * 2.0 - 1.0, 1.0, 1.0);
vec3 dir = normalize(a.xyz / a.w - u_cam_pos);
// level 0: the equirect seam (atan wraps) would otherwise pick the smallest mip along one column
vec3 col = min(textureLod(u_sky, skyUV(dir), 0.0).rgb, vec3(4096.0)) * u_sky_gain;
float l = dot(col, vec3(0.2126, 0.7152, 0.0722));
col = max(mix(vec3(l), col, u_sky_sat), vec3(0.0));
// the photograph's sky dims with the day (daylight.ludic); the night adds its own
col *= u_ibl_scale;
float night = 1.0 - smoothstep(0.0, 0.45, u_daylight);
if (night > 0.0) {
vec3 nightCol = mix(vec3(0.012, 0.016, 0.034), vec3(0.003, 0.004, 0.010), clamp(dir.y, 0.0, 1.0));
float stars = starField(dir, u_time) * smoothstep(-0.02, 0.15, dir.y);
nightCol += stars * vec3(0.55, 0.6, 0.7) * night;
col += nightCol * night;
}
// below the horizon the HDRI ground is replaced by the fog colour
float below = smoothstep(0.0, -0.08, dir.y);
vec3 fogCol = skyPrefiltered(vec3(dir.x, 0.02, dir.z), 0.6);
col = mix(col, fogCol, below);
o_color = vec4(sane(col), 1.0);
}