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>
No files matched your search
|
Before Width: | Height: | Size: 547 KiB After Width: | Height: | Size: 446 KiB |
|
Before Width: | Height: | Size: 599 KiB After Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 610 KiB After Width: | Height: | Size: 420 KiB |
|
Before Width: | Height: | Size: 507 KiB After Width: | Height: | Size: 419 KiB |
|
Before Width: | Height: | Size: 449 KiB After Width: | Height: | Size: 410 KiB |
|
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 261 KiB |
@@ -257,6 +257,14 @@ de contacts existant les sépare du fond.
|
|||||||
plus haute et plus forte (62°, 1,35) avec ombres douces à 70 % d'opacité,
|
plus haute et plus forte (62°, 1,35) avec ombres douces à 70 % d'opacité,
|
||||||
SSAO pour l'occlusion à l'échelle du maillage, tonemap filmique, perspective
|
SSAO pour l'occlusion à l'échelle du maillage, tonemap filmique, perspective
|
||||||
aérienne et albédo du brouillard volumétrique teintés.
|
aérienne et albédo du brouillard volumétrique teintés.
|
||||||
|
- Lumière par profondeur (même jour) : le shader atténue la lumière du jour
|
||||||
|
par Beer-Lambert spectral (absorption par canal 0,11 / 0,04 / 0,032 par
|
||||||
|
mètre, normalisée à 24 m) ; la partie scalaire réduit le soleil (fonction
|
||||||
|
`light()`, directionnelle seulement) et l'ambiance ciel (`AO`), la partie
|
||||||
|
chromatique teinte l'albédo. Les projecteurs du sous-marin ne sont pas
|
||||||
|
atténués. Plancher à 5 % pour que le pied du tombant reste lisible. Palette
|
||||||
|
GDP §18.2 obtenue de fait : vert-bleu à 24 m, bleu froid à 45 m, indigo au
|
||||||
|
pied du tombant ; ciel, brouillard et soleil (0,9, ombres 45 %) refroidis.
|
||||||
|
|
||||||
**État du 2026-10-06 (demande d'Olivier)** : le shader mélange sable et roche
|
**État du 2026-10-06 (demande d'Olivier)** : le shader mélange sable et roche
|
||||||
par la pente à partir de 20° (sable sous 20°, roche au-dessus de 40°, fondu
|
par la pente à partir de 20° (sable sous 20°, roche au-dessus de 40°, fondu
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
[ext_resource type="Script" path="res://game/world/sites/bathymetric_terrain_actor.gd" id="2_terrain"]
|
[ext_resource type="Script" path="res://game/world/sites/bathymetric_terrain_actor.gd" id="2_terrain"]
|
||||||
|
|
||||||
[sub_resource type="ProceduralSkyMaterial" id="SkyUnderwater"]
|
[sub_resource type="ProceduralSkyMaterial" id="SkyUnderwater"]
|
||||||
sky_top_color = Color(0.3, 0.62, 0.64, 1)
|
sky_top_color = Color(0.22, 0.52, 0.66, 1)
|
||||||
sky_horizon_color = Color(0.13, 0.36, 0.41, 1)
|
sky_horizon_color = Color(0.09, 0.28, 0.4, 1)
|
||||||
sky_curve = 0.2
|
sky_curve = 0.2
|
||||||
sky_energy_multiplier = 1.0
|
sky_energy_multiplier = 1.0
|
||||||
ground_horizon_color = Color(0.11, 0.3, 0.35, 1)
|
ground_horizon_color = Color(0.05, 0.17, 0.27, 1)
|
||||||
ground_bottom_color = Color(0.015, 0.05, 0.07, 1)
|
ground_bottom_color = Color(0.01, 0.03, 0.07, 1)
|
||||||
ground_curve = 0.1
|
ground_curve = 0.1
|
||||||
ground_energy_multiplier = 0.6
|
ground_energy_multiplier = 0.6
|
||||||
sun_angle_max = 40.0
|
sun_angle_max = 40.0
|
||||||
@@ -26,7 +26,7 @@ background_mode = 2
|
|||||||
sky = SubResource("SkyResource")
|
sky = SubResource("SkyResource")
|
||||||
ambient_light_source = 3
|
ambient_light_source = 3
|
||||||
ambient_light_sky_contribution = 1.0
|
ambient_light_sky_contribution = 1.0
|
||||||
ambient_light_energy = 1.0
|
ambient_light_energy = 1.3
|
||||||
reflected_light_source = 1
|
reflected_light_source = 1
|
||||||
tonemap_mode = 2
|
tonemap_mode = 2
|
||||||
tonemap_exposure = 1.15
|
tonemap_exposure = 1.15
|
||||||
@@ -48,8 +48,8 @@ glow_intensity = 0.8
|
|||||||
glow_blend_mode = 0
|
glow_blend_mode = 0
|
||||||
glow_hdr_threshold = 1.2
|
glow_hdr_threshold = 1.2
|
||||||
fog_enabled = true
|
fog_enabled = true
|
||||||
fog_light_color = Color(0.1, 0.3, 0.35, 1)
|
fog_light_color = Color(0.05, 0.18, 0.29, 1)
|
||||||
fog_light_energy = 0.52
|
fog_light_energy = 0.38
|
||||||
fog_sun_scatter = 0.15
|
fog_sun_scatter = 0.15
|
||||||
fog_density = 0.006
|
fog_density = 0.006
|
||||||
fog_aerial_perspective = 0.5
|
fog_aerial_perspective = 0.5
|
||||||
@@ -57,7 +57,7 @@ fog_height = -4.0
|
|||||||
fog_height_density = 0.025
|
fog_height_density = 0.025
|
||||||
volumetric_fog_enabled = true
|
volumetric_fog_enabled = true
|
||||||
volumetric_fog_density = 0.01
|
volumetric_fog_density = 0.01
|
||||||
volumetric_fog_albedo = Color(0.6, 0.85, 0.9, 1)
|
volumetric_fog_albedo = Color(0.5, 0.78, 0.92, 1)
|
||||||
volumetric_fog_anisotropy = 0.45
|
volumetric_fog_anisotropy = 0.45
|
||||||
volumetric_fog_length = 72.0
|
volumetric_fog_length = 72.0
|
||||||
volumetric_fog_ambient_inject = 0.5
|
volumetric_fog_ambient_inject = 0.5
|
||||||
@@ -73,11 +73,11 @@ environment = SubResource("EnvironmentUnderwater")
|
|||||||
|
|
||||||
[node name="AmbientDirection" type="DirectionalLight3D" parent="."]
|
[node name="AmbientDirection" type="DirectionalLight3D" parent="."]
|
||||||
rotation_degrees = Vector3(-62, -28, 0)
|
rotation_degrees = Vector3(-62, -28, 0)
|
||||||
light_color = Color(0.56, 0.8, 0.82, 1)
|
light_color = Color(0.5, 0.78, 0.88, 1)
|
||||||
light_energy = 1.35
|
light_energy = 0.9
|
||||||
light_angular_distance = 3.0
|
light_angular_distance = 3.0
|
||||||
shadow_enabled = true
|
shadow_enabled = true
|
||||||
shadow_opacity = 0.7
|
shadow_opacity = 0.45
|
||||||
shadow_blur = 2.0
|
shadow_blur = 2.0
|
||||||
directional_shadow_max_distance = 160.0
|
directional_shadow_max_distance = 160.0
|
||||||
directional_shadow_fade_start = 0.75
|
directional_shadow_fade_start = 0.75
|
||||||
|
|||||||
@@ -7,14 +7,23 @@ render_mode depth_draw_opaque;
|
|||||||
// The mesh has no tangents: perturbed normals are built in world space and written
|
// 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
|
// 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.
|
// 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 sand_color : source_color = vec3(0.56, 0.52, 0.42);
|
||||||
uniform vec3 rock_color : source_color = vec3(0.21, 0.20, 0.18);
|
uniform vec3 rock_color : source_color = vec3(0.22, 0.21, 0.19);
|
||||||
uniform vec3 deep_tint : source_color = vec3(0.04, 0.09, 0.15);
|
|
||||||
uniform float sand_slope_cosine = 0.9397; // cos(20 deg)
|
uniform float sand_slope_cosine = 0.9397; // cos(20 deg)
|
||||||
uniform float rock_slope_cosine = 0.7660; // cos(40 deg)
|
uniform float rock_slope_cosine = 0.7660; // cos(40 deg)
|
||||||
uniform float depth_tint_start_m = 20.0;
|
// Per-channel absorption of clear tropical water, per metre, scaled for readability.
|
||||||
uniform float depth_tint_end_m = 90.0;
|
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 sand_normal_map : hint_normal, repeat_enable, filter_linear_mipmap;
|
||||||
uniform sampler2D rock_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_position;
|
||||||
varying vec3 world_normal;
|
varying vec3 world_normal;
|
||||||
|
varying float daylight;
|
||||||
|
|
||||||
void vertex() {
|
void vertex() {
|
||||||
world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
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));
|
vec3 shading_normal = normalize(mix(sand_normal, rock_normal, rockness));
|
||||||
NORMAL = normalize((VIEW_MATRIX * vec4(shading_normal, 0.0)).xyz);
|
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 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 sand_albedo = sand_color * (1.0 + (grain - 0.5) * 0.3);
|
||||||
vec3 albedo = mix(sand_albedo, rock_albedo, rockness);
|
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 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);
|
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;
|
AO_LIGHT_AFFECT = 0.35;
|
||||||
ROUGHNESS = mix(0.95, 0.78 + 0.12 * grain, rockness);
|
ROUGHNESS = mix(0.95, 0.78 + 0.12 * grain, rockness);
|
||||||
METALLIC = 0.0;
|
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;
|
||||||
|
}
|
||||||