mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
feat: lit particle shader for nebula dust weather effect
Replace NebulaDust's flat unshaded glow material with a custom ShaderMaterial: a fake per-pixel puff normal on the billboarded quad feeds a light() override so motes catch the directional light and a nebula-core color bias, alpha gets a depth-texture soft-particle fade so motes no longer hard-clip through boundary/decoration geometry, and a per-particle hash adds subtle sparkle.
This commit is contained in:
+10
-12
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=15 format=3]
|
||||
[gd_scene load_steps=16 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/arena.gd" id="1_iywne"]
|
||||
[ext_resource type="PackedScene" path="res://objects/arena_boundary.tscn" id="2_bndry"]
|
||||
@@ -8,6 +8,7 @@
|
||||
[ext_resource type="PackedScene" path="res://assets/models/nebula_debris.glb" id="9_debris"]
|
||||
[ext_resource type="Texture2D" path="res://assets/textures/particle_glow.png" id="10_dust"]
|
||||
[ext_resource type="PackedScene" path="res://assets/models/nebula_planet.glb" id="11_planet"]
|
||||
[ext_resource type="Shader" path="res://shaders/nebula_dust.gdshader" id="12_dust_shader"]
|
||||
|
||||
[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_nebula"]
|
||||
panorama = ExtResource("7_nebula")
|
||||
@@ -39,19 +40,16 @@ adjustment_brightness = 1.0
|
||||
adjustment_contrast = 1.05
|
||||
adjustment_saturation = 1.08
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_dust"]
|
||||
transparency = 1
|
||||
shading_mode = 0
|
||||
billboard_mode = 1
|
||||
vertex_color_use_as_albedo = true
|
||||
albedo_color = Color(1, 0.75, 0.9, 1)
|
||||
albedo_texture = ExtResource("10_dust")
|
||||
emission_enabled = true
|
||||
emission = Color(1, 0.6, 0.85, 1)
|
||||
emission_energy_multiplier = 2.5
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_dust"]
|
||||
shader = ExtResource("12_dust_shader")
|
||||
shader_parameter/albedo_tex = ExtResource("10_dust")
|
||||
shader_parameter/core_bias_color = Color(1, 0.6, 0.85, 1)
|
||||
shader_parameter/core_bias_amount = 0.35
|
||||
shader_parameter/emission_strength = 2.5
|
||||
shader_parameter/soft_fade_distance = 1.0
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_dust"]
|
||||
material = SubResource("StandardMaterial3D_dust")
|
||||
material = SubResource("ShaderMaterial_dust")
|
||||
size = Vector2(0.4, 0.4)
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_dust"]
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
shader_type spatial;
|
||||
render_mode blend_mix, depth_draw_never, cull_disabled, specular_disabled;
|
||||
|
||||
uniform sampler2D albedo_tex : source_color;
|
||||
uniform sampler2D depth_tex : hint_depth_texture, filter_linear_mipmap;
|
||||
uniform vec3 core_bias_color : source_color = vec3(1.0, 0.6, 0.85);
|
||||
uniform float core_bias_amount : hint_range(0.0, 1.0) = 0.35;
|
||||
uniform float emission_strength : hint_range(0.0, 5.0) = 2.5;
|
||||
uniform float soft_fade_distance : hint_range(0.0, 5.0) = 1.0;
|
||||
|
||||
varying float v_flicker;
|
||||
|
||||
void vertex() {
|
||||
// true billboard: strip rotation from the per-instance modelview, keep translation + scale
|
||||
mat4 mv = MODELVIEW_MATRIX;
|
||||
mv[0].xyz = vec3(length(mv[0].xyz), 0.0, 0.0);
|
||||
mv[1].xyz = vec3(0.0, length(mv[1].xyz), 0.0);
|
||||
mv[2].xyz = vec3(0.0, 0.0, length(mv[2].xyz));
|
||||
VERTEX = (mv * vec4(VERTEX, 1.0)).xyz;
|
||||
|
||||
// INSTANCE_CUSTOM.x is GPUParticles3D's per-particle random seed, baked at spawn
|
||||
float seed = INSTANCE_CUSTOM.x;
|
||||
float flicker_hash = fract(sin((seed + floor(TIME * 6.0)) * 127.1) * 43758.5453);
|
||||
v_flicker = 0.85 + 0.15 * flicker_hash;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// fake a puff/sphere normal from local UV so a camera-facing billboard can still catch directional light;
|
||||
// the quad is billboarded to face the camera in vertex(), so this normal is already view-space aligned
|
||||
vec2 centered = UV * 2.0 - 1.0;
|
||||
float r2 = dot(centered, centered);
|
||||
float mask = clamp(1.0 - r2, 0.0, 1.0);
|
||||
NORMAL = normalize(vec3(centered, sqrt(max(mask, 0.001))));
|
||||
|
||||
vec4 tex = texture(albedo_tex, UV);
|
||||
ALBEDO = tex.rgb * mix(vec3(1.0), core_bias_color, core_bias_amount);
|
||||
EMISSION = core_bias_color * tex.rgb * emission_strength * 0.15;
|
||||
ALPHA = tex.a * COLOR.a * mask;
|
||||
|
||||
float raw_depth = texture(depth_tex, SCREEN_UV).r;
|
||||
vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, raw_depth);
|
||||
vec4 view_pos = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
|
||||
view_pos.xyz /= view_pos.w;
|
||||
float scene_depth = -view_pos.z;
|
||||
float particle_depth = -VERTEX.z;
|
||||
ALPHA *= clamp((scene_depth - particle_depth) / soft_fade_distance, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void light() {
|
||||
float ndotl = clamp(dot(NORMAL, LIGHT), 0.0, 1.0);
|
||||
DIFFUSE_LIGHT += LIGHT_COLOR * ndotl * ALBEDO * emission_strength * v_flicker * ATTENUATION;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ A subagent ran the game and critiqued arena_02 head-on against Rocket League 2 i
|
||||
- [x] **Richer nebula sky + planet surface textures** (extends the existing procedural generation — no new asset types). `sky_nebula.png` is essentially one noise-filter pass over a bright core; `planet_surface.png` is a flat gradient with a single vortex swirl and no bands, craters, or day/night terminator. Done: `tools/textures/gen_nebula_sky.py` gained two more dust-lane layers at different scales plus subtle patchy hue variation within the bright core; `tools/textures/gen_planet_surface.py` is a new generator (no prior script existed, despite the note below) producing latitude bands, 3 storm vortices, and a lit/unlit terminator baked from the sphere's exact UV convention (reverse-engineered from `nebula_planet.glb`'s vertex data) dotted against `arena_02.tscn`'s actual `DirectionalLight3D` direction — verified in-engine via godot-mcp screenshots showing a clean crescent terminator; both `Game/assets/textures/planet_surface.png` and the live-feeding sidecar `Game/assets/models/nebula_planet_planet_surface.png` were regenerated and reimported.
|
||||
Prompt: "Extend the nebula/planet texture generators (see `tools/textures/` once committed, per the star-sprite TODO item above) with more detail layers: for the sky, add a second/third dust-lane layer at a different scale plus subtler color variation within the bright core (real nebulae aren't one flat color); for the planet, add a proper lit/unlit terminator gradient (the side facing the arena's directional light should read brighter), more band variation at different latitudes, and a couple more storm-vortex features so it doesn't read as a single gradient with one twist. Regenerate both textures, reimport, and screenshot-verify."
|
||||
|
||||
- [ ] **Particle lighting response for the nebula dust ("weather") effect** (no models — particle material/shader only). The `NebulaDust` `GPUParticles3D` motes are generic soft glow sprites with no lighting interaction, no depth-based fade, and no secondary motion (no sparkle/color shift).
|
||||
- [x] **Particle lighting response for the nebula dust ("weather") effect** (no models — particle material/shader only). `NebulaDust`'s `StandardMaterial3D` was replaced with a new `ShaderMaterial` (`Game/shaders/nebula_dust.gdshader`, the project's first particle/spatial shader): a fake per-pixel puff normal is derived from local UV on the true-billboarded quad and fed into a `light()` override (`LIGHT`/`LIGHT_COLOR`/`ATTENUATION`) so motes visibly catch the arena's directional light and pick up a fixed nebula-core color bias; alpha is multiplied by a depth-texture-based soft-particle fade so motes no longer hard-clip through boundary/decoration geometry; a per-particle flicker (hashed from `INSTANCE_CUSTOM.x` + `TIME`) adds subtle sparkle. Verified via godot-mcp screenshots from multiple angles (visible lit/unlit shading variation, smooth fade near geometry, per-mote sparkle) and zero-error headless runs of `free_play`/`match`/`spectate`/`training`.
|
||||
Prompt: "In `arena_02.tscn`'s `NebulaDust` particle system, replace the current unshaded glow-sprite material with a custom shader that fakes lighting response — note the sprites are camera-facing billboards, so they have no fixed world-space normal and true per-pixel PBR lighting won't behave like it would on a solid mesh. A practical approach: derive a fake per-pixel normal from the quad's local UV (as if each sprite were a small sphere/puff, same trick used for 2D lit particle effects) and light that against the directional light + a fixed 'glow color' bias toward the nebula core direction, rather than relying on the mesh's real (camera-facing) normal. Add soft-particle depth fade (compare particle depth to the depth buffer) so motes don't hard-clip through the boundary/decoration geometry, and add subtle per-particle brightness flicker via the color ramp or shader for sparkle. Verify visually via godot-mcp screenshots that the dust reads as lit rather than flat-glowing, from a couple of different camera angles (billboard lighting tricks can look wrong from some angles even when right from others)."
|
||||
|
||||
- [x] **Ship + ball visual pass**: `Game/objects/ship.tscn`'s 5 primitives replaced with a Blender-greebled hull/nose/canopy/tailfin/twin nacelles (source `Game/assets/blender_models/ship.blend`, generator `tools/blender/gen_ship.py`); ball fully remodeled as a smooth round icosphere with a crossed emissive accent pattern (`ball.blend`, `gen_ball.py`) rather than just a material tweak, replacing the old flat-shaded `gold_ball`. `Nose`/`TailFin` node names preserved for `_apply_team_color()`; `CollisionShape3D`/`RigidBody3D` physics on both ship and ball untouched (verified). Each part's mesh is extracted to a standalone `.res` (`tools/blender/extract_meshes.gd`) rather than referenced via `glb::ArrayMesh_xxx`, which doesn't reliably resolve across scene files. Verified via in-game screenshots (both team colors, orientation) and headless runs of `free_play`/`training`/`match`.
|
||||
|
||||
Reference in New Issue
Block a user