Added trail effect
This commit is contained in:
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user