diff --git a/src/app/constants/RouterConstants.ts b/src/app/constants/RouterConstants.ts index e199a65..1ce6a6a 100644 --- a/src/app/constants/RouterConstants.ts +++ b/src/app/constants/RouterConstants.ts @@ -8,7 +8,7 @@ import {ConwayGolComponent} from '../pages/algorithms/conway-gol/conway-gol.comp import {LabyrinthComponent} from '../pages/algorithms/pathfinding/labyrinth/labyrinth.component'; import {FractalComponent} from '../pages/algorithms/fractal/fractal.component'; import {Fractal3dComponent} from '../pages/algorithms/fractal3d/fractal3d.component'; -import {PendulumComponent} from '../pages/algorithms/pendle/pendulum.component'; +import {PendulumComponent} from '../pages/algorithms/pendulum/pendulum.component'; export class RouterConstants { diff --git a/src/app/pages/algorithms/pendle/pendulum.component.ts b/src/app/pages/algorithms/pendle/pendulum.component.ts deleted file mode 100644 index f495381..0000000 --- a/src/app/pages/algorithms/pendle/pendulum.component.ts +++ /dev/null @@ -1,164 +0,0 @@ -import {Component} from '@angular/core'; -import {BabylonCanvas, RenderConfig, SceneReadyEvent} from '../../../shared/rendering/canvas/babylon-canvas.component'; -import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card'; -import {ComputeShader, ShaderLanguage, StorageBuffer, UniformBuffer} from '@babylonjs/core'; - -@Component({ - selector: 'app-pendulum', - imports: [ - BabylonCanvas, - MatCard, - MatCardContent, - MatCardHeader, - MatCardTitle, - ], - templateUrl: './pendulum.component.html', - styleUrl: './pendulum.component.scss', -}) -export class PendulumComponent { -// --- VERTEX SHADER --- -// Das hat funktioniert. Wir definieren Attribute, Babylon baut 'VertexInputs'. -// Wir geben 'FragmentInputs' zurück (was Babylon aus unseren varying baut). - private readonly vertexShaderWGSL = ` - attribute position : vec3; - attribute uv : vec2; - varying vUV : vec2; - - @vertex - fn main(input : VertexInputs) -> FragmentInputs { - var output : FragmentInputs; - output.position = vec4(input.position, 1.0); - output.vUV = input.uv; - return output; - } - `; - -// --- FRAGMENT SHADER (FIXED) --- -// Änderungen: -// 1. Rückgabetyp ist jetzt 'FragmentOutputs' (das Babylon Struct). -// 2. Wir schreiben in 'fragmentOutputs.color'. -// 3. Wir returnen 'fragmentOutputs'. - private readonly fragmentShaderWGSL = ` - varying vUV : vec2; - - var pixelBuffer : array; - var params : Params; - - struct Params { - resolution: vec2, - time: f32 - }; - - @fragment - fn main(input : FragmentInputs) -> FragmentOutputs { - let x = u32(input.vUV.x * params.resolution.x); - let y = u32(input.vUV.y * params.resolution.y); - let width = u32(params.resolution.x); - - let index = y * width + x; - let total = u32(params.resolution.x * params.resolution.y); - - // Default Farbe (schwarz) - var color = vec4(0.0, 0.0, 0.0, 1.0); - - if (index < total) { - let val = pixelBuffer[index]; - color = vec4(val, val * 0.5, 0.2, 1.0); - } - - // Babylon stellt die Variable 'fragmentOutputs' bereit. - // Das Feld heißt standardmäßig 'color'. - fragmentOutputs.color = color; - - return fragmentOutputs; - } - `; - -// --- COMPUTE SHADER (Unverändert) --- - private readonly computeShaderWGSL = ` - @group(0) @binding(0) var pixelBuffer : array; - @group(0) @binding(1) var params : Params; - - struct Params { - resolution: vec2, - time: f32 - }; - - @compute @workgroup_size(64) - fn main(@builtin(global_invocation_id) global_id : vec3) { - let index = global_id.x; - let totalPixels = u32(params.resolution.x * params.resolution.y); - if (index >= totalPixels) { return; } - - let width = u32(params.resolution.x); - let x = f32(index % width); - let y = f32(index / width); - - // Zeit-Variable nutzen für Animation - let value = sin(x * 0.05 + params.time) * cos(y * 0.05 + params.time); - - pixelBuffer[index] = value * 0.5 + 0.5; - } - `; - - renderConfig: RenderConfig = { - mode: '2D', - initialViewSize: 2, - shaderLanguage: ShaderLanguage.WGSL, - vertexShader: this.vertexShaderWGSL, - fragmentShader: this.fragmentShaderWGSL, - uniformNames: ["params"] - }; - - onSceneReady(event: SceneReadyEvent) { - const engine = event.engine; - const scene = event.scene; - - const width = engine.getRenderWidth(); - const height = engine.getRenderHeight(); - const totalPixels = width * height; - - // Buffer: 1 Float pro Pixel - const bufferSize = totalPixels * 4; - const pixelBuffer = new StorageBuffer(engine, bufferSize); - - // Uniform Buffer - const ubo = new UniformBuffer(engine); - ubo.addUniform("resolution", 2); - ubo.addUniform("time", 1); - ubo.update(); - - // Compute Shader - const cs = new ComputeShader("myCompute", engine, { - computeSource: this.computeShaderWGSL - }, { - bindingsMapping: { - "pixelBuffer": { group: 0, binding: 0 }, - "params": { group: 0, binding: 1 } - } - }); - cs.setStorageBuffer("pixelBuffer", pixelBuffer); - cs.setUniformBuffer("params", ubo); - - // Material Setup - const plane = scene.getMeshByName("plane"); - if (plane && plane.material) { - const mat = plane.material as any; - mat.setStorageBuffer("pixelBuffer", pixelBuffer); - mat.setUniformBuffer("params", ubo); - } - - // Render Loop - let time = 0; - scene.onBeforeRenderObservable.add(() => { - time += engine.getDeltaTime() / 1000.0; - - ubo.updateFloat2("resolution", width, height); - ubo.updateFloat("time", time); - ubo.update(); - - const dispatchCount = Math.ceil(totalPixels / 64); - cs.dispatch(dispatchCount, 1, 1); - }); - } -} diff --git a/src/app/pages/algorithms/pendle/pendulum.component.html b/src/app/pages/algorithms/pendulum/pendulum.component.html similarity index 100% rename from src/app/pages/algorithms/pendle/pendulum.component.html rename to src/app/pages/algorithms/pendulum/pendulum.component.html diff --git a/src/app/pages/algorithms/pendle/pendulum.component.scss b/src/app/pages/algorithms/pendulum/pendulum.component.scss similarity index 100% rename from src/app/pages/algorithms/pendle/pendulum.component.scss rename to src/app/pages/algorithms/pendulum/pendulum.component.scss diff --git a/src/app/pages/algorithms/pendulum/pendulum.component.ts b/src/app/pages/algorithms/pendulum/pendulum.component.ts new file mode 100644 index 0000000..7292393 --- /dev/null +++ b/src/app/pages/algorithms/pendulum/pendulum.component.ts @@ -0,0 +1,80 @@ +import {Component} from '@angular/core'; +import {BabylonCanvas, RenderConfig, SceneReadyEvent} from '../../../shared/rendering/canvas/babylon-canvas.component'; +import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card'; +import {ComputeShader, ShaderLanguage, StorageBuffer, UniformBuffer} from '@babylonjs/core'; +import {PENDULUM_COMPUTE_SHADER_WGSL, PENDULUM_FRAGMENT_SHADER_WGSL, PENDULUM_VERTEX_SHADER_WGSL} from './pendulum.shader'; + +@Component({ + selector: 'app-pendulum', + imports: [ + BabylonCanvas, + MatCard, + MatCardContent, + MatCardHeader, + MatCardTitle, + ], + templateUrl: './pendulum.component.html', + 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"] + }; + + onSceneReady(event: SceneReadyEvent) { + const engine = event.engine; + const scene = event.scene; + + const width = engine.getRenderWidth(); + const height = engine.getRenderHeight(); + const totalPixels = width * height; + + // Buffer: 1 Float pro Pixel + const bufferSize = totalPixels * 4; + const pixelBuffer = new StorageBuffer(engine, bufferSize); + + // Uniform Buffer + const ubo = new UniformBuffer(engine); + ubo.addUniform("resolution", 2); + ubo.addUniform("time", 1); + ubo.update(); + + // Compute Shader + const cs = new ComputeShader("Pendulum Compute Shader", engine, { + computeSource: PENDULUM_COMPUTE_SHADER_WGSL + }, { + bindingsMapping: { + "pixelBuffer": { group: 0, binding: 0 }, + "params": { group: 0, binding: 1 } + } + }); + cs.setStorageBuffer("pixelBuffer", pixelBuffer); + cs.setUniformBuffer("params", ubo); + + // Material Setup + const plane = scene.getMeshByName("plane"); + if (plane?.material) { + const mat = plane.material as any; + mat.setStorageBuffer("pixelBuffer", pixelBuffer); + mat.setUniformBuffer("params", ubo); + } + + // Render Loop + let time = 0; + scene.onBeforeRenderObservable.add(() => { + time += engine.getDeltaTime() / 1000.0; + + ubo.updateFloat2("resolution", width, height); + ubo.updateFloat("time", time); + ubo.update(); + + const dispatchCount = Math.ceil(totalPixels / 64); + cs.dispatch(dispatchCount, 1, 1); + }); + } +} diff --git a/src/app/pages/algorithms/pendulum/pendulum.shader.ts b/src/app/pages/algorithms/pendulum/pendulum.shader.ts new file mode 100644 index 0000000..75237a0 --- /dev/null +++ b/src/app/pages/algorithms/pendulum/pendulum.shader.ts @@ -0,0 +1,74 @@ +export const PENDULUM_VERTEX_SHADER_WGSL = ` + attribute position : vec3; + attribute uv : vec2; + varying vUV : vec2; + + @vertex + fn main(input : VertexInputs) -> FragmentInputs { + var output : FragmentInputs; + output.position = vec4(input.position, 1.0); + output.vUV = input.uv; + return output; + } + `; + +export const PENDULUM_FRAGMENT_SHADER_WGSL = ` + varying vUV : vec2; + + var pixelBuffer : array; + var params : Params; + + struct Params { + resolution: vec2, + time: f32 + }; + + @fragment + fn main(input : FragmentInputs) -> FragmentOutputs { + let x = u32(input.vUV.x * params.resolution.x); + let y = u32(input.vUV.y * params.resolution.y); + let width = u32(params.resolution.x); + + let index = y * width + x; + let total = u32(params.resolution.x * params.resolution.y); + + // Default Color (Black) + var color = vec4(0.0, 0.0, 0.0, 1.0); + + if (index < total) { + let val = pixelBuffer[index]; + color = vec4(val, val * 0.5, 0.2, 1.0); + } + + //fragmentOutput is provided by babylon + fragmentOutputs.color = color; + + return fragmentOutputs; + } + `; + +export const PENDULUM_COMPUTE_SHADER_WGSL = ` + @group(0) @binding(0) var pixelBuffer : array; + @group(0) @binding(1) var params : Params; + + struct Params { + resolution: vec2, + time: f32 + }; + + @compute @workgroup_size(64) + fn main(@builtin(global_invocation_id) global_id : vec3) { + let index = global_id.x; + let totalPixels = u32(params.resolution.x * params.resolution.y); + if (index >= totalPixels) { return; } + + let width = u32(params.resolution.x); + let x = f32(index % width); + let y = f32(index / width); + + // Zeit-Variable nutzen für Animation + let value = sin(x * 0.05 + params.time) * cos(y * 0.05 + params.time); + + pixelBuffer[index] = value * 0.5 + 0.5; + } + `;