`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>
32 lines
1.4 KiB
GLSL
32 lines
1.4 KiB
GLSL
// Second generation pass: copy the height into R and bake the B-spline surface normal into
|
|
// GBA, once, at texel resolution. terrain.frag used to differentiate the bicubic height
|
|
// per pixel — four bicubic reads, sixteen taps — for a quantity that never changes.
|
|
in vec2 v_uv;
|
|
out vec4 o;
|
|
uniform sampler2D u_src;
|
|
uniform float u_half;
|
|
float heightSmooth(sampler2D tex, vec2 uv) {
|
|
vec2 res = vec2(textureSize(tex, 0));
|
|
vec2 t = uv * res - 0.5;
|
|
vec2 f = fract(t);
|
|
vec2 i = floor(t);
|
|
vec2 w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0;
|
|
vec2 w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0;
|
|
vec2 w3 = f * f * f / 6.0;
|
|
vec2 w2 = 1.0 - w0 - w1 - w3;
|
|
vec2 s0 = w0 + w1, s1 = w2 + w3;
|
|
vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res;
|
|
vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res;
|
|
return (texture(tex, vec2(o0.x, o0.y)).r * s0.x + texture(tex, vec2(o1.x, o0.y)).r * s1.x) * s0.y
|
|
+ (texture(tex, vec2(o0.x, o1.y)).r * s0.x + texture(tex, vec2(o1.x, o1.y)).r * s1.x) * s1.y;
|
|
}
|
|
void main() {
|
|
float step = 1.0 / float(textureSize(u_src, 0).x);
|
|
float world = step * 2.0 * u_half;
|
|
float hl = heightSmooth(u_src, v_uv - vec2(step, 0));
|
|
float hr = heightSmooth(u_src, v_uv + vec2(step, 0));
|
|
float hd = heightSmooth(u_src, v_uv - vec2(0, step));
|
|
float hu = heightSmooth(u_src, v_uv + vec2(0, step));
|
|
vec3 n = normalize(vec3(hl - hr, 2.0 * world, hd - hu));
|
|
o = vec4(texture(u_src, v_uv).r, n);
|
|
}
|