Quality Gate / Quality Gate (pull_request) Failing after 2m58s
SeabedFloraActor scatters seagrass tufts on flat sand in the lit band and sea fans on rock between 15 and 60 m, deterministically from the site id, one MultiMesh per tile and kind, with procedural alpha-cut meshes and textures (SeabedFloraAssets), vertex sway and wrapped lighting, no collision and no sonar presence. Placement reads the field arrays directly and runs on the worker thread pool. The seabed shader grows turf algae on sunlit rock, coralline crusts in patches and encrusting sponges in the shade, fading out between 40 and 75 m. The water-column optics move to a shared shader include. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
61 lines
2.4 KiB
Plaintext
61 lines
2.4 KiB
Plaintext
shader_type spatial;
|
|
render_mode cull_disabled, depth_draw_opaque;
|
|
|
|
// Seagrass tufts and sea fans as alpha-cut billboards instanced by MultiMesh. The mesh
|
|
// is unit sized (y from 0 to 1); the instance transform gives size and heading, the
|
|
// instance COLOR the species tint, INSTANCE_CUSTOM.x the sway phase and
|
|
// INSTANCE_CUSTOM.y the sway weight (grass bends, fans barely). Daylight follows the
|
|
// same water column as the seabed; the submarine's projectors are not attenuated.
|
|
|
|
#include "res://game/world/sites/water_light.gdshaderinc"
|
|
|
|
uniform sampler2D albedo_map : source_color, filter_linear_mipmap;
|
|
uniform vec3 water_absorption_per_m = vec3(0.11, 0.04, 0.032);
|
|
uniform float reference_depth_m = 24.0;
|
|
uniform float minimum_daylight = 0.05;
|
|
uniform float sway_amplitude = 0.09;
|
|
uniform float sway_speed = 0.8;
|
|
uniform float alpha_threshold = 0.5;
|
|
uniform float translucency = 0.7;
|
|
|
|
varying vec3 world_position;
|
|
varying float daylight;
|
|
varying vec3 chroma;
|
|
|
|
void vertex() {
|
|
float phase = INSTANCE_CUSTOM.x * TAU;
|
|
float weight = INSTANCE_CUSTOM.y * VERTEX.y * VERTEX.y;
|
|
float swing = sin(TIME * sway_speed + phase) + 0.35 * sin(TIME * sway_speed * 2.3 + phase * 1.7);
|
|
VERTEX.x += swing * sway_amplitude * weight;
|
|
VERTEX.z += cos(TIME * sway_speed * 0.7 + phase) * sway_amplitude * 0.5 * weight;
|
|
world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
|
vec3 transmittance = water_transmittance(-world_position.y, water_absorption_per_m, reference_depth_m);
|
|
daylight = water_daylight(transmittance, minimum_daylight);
|
|
chroma = water_chroma(transmittance, water_absorption_per_m, reference_depth_m);
|
|
}
|
|
|
|
void fragment() {
|
|
vec4 sample = texture(albedo_map, UV);
|
|
ALPHA = sample.a;
|
|
ALPHA_SCISSOR_THRESHOLD = alpha_threshold;
|
|
ALBEDO = sample.rgb * COLOR.rgb * chroma;
|
|
if (!FRONT_FACING) {
|
|
NORMAL = -NORMAL;
|
|
}
|
|
AO = daylight;
|
|
AO_LIGHT_AFFECT = 0.35;
|
|
ROUGHNESS = 0.85;
|
|
METALLIC = 0.0;
|
|
}
|
|
|
|
void light() {
|
|
float sun = water_sun_factor(LIGHT_IS_DIRECTIONAL, daylight);
|
|
// Thin blades and fan nets let light through and are lit from both faces: the
|
|
// lambert term is wrapped around the blade, so a vertical blade under an
|
|
// overhead sun is not black.
|
|
float facing = dot(NORMAL, LIGHT);
|
|
float lambert = clamp(facing, 0.0, 1.0);
|
|
float wrapped = clamp(abs(facing) * 0.5 + 0.5, 0.0, 1.0) * translucency;
|
|
DIFFUSE_LIGHT += (lambert + wrapped) * ATTENUATION * LIGHT_COLOR * ALBEDO * sun / PI;
|
|
}
|