shader_type spatial;
render_mode unshaded, cull_disabled, depth_draw_never, depth_test_disabled;
// RAYMARCHED VIEW OF THE DENSITY FIELD ITSELF.
//
// Whether the seabed actually has caves is a question about the density
// function, and every instrument so far has answered it through something else:
// the mesher, the lookdev material, the water, the atmosphere. This marches the
// field directly. If there are no overhangs here, there are none anywhere, and
// nothing downstream can be blamed.
//
// After Geiss, GPU Gems 3 ch.1: the field is NOT a signed distance function --
// it is a density with no Lipschitz bound -- so this takes fixed steps rather
// than sphere-tracing, and refines with a bisection once it crosses.
#include "res://shaders/terrain_density.gdshaderinc"
uniform vec3 cam_pos = vec3(0.0, -2000.0, 0.0);
uniform vec3 cam_fwd = vec3(0.0, 0.0, -1.0);
uniform vec3 cam_right = vec3(1.0, 0.0, 0.0);
uniform vec3 cam_up = vec3(0.0, 1.0, 0.0);
uniform float fov_scale = 0.6;
uniform float march_step = 60.0;
uniform int march_steps = 96;
uniform float grad_eps = 30.0;
// ⚠ THE FOOTPRINT IS THE LEVER, not the step count. It is what nyquist_octaves
// turns into an octave count, so marching with a footprint equal to the step
// asks for exactly the detail a step can resolve and drops the rest. The first
// version passed grad_eps (8 m) for every one of ~400 evaluations per pixel and
// got essentially every octave: about 400 million full evaluations a frame at
// 1280x800, which is seconds per frame, not a frame rate.
uniform float march_footprint = 60.0;
uniform float water_y = 700.0;
// 0 = shade by normal, 1 = by depth below the waterline, 2 = flat lambert
uniform int view_mode = 2;
uniform vec3 sun_dir = vec3(0.45, 0.6, 0.35);
float dens_at(vec3 p, float footprint) {
float r = 0.0;
vec2 g = vec2(0.0);
return evaluate_density(p.x, p.y, p.z, footprint, r, g);
}
float dens(vec3 p) { return dens_at(p, march_footprint); }
void vertex() { POSITION = vec4(VERTEX.xy * 2.0, 1.0, 1.0); }
void fragment() {
vec2 uv = SCREEN_UV * 2.0 - 1.0;
uv.x *= VIEWPORT_SIZE.x / VIEWPORT_SIZE.y;
vec3 rd = normalize(cam_fwd + cam_right * uv.x * fov_scale + cam_up * uv.y * fov_scale);
float t = 0.0;
bool hit = false;
float prev = dens(cam_pos);
for (int i = 0; i < march_steps; i++) {
t += march_step;
float d = dens(cam_pos + rd * t);
if (d > 0.0 && prev <= 0.0) {
// Bisect the crossing: a fixed step lands anywhere inside the
// surface and the normal would be sampled off it.
float lo = t - march_step;
float hi = t;
for (int k = 0; k < 6; k++) {
float mid = 0.5 * (lo + hi);
if (dens(cam_pos + rd * mid) > 0.0) { hi = mid; } else { lo = mid; }
}
t = 0.5 * (lo + hi);
hit = true;
break;
}
prev = d;
}
if (!hit) {
ALBEDO = vec3(0.05, 0.09, 0.14);
EMISSION = ALBEDO;
} else {
vec3 p = cam_pos + rd * t;
// SIX taps. The field is volumetric here -- the heightfield identity
// d = H - y does NOT hold below the coastal buffer, so dd/dy cannot be
// assumed constant, which is exactly what makes the colour in game
// stop following the shape.
float e = grad_eps;
vec3 n = normalize(vec3(
dens_at(p + vec3(e,0,0), e) - dens_at(p - vec3(e,0,0), e),
dens_at(p + vec3(0,e,0), e) - dens_at(p - vec3(0,e,0), e),
dens_at(p + vec3(0,0,e), e) - dens_at(p - vec3(0,0,e), e)));
n = -n; // density is positive INSIDE, so the outward normal is -grad
vec3 c;
if (view_mode == 0) {
c = n * 0.5 + 0.5;
} else if (view_mode == 1) {
c = vec3(clamp((water_y - p.y) / 4000.0, 0.0, 1.0));
} else {
float lam = clamp(dot(n, normalize(sun_dir)), 0.0, 1.0);
// Depth tint so overhangs read against the basin behind them.
vec3 base = mix(vec3(0.42, 0.40, 0.34), vec3(0.10, 0.16, 0.20),
clamp((water_y - p.y) / 3000.0, 0.0, 1.0));
c = base * (0.12 + 0.88 * lam);
}
ALBEDO = c;
EMISSION = c;
}
}