xrjamfeb2025/scenes/particle_test.gdshader

66 lines
2.9 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This shader is used with a cloned GPUParticles3D system that is rendered in an offscreen SubViewport
// (sized 8×2 pixels) on a dedicated layer (e.g. layer 20). Its purpose is to check each particles full 3D
// world-space position against up to eight static target areas. Each target area is defined by a center (vec3),
// extents (vec3, representing half-sizes), and a precomputed inverse basis (mat3) that converts world space into
// the targets local space. If a particle is within a target, the shader maps its geometry to a quad occupying the
// horizontal slice corresponding to that target (i.e. one pixel column in the 8×1 output). The fragment outputs red
// with a configurable alpha value. With additive blending, the red intensity in each slice roughly indicates the number
// of particles hitting that target.
//
// Targets with zero extents are considered disabled.
shader_type spatial;
render_mode unshaded, depth_draw_never, cull_disabled, blend_add;
const int MAX_TARGETS = 8;
uniform vec3 target_center[MAX_TARGETS]; // 3D centers for each target.
uniform vec3 target_extents[MAX_TARGETS]; // 3D half-sizes for each target.
uniform mat3 target_inv_basis[MAX_TARGETS]; // Precomputed inverse bases (inverts the target's rotation).
void vertex() {
// Compute the particle's 3D world-space center (assumes local origin is the center).
vec3 world_pos = (MODEL_MATRIX * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
int hit_target = -1;
// Loop through targets.
for (int i = 0; i < MAX_TARGETS; i++) {
// Skip disabled targets.
if (target_extents[i] == vec3(0.0))
continue;
// Transform the world position into the target's local space.
vec3 local_pos = target_inv_basis[i] * (world_pos - target_center[i]);
// Check if within the target's extents on all three axes.
if (abs(local_pos.x) <= target_extents[i].x &&
abs(local_pos.y) <= target_extents[i].y &&
abs(local_pos.z) <= target_extents[i].z) {
hit_target = i;
break;
}
}
if (hit_target == -1) {
// Particle is not inside any target: send its geometry offscreen.
POSITION = vec4(2.0, 2.0, 2.0, 1.0);
} else {
// Map the target index to a horizontal slice of clip space.
float slice_width = 2.0 / float(MAX_TARGETS);
float x_min = -1.0 + slice_width * float(hit_target);
float x_max = x_min + slice_width;
// Output a quad covering the full vertical range.
if (VERTEX_ID == 0) {
POSITION = vec4(x_min, -1.0, 0.0, 1.0);
} else if (VERTEX_ID == 1) {
POSITION = vec4(x_max, -1.0, 0.0, 1.0);
} else if (VERTEX_ID == 2) {
POSITION = vec4(x_min, 1.0, 0.0, 1.0);
} else {
POSITION = vec4(x_max, 1.0, 0.0, 1.0);
}
}
}
void fragment() {
ALBEDO = vec3(1.0, 0.0, 0.0);
ALPHA = 0.01f;
}