feature/webGPU #25

Merged
lobo merged 14 commits from feature/webGPU into main 2026-02-21 12:32:04 +01:00
3 changed files with 31 additions and 41 deletions
Showing only changes of commit f499b78fd5 - Show all commits

View File

@@ -17,13 +17,15 @@ import {PENDULUM_FRAGMENT_SHADER_WGSL, PENDULUM_PHYSIC_COMPUTE_SHADER_WGSL, PEND
styleUrl: './pendulum.component.scss',
})
export class PendulumComponent {
renderConfig: RenderConfig = {
mode: '2D',
initialViewSize: 2,
shaderLanguage: ShaderLanguage.WGSL,
vertexShader: PENDULUM_VERTEX_SHADER_WGSL,
fragmentShader: PENDULUM_FRAGMENT_SHADER_WGSL,
uniformNames: ["params"]
uniformNames: [],
uniformBufferNames: ["params"]
};
onSceneReady(event: SceneReadyEvent) {
@@ -36,17 +38,11 @@ export class PendulumComponent {
const height = engine.getRenderHeight();
const totalPixels = width * height;
console.log("Width and Height ", width, height);
// A. Buffer Setup
// 1. Pixel Buffer (wie gehabt)
const pixelBuffer = new StorageBuffer(engine, totalPixels * 4);
// 2. State Buffer (4 Floats: theta1, theta2, v1, v2)
const stateBuffer = new StorageBuffer(engine, 4 * 4);
stateBuffer.update(new Float32Array([Math.PI / 4, Math.PI / 2, 0, 0]));
// 3. Params Uniform Buffer
const ubo = new UniformBuffer(engine);
ubo.addUniform("resolution", 2);
ubo.addUniform("time", 1);
@@ -57,13 +53,10 @@ export class PendulumComponent {
ubo.addUniform("l1", 1);
ubo.addUniform("l2", 1);
ubo.addUniform("damping", 1);
ubo.addUniform("pad1", 1); //Alignment
ubo.addUniform("pad2", 1); //Alignment
ubo.addUniform("pad1", 1);
ubo.addUniform("pad2", 1);
ubo.update();
// B. Compute Shaders Setup
// CS1: Physics
const csPhysics = new ComputeShader("physics", engine, {
computeSource: PENDULUM_PHYSIC_COMPUTE_SHADER_WGSL
}, {
@@ -75,7 +68,6 @@ export class PendulumComponent {
csPhysics.setStorageBuffer("state", stateBuffer);
csPhysics.setUniformBuffer("p", ubo);
// CS2: Render/Trail
const csRender = new ComputeShader("render", engine, {
computeSource: PENDULUM_RENDER_COMPUTE_SHADER_WGSL
}, {
@@ -89,7 +81,6 @@ export class PendulumComponent {
csRender.setUniformBuffer("p", ubo);
csRender.setStorageBuffer("state", stateBuffer);
// C. Material Setup (Anzeige)
const plane = scene.getMeshByName("plane");
if (plane?.material) {
const mat = plane.material as any;
@@ -97,39 +88,33 @@ export class PendulumComponent {
mat.setUniformBuffer("params", ubo);
}
// D. Simulation Loop
let time = 0;
// Physik Parameter
const dt = 0.015;
const g = 9.81;
const m1 = 2.0;
const m2 = 1.0;
const l1 = 1.5;
const l2 = 1.2;
const damping = 0.99;
// Du hast die Physik wieder drin gelassen, das ist super:
scene.onBeforeRenderObservable.add(() => {
time += dt;
// Update Params
ubo.updateFloat2("resolution", width, height);
const currentWidth = engine.getRenderWidth();
const currentHeight = engine.getRenderHeight();
ubo.updateFloat2("resolution", currentWidth, currentHeight);
ubo.updateFloat("time", time);
ubo.updateFloat("dt", dt);
ubo.updateFloat("g", g);
ubo.updateFloat("m1", m1);
ubo.updateFloat("m2", m2);
ubo.updateFloat("l1", l1);
ubo.updateFloat("l2", l2);
ubo.updateFloat("damping", damping);
ubo.updateFloat("g", 9.81);
ubo.updateFloat("m1", 2.0);
ubo.updateFloat("m2", 1.0);
ubo.updateFloat("l1", 1.5);
ubo.updateFloat("l2", 1.2);
ubo.updateFloat("damping", 0.99);
ubo.updateFloat("pad1", 0);
ubo.updateFloat("pad2", 0);
ubo.update();
// Do physics (1 thread)
// Physik Dispatch an
csPhysics.dispatch(1, 1, 1);
// Paint per pixel
const totalPixels = currentWidth * currentHeight;
const dispatchCount = Math.ceil(totalPixels / 64);
csRender.dispatch(dispatchCount, 1, 1);
});

View File

@@ -15,7 +15,7 @@
export const PENDULUM_FRAGMENT_SHADER_WGSL = `
varying vUV : vec2<f32>;
var<storage, read> pixelBuffer : array<f32>;
var<uniform> params : Params;
var<uniform> params : Params; // Zurück zum bewährten Struct!
struct Params {
resolution: vec2<f32>,
@@ -27,23 +27,26 @@ export const PENDULUM_FRAGMENT_SHADER_WGSL = `
l1: f32,
l2: f32,
damping: f32,
pad1: f32, // <-- Alignment
pad2: f32 // <-- Alignment
pad1: f32,
pad2: f32
};
@fragment
fn main(input : FragmentInputs) -> FragmentOutputs {
let width = u32(params.resolution.x);
let x = u32(input.vUV.x * params.resolution.x);
let y = u32(input.vUV.y * params.resolution.y);
let height = u32(params.resolution.y);
// clamp schützt uns vor Abstürzen durch Rundungsfehler am Bildschirmrand
let x = clamp(u32(input.vUV.x * params.resolution.x), 0u, width - 1u);
let y = clamp(u32(input.vUV.y * params.resolution.y), 0u, height - 1u);
let index = y * width + x;
let val = pixelBuffer[index];
// Hintergrund = Dunkelgrau, Linien = Grau, Massen = Weiß
var color = vec3<f32>(0.1, 0.1, 0.15);
if (val > 0.1) { color = vec3<f32>(0.5, 0.5, 0.5); } // Linie
if (val > 0.8) { color = vec3<f32>(1.0, 1.0, 1.0); } // Masse
if (val > 0.1) { color = vec3<f32>(0.5, 0.5, 0.5); }
if (val > 0.8) { color = vec3<f32>(1.0, 1.0, 1.0); }
fragmentOutputs.color = vec4<f32>(color, 1.0);
return fragmentOutputs;

View File

@@ -8,6 +8,7 @@ export interface RenderConfig {
vertexShader: string;
fragmentShader: string;
uniformNames: string[];
uniformBufferNames?: string[];
}
export type RenderCallback = (material: ShaderMaterial, camera: Camera, canvas: HTMLCanvasElement, scene: Scene) => void;
@@ -142,6 +143,7 @@ export class BabylonCanvas implements AfterViewInit, OnDestroy {
{
attributes: ["position", "uv"],
uniforms: ["resolution", "cameraPosition", ...this.config.uniformNames],
uniformBuffers: this.config.uniformBufferNames ?? [],
shaderLanguage: this.config.shaderLanguage ?? ShaderLanguage.GLSL
}
);