Files
CosmicClash/Game/shaders/goal_net.gdshader
T
Josh Creek 66b6215fdd feat(goal): recess goals into the hull behind a netted pocket
The goal was a single flat translucent slab. Because the chase camera sits
behind the goal line at kickoff it rendered as a large blue rectangle across
the lower screen, and the hull beside the mouth was translucent field panel,
so the pocket behind it showed straight through the wall and the net appeared
a second time alongside the frame.

The boundary now carries an opaque bulkhead around each mouth, which occludes
properly. On top of that the goal becomes a pocket sunk into the wall: a dark
machined bezel lining the opening, a thin team-coloured emissive rim flush
with the wall face, and netting from a single box viewed inside-out, giving a
five-sided pocket instead of a flat panel across the back.

The net is cut with discard rather than alpha blending so it still writes
depth and sorts against the frame and the containment field like solid
geometry; a blended net would join the transparent queue and sort per object
against the boundary shell, which is the artefact this whole pass removed.

goal.tscn drops from 18 node/sub-resource blocks to 3 — it is now just the
Area3D, its CollisionShape3D and the script. All geometry is built in code
from two loops, and the mouth is measured off the collision shape rather than
restated, so the frame cannot drift from the volume that actually scores.

The sensor, its collision shape and the goal_scored signal are unchanged, and
visuals are skipped under --headless.
2026-08-04 13:36:07 +01:00

56 lines
1.9 KiB
Plaintext

// Netting for the goal pocket.
//
// Applied to a box viewed from the inside (cull_front), so one mesh gives a
// five-sided pocket of netting rather than a flat panel across the back. The
// strand pattern is driven from model-space position rather than UV so spacing
// stays physical and continuous across all five faces — a box's UV layout
// would stretch it differently on each.
//
// Cut with discard rather than alpha blending on purpose: an alpha-scissor net
// still writes depth, so it sorts against the pocket, the frame and the
// arena's containment field like ordinary solid geometry. A blended net would
// join the transparent queue and sort per-object against the boundary shell,
// which is the class of artefact this whole pass removed.
shader_type spatial;
render_mode cull_front, diffuse_burley;
uniform vec3 net_color : source_color = vec3(0.65, 0.78, 1.0);
uniform float cell_size = 0.13;
uniform float strand_width = 0.011;
uniform float emission_strength : hint_range(0.0, 4.0) = 0.35;
varying vec3 v_local;
varying vec3 v_local_normal;
void vertex() {
v_local = VERTEX;
v_local_normal = NORMAL;
}
// Distance to the nearest strand centre line of a square lattice.
float strand(vec2 p) {
vec2 d = abs(fract(p / cell_size) - 0.5) * cell_size;
return min(d.x, d.y);
}
void fragment() {
vec3 n = abs(normalize(v_local_normal));
// Project onto whichever plane this face lies in.
float d = n.x > max(n.y, n.z)
? strand(v_local.zy)
: (n.y > n.z ? strand(v_local.xz) : strand(v_local.xy));
if (d > strand_width) {
discard;
}
// Only back faces survive cull_front, and their normals point out of the
// pocket rather than into it, so flip them or the netting lights from
// behind and reads flat black.
if (!FRONT_FACING) {
NORMAL = -NORMAL;
}
ALBEDO = net_color * 0.25;
EMISSION = net_color * emission_strength;
ROUGHNESS = 0.65;
METALLIC = 0.1;
}