Added trail effect

This commit is contained in:
2026-02-21 10:21:21 +01:00
parent 13f99ac7ae
commit 2bfa8ba9a1
4 changed files with 54 additions and 22 deletions

View File

@@ -38,7 +38,8 @@ export class PendulumComponent {
m2: 1.0, m2: 1.0,
l1: 1.5, l1: 1.5,
l2: 1.2, l2: 1.2,
damping: 0.999 // Less damping for longer swinging damping: 0.999,
trailDecay: 0.98
}; };
onSceneReady(event: SceneReadyEvent) { onSceneReady(event: SceneReadyEvent) {
@@ -55,8 +56,8 @@ export class PendulumComponent {
const stateBuffer = new StorageBuffer(engine, 4 * 4); const stateBuffer = new StorageBuffer(engine, 4 * 4);
stateBuffer.update(new Float32Array([Math.PI / 4, Math.PI / 2, 0, 0])); // Initial angles stateBuffer.update(new Float32Array([Math.PI / 4, Math.PI / 2, 0, 0])); // Initial angles
const paramsBuffer = new StorageBuffer(engine, 10 * 4); const paramsBuffer = new StorageBuffer(engine, 12 * 4);
const paramsData = new Float32Array(10); const paramsData = new Float32Array(12);
// --- 2. SHADERS --- // --- 2. SHADERS ---
const csPhysics = new ComputeShader("physics", engine, const csPhysics = new ComputeShader("physics", engine,
@@ -100,6 +101,8 @@ export class PendulumComponent {
paramsData[7] = this.simParams.l1; paramsData[7] = this.simParams.l1;
paramsData[8] = this.simParams.l2; paramsData[8] = this.simParams.l2;
paramsData[9] = this.simParams.damping; paramsData[9] = this.simParams.damping;
paramsData[10] = this.simParams.trailDecay;
paramsData[11] = 0; // Pad
paramsBuffer.update(paramsData); paramsBuffer.update(paramsData);

View File

@@ -23,7 +23,9 @@ const SHARED_STRUCTS = `
m2: f32, m2: f32,
l1: f32, l1: f32,
l2: f32, l2: f32,
damping: f32 damping: f32,
trailDecay: f32,
pad: f32 // <-- Padding for safe 16-byte memory alignment
}; };
struct State { struct State {
@@ -44,30 +46,45 @@ export const PENDULUM_FRAGMENT_SHADER_WGSL = SHARED_STRUCTS + `
let width = u32(p.width); let width = u32(p.width);
let height = u32(p.height); let height = u32(p.height);
// Fallback if buffer is not loaded yet
if (width == 0u || height == 0u) { if (width == 0u || height == 0u) {
fragmentOutputs.color = vec4<f32>(0.5, 0.0, 0.0, 1.0); fragmentOutputs.color = vec4<f32>(0.5, 0.0, 0.0, 1.0);
return fragmentOutputs; return fragmentOutputs;
} }
// Direct access to the pixel via screen coordinates
let x = u32(input.position.x); let x = u32(input.position.x);
let y = u32(input.position.y); let y = u32(input.position.y);
// Boundary check to prevent reading outside the buffer
if (x >= width || y >= height) { if (x >= width || y >= height) {
fragmentOutputs.color = vec4<f32>(0.0, 0.0, 0.0, 1.0); fragmentOutputs.color = vec4<f32>(0.0, 0.0, 0.0, 1.0);
return fragmentOutputs; return fragmentOutputs;
} }
let index = y * width + x; let index = y * width + x;
let val = pixelBuffer[index];
var color = vec3<f32>(0.1, 0.1, 0.15); // Background // --- THE MAGIC DECODING ---
if (val > 0.1) { color = vec3<f32>(0.5, 0.5, 0.5); } // Line let rawVal = pixelBuffer[index];
if (val > 0.8) { color = vec3<f32>(1.0, 1.0, 1.0); } // Mass var trailVal = rawVal;
var isLine = false;
fragmentOutputs.color = vec4<f32>(color, 1.0); // Check if the +10.0 flag is present (meaning this pixel is currently a line)
if (trailVal >= 10.0) {
isLine = true;
trailVal = trailVal - 10.0; // Remove flag to get the true trail value underneath
}
let bgColor = vec3<f32>(0.1, 0.1, 0.15);
let massColor = vec3<f32>(1.0, 1.0, 1.0);
let lineColor = vec3<f32>(0.5, 0.5, 0.5);
// Calculate background blending with the trail
var finalColor = mix(bgColor, massColor, clamp(trailVal, 0.0, 1.0));
// Overwrite with the grey line if necessary
if (isLine) {
finalColor = lineColor;
}
fragmentOutputs.color = vec4<f32>(finalColor, 1.0);
return fragmentOutputs; return fragmentOutputs;
} }
`; `;
@@ -87,7 +104,6 @@ export const PENDULUM_PHYSIC_COMPUTE_SHADER_WGSL = SHARED_STRUCTS + `
let delta_t = t1 - t2; let delta_t = t1 - t2;
// Equations split for better readability
let num1 = -p.g * (2.0 * p.m1 + p.m2) * sin(t1) let num1 = -p.g * (2.0 * p.m1 + p.m2) * sin(t1)
- p.m2 * p.g * sin(t1 - 2.0 * t2) - p.m2 * p.g * sin(t1 - 2.0 * t2)
- 2.0 * sin(delta_t) * p.m2 * (v2 * v2 * p.l2 + v1 * v1 * p.l1 * cos(delta_t)); - 2.0 * sin(delta_t) * p.m2 * (v2 * v2 * p.l2 + v1 * v1 * p.l1 * cos(delta_t));
@@ -98,7 +114,6 @@ export const PENDULUM_PHYSIC_COMPUTE_SHADER_WGSL = SHARED_STRUCTS + `
let den2 = p.l2 * (2.0 * p.m1 + p.m2 - p.m2 * cos(2.0 * delta_t)); let den2 = p.l2 * (2.0 * p.m1 + p.m2 - p.m2 * cos(2.0 * delta_t));
let a2 = num2 / den2; let a2 = num2 / den2;
// Integration (Semi-Implicit Euler)
let new_v1 = (v1 + a1 * p.dt) * p.damping; let new_v1 = (v1 + a1 * p.dt) * p.damping;
let new_v2 = (v2 + a2 * p.dt) * p.damping; let new_v2 = (v2 + a2 * p.dt) * p.damping;
@@ -137,25 +152,39 @@ export const PENDULUM_RENDER_COMPUTE_SHADER_WGSL = SHARED_STRUCTS + `
let aspect = p.width / p.height; let aspect = p.width / p.height;
let uv_corr = vec2<f32>(uv.x * aspect, uv.y); let uv_corr = vec2<f32>(uv.x * aspect, uv.y);
var newVal = 0.0; // Clear background // --- TRAIL EXTRACT & DECAY ---
let oldRaw = pixelBuffer[index];
var oldTrail = oldRaw;
// If the pixel was a line last frame, remove the +10 flag to get the trail memory
if (oldTrail >= 10.0) {
oldTrail = oldTrail - 10.0;
}
var newVal = oldTrail * p.trailDecay;
// Pendulum geometry // Pendulum geometry
let origin = vec2<f32>(0.5 * aspect, 0.3); let origin = vec2<f32>(0.5 * aspect, 0.3);
let displayScale = 0.15; let displayScale = 0.15;
// Calculate positions
let p1 = origin + vec2<f32>(sin(state.theta1), cos(state.theta1)) * p.l1 * displayScale; let p1 = origin + vec2<f32>(sin(state.theta1), cos(state.theta1)) * p.l1 * displayScale;
let p2 = p1 + vec2<f32>(sin(state.theta2), cos(state.theta2)) * p.l2 * displayScale; let p2 = p1 + vec2<f32>(sin(state.theta2), cos(state.theta2)) * p.l2 * displayScale;
// Distances
let dLine1 = sdSegment(uv_corr, origin, p1); let dLine1 = sdSegment(uv_corr, origin, p1);
let dLine2 = sdSegment(uv_corr, p1, p2); let dLine2 = sdSegment(uv_corr, p1, p2);
let dMass1 = length(uv_corr - p1); let dMass1 = length(uv_corr - p1);
let dMass2 = length(uv_corr - p2); let dMass2 = length(uv_corr - p2);
// Draw let isLine = (dLine1 < 0.002 || dLine2 < 0.002);
if (dLine1 < 0.003 || dLine2 < 0.003) { newVal = 0.5; } let isMass = (dMass1 < 0.01 || dMass2 < 0.01);
if (dMass1 < 0.02 || dMass2 < 0.02) { newVal = 1.0; }
// --- SMART LAYERING ---
if (isMass) {
newVal = 1.0;
} else if (isLine) {
// Lines are marked with +10.0 to tell the fragment shader to paint them grey,
// WITHOUT overwriting the fading trail memory underneath!
newVal = newVal + 10.0;
}
pixelBuffer[index] = newVal; pixelBuffer[index] = newVal;
} }

View File

@@ -1,7 +1,7 @@
{ {
"APP": { "APP": {
"TITLE": "Playground", "TITLE": "Playground",
"COPYRIGHT": "Bilder urheberrechtlich geschützt, keine Nutzung ohne Zustimmung!" "COPYRIGHT": "Bilder und Sourcecode sind urheberrechtlich geschützt, keine Nutzung ohne Zustimmung!"
}, },
"TOPBAR": { "TOPBAR": {
"ABOUT": "Über mich", "ABOUT": "Über mich",

View File

@@ -1,7 +1,7 @@
{ {
"APP": { "APP": {
"TITLE": "Playground", "TITLE": "Playground",
"COPYRIGHT": "Images protected by copyright, no use without permission!" "COPYRIGHT": "Images and code protected by copyright, no use without permission!"
}, },
"TOPBAR": { "TOPBAR": {
"ABOUT": "About me", "ABOUT": "About me",