feat(world): depth-attenuated daylight and cold-water palette for the seabed
The seabed shader applies per-channel Beer-Lambert transmittance relative to a 24 m reference depth: the scalar part dims the directional light in a custom light() function and the sky ambient through AO, the chroma part tints the albedo, and artificial lights are left untouched for the submarine's projectors. Sky, fog and sun are cooled and the sun softened so the plateau reads green-blue, the canyon floor cold blue and the drop-off foot near-indigo. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
26a7dfc4f7
commit
34b39a8dbc
9 files changed
+58
-23
No files matched your search
@@ -7,14 +7,23 @@ render_mode depth_draw_opaque;
|
||||
// The mesh has no tangents: perturbed normals are built in world space and written
|
||||
// to NORMAL in view space. Detail shading comes from the normal maps, a grain map
|
||||
// used as ambient occlusion in the pits, and albedo mottling.
|
||||
//
|
||||
// Daylight is attenuated spectrally with depth (Beer-Lambert, per channel): red is
|
||||
// gone first, blue last, and the whole scene darkens by exp(-k * depth). The scalar
|
||||
// part scales the sun (light() function, directional only) and the sky ambient (AO);
|
||||
// the chroma part tints the albedo. Artificial lights such as the submarine's
|
||||
// projectors are not attenuated, so they stay useful on the drop-off. Everything is
|
||||
// normalised at reference_depth_m, where the look is the authored palette.
|
||||
|
||||
uniform vec3 sand_color : source_color = vec3(0.50, 0.46, 0.34);
|
||||
uniform vec3 rock_color : source_color = vec3(0.21, 0.20, 0.18);
|
||||
uniform vec3 deep_tint : source_color = vec3(0.04, 0.09, 0.15);
|
||||
uniform vec3 sand_color : source_color = vec3(0.56, 0.52, 0.42);
|
||||
uniform vec3 rock_color : source_color = vec3(0.22, 0.21, 0.19);
|
||||
uniform float sand_slope_cosine = 0.9397; // cos(20 deg)
|
||||
uniform float rock_slope_cosine = 0.7660; // cos(40 deg)
|
||||
uniform float depth_tint_start_m = 20.0;
|
||||
uniform float depth_tint_end_m = 90.0;
|
||||
// Per-channel absorption of clear tropical water, per metre, scaled for readability.
|
||||
uniform vec3 water_absorption_per_m = vec3(0.11, 0.04, 0.032);
|
||||
uniform float reference_depth_m = 24.0;
|
||||
// Floor on the daylight factor so the drop-off foot stays readable rather than black.
|
||||
uniform float minimum_daylight = 0.05;
|
||||
|
||||
uniform sampler2D sand_normal_map : hint_normal, repeat_enable, filter_linear_mipmap;
|
||||
uniform sampler2D rock_normal_map : hint_normal, repeat_enable, filter_linear_mipmap;
|
||||
@@ -31,6 +40,7 @@ uniform vec2 ripple_direction = vec2(0.8, 0.6);
|
||||
|
||||
varying vec3 world_position;
|
||||
varying vec3 world_normal;
|
||||
varying float daylight;
|
||||
|
||||
void vertex() {
|
||||
world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
@@ -92,20 +102,37 @@ void fragment() {
|
||||
vec3 shading_normal = normalize(mix(sand_normal, rock_normal, rockness));
|
||||
NORMAL = normalize((VIEW_MATRIX * vec4(shading_normal, 0.0)).xyz);
|
||||
|
||||
// Albedo: mottled rock, lightly grained sand, both tinted with depth.
|
||||
// Albedo: mottled rock, lightly grained sand.
|
||||
vec3 rock_albedo = rock_color * (1.0 + (grain - 0.5) * 2.0 * grain_albedo_contrast);
|
||||
vec3 sand_albedo = sand_color * (1.0 + (grain - 0.5) * 0.3);
|
||||
vec3 albedo = mix(sand_albedo, rock_albedo, rockness);
|
||||
float depth_m = -world_position.y;
|
||||
float tint = smoothstep(depth_tint_start_m, depth_tint_end_m, depth_m);
|
||||
albedo = mix(albedo, deep_tint, tint * 0.6);
|
||||
ALBEDO = albedo;
|
||||
|
||||
// Detail shading: pits of the grain map and ripple troughs read as occlusion.
|
||||
// Water column: spectral transmittance relative to the reference depth.
|
||||
float depth_m = -world_position.y;
|
||||
vec3 transmittance = exp(-water_absorption_per_m * (depth_m - reference_depth_m));
|
||||
float brightness = dot(transmittance, vec3(0.2126, 0.7152, 0.0722));
|
||||
vec3 chroma = transmittance / max(brightness, 0.001);
|
||||
daylight = max(brightness, minimum_daylight);
|
||||
// Normalise so the authored palette holds at the reference depth once the
|
||||
// reference transmittance (red already filtered by 24 m of water) is applied.
|
||||
vec3 reference_chroma = exp(-water_absorption_per_m * reference_depth_m);
|
||||
reference_chroma /= dot(reference_chroma, vec3(0.2126, 0.7152, 0.0722));
|
||||
ALBEDO = albedo * chroma * reference_chroma;
|
||||
|
||||
// Detail shading: pits of the grain map and ripple troughs read as occlusion;
|
||||
// the daylight factor dims the sky ambient with depth.
|
||||
float rock_ao = mix(1.0, 0.45 + 0.55 * grain, grain_ao_strength);
|
||||
float sand_ao = mix(1.0, 0.8 + 0.2 * clamp(sand_tangent.z, 0.0, 1.0), grain_ao_strength * 0.6);
|
||||
AO = mix(sand_ao, rock_ao, rockness);
|
||||
AO = mix(sand_ao, rock_ao, rockness) * daylight;
|
||||
AO_LIGHT_AFFECT = 0.35;
|
||||
ROUGHNESS = mix(0.95, 0.78 + 0.12 * grain, rockness);
|
||||
METALLIC = 0.0;
|
||||
}
|
||||
|
||||
// Lambert diffuse; the sun (directional) is dimmed by the water column, other lights
|
||||
// (submarine projectors) are not. Sand and rock are rough enough to skip specular.
|
||||
void light() {
|
||||
float sun_factor = LIGHT_IS_DIRECTIONAL ? daylight : 1.0;
|
||||
float lambert = clamp(dot(NORMAL, LIGHT), 0.0, 1.0);
|
||||
DIFFUSE_LIGHT += lambert * ATTENUATION * LIGHT_COLOR * ALBEDO * sun_factor / PI;
|
||||
}
|
||||
Reference in new issue
Block a user