Compare commits
4 Commits
main
...
8cdf00fdc9
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cdf00fdc9 | |||
| 38e256544f | |||
| 0a57ea0ad7 | |||
| 8584b5163d |
6
package-lock.json
generated
6
package-lock.json
generated
@@ -11673,9 +11673,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.1",
|
"version": "6.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
|
||||||
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const routes: Routes = [
|
|||||||
{ path: RouterConstants.GOL.PATH, component: RouterConstants.GOL.COMPONENT},
|
{ path: RouterConstants.GOL.PATH, component: RouterConstants.GOL.COMPONENT},
|
||||||
{ path: RouterConstants.LABYRINTH.PATH, component: RouterConstants.LABYRINTH.COMPONENT},
|
{ path: RouterConstants.LABYRINTH.PATH, component: RouterConstants.LABYRINTH.COMPONENT},
|
||||||
{ path: RouterConstants.FRACTAL.PATH, component: RouterConstants.FRACTAL.COMPONENT},
|
{ path: RouterConstants.FRACTAL.PATH, component: RouterConstants.FRACTAL.COMPONENT},
|
||||||
{ path: RouterConstants.FRACTAL3d.PATH, component: RouterConstants.FRACTAL3d.COMPONENT}
|
{ path: RouterConstants.FRACTAL3d.PATH, component: RouterConstants.FRACTAL3d.COMPONENT},
|
||||||
|
{ path: RouterConstants.PATH_TRACING.PATH, component: RouterConstants.PATH_TRACING.COMPONENT}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {ConwayGolComponent} from '../pages/algorithms/conway-gol/conway-gol.comp
|
|||||||
import {LabyrinthComponent} from '../pages/algorithms/pathfinding/labyrinth/labyrinth.component';
|
import {LabyrinthComponent} from '../pages/algorithms/pathfinding/labyrinth/labyrinth.component';
|
||||||
import {FractalComponent} from '../pages/algorithms/fractal/fractal.component';
|
import {FractalComponent} from '../pages/algorithms/fractal/fractal.component';
|
||||||
import {Fractal3dComponent} from '../pages/algorithms/fractal3d/fractal3d.component';
|
import {Fractal3dComponent} from '../pages/algorithms/fractal3d/fractal3d.component';
|
||||||
|
import {PathTracingComponent} from '../pages/algorithms/path-tracing/path-tracing.component';
|
||||||
|
|
||||||
export class RouterConstants {
|
export class RouterConstants {
|
||||||
|
|
||||||
@@ -65,6 +66,12 @@ export class RouterConstants {
|
|||||||
COMPONENT: Fractal3dComponent
|
COMPONENT: Fractal3dComponent
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static readonly PATH_TRACING = {
|
||||||
|
PATH: 'algorithms/pathtracing',
|
||||||
|
LINK: '/algorithms/pathtracing',
|
||||||
|
COMPONENT: PathTracingComponent
|
||||||
|
};
|
||||||
|
|
||||||
static readonly IMPRINT = {
|
static readonly IMPRINT = {
|
||||||
PATH: 'imprint',
|
PATH: 'imprint',
|
||||||
LINK: '/imprint',
|
LINK: '/imprint',
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {UrlConstants} from '../../../constants/UrlConstants';
|
|||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
import {BabylonCanvas, RenderCallback, RenderConfig} from '../../../shared/rendering/canvas/babylon-canvas.component';
|
import {BabylonCanvas, RenderCallback, RenderConfig} from '../../../shared/rendering/canvas/babylon-canvas.component';
|
||||||
import {FRACTAL2D_FRAGMENT, FRACTAL2D_VERTEX} from './fractal.shader';
|
import {FRACTAL2D_FRAGMENT, FRACTAL2D_VERTEX} from './fractal.shader';
|
||||||
import {PointerEventTypes, PointerInfo, Scene, ShaderMaterial, Vector2} from '@babylonjs/core';
|
import {PointerEventTypes, PointerInfo, Scene, ShaderMaterial, Vector2, WebGPUEngine} from '@babylonjs/core';
|
||||||
import {MatButton} from '@angular/material/button';
|
import {MatButton} from '@angular/material/button';
|
||||||
import {MatIcon} from '@angular/material/icon';
|
import {MatIcon} from '@angular/material/icon';
|
||||||
import {NgxSliderModule, Options} from '@angular-slider/ngx-slider';
|
import {NgxSliderModule, Options} from '@angular-slider/ngx-slider';
|
||||||
@@ -74,7 +74,9 @@ export class FractalComponent implements OnInit {
|
|||||||
|
|
||||||
renderConfig: RenderConfig = {
|
renderConfig: RenderConfig = {
|
||||||
mode: '2D',
|
mode: '2D',
|
||||||
|
pipeline: 'Material',
|
||||||
initialViewSize: 100,
|
initialViewSize: 100,
|
||||||
|
activeAccumulationBuffer: false,
|
||||||
vertexShader: FRACTAL2D_VERTEX,
|
vertexShader: FRACTAL2D_VERTEX,
|
||||||
fragmentShader: FRACTAL2D_FRAGMENT,
|
fragmentShader: FRACTAL2D_FRAGMENT,
|
||||||
uniformNames: ["worldViewProjection", "time", "targetPosition","center", "zoom", "maxIterations", "algorithm", "colorScheme", "juliaC"]
|
uniformNames: ["worldViewProjection", "time", "targetPosition","center", "zoom", "maxIterations", "algorithm", "colorScheme", "juliaC"]
|
||||||
@@ -155,8 +157,8 @@ export class FractalComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSceneReady(scene: Scene): void {
|
onSceneReady(options: {scene: Scene, engine: WebGPUEngine}): void {
|
||||||
scene.onPointerObservable.add((pointerInfo) => {
|
options.scene.onPointerObservable.add((pointerInfo) => {
|
||||||
switch (pointerInfo.type) {
|
switch (pointerInfo.type) {
|
||||||
|
|
||||||
case PointerEventTypes.POINTERDOWN:
|
case PointerEventTypes.POINTERDOWN:
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ export class Fractal3dComponent {
|
|||||||
|
|
||||||
fractalConfig: RenderConfig = {
|
fractalConfig: RenderConfig = {
|
||||||
mode: '3D',
|
mode: '3D',
|
||||||
|
pipeline: 'Material',
|
||||||
|
activeAccumulationBuffer: false,
|
||||||
initialViewSize: 4,
|
initialViewSize: 4,
|
||||||
vertexShader: MANDELBULB_VERTEX,
|
vertexShader: MANDELBULB_VERTEX,
|
||||||
fragmentShader: MANDELBULB_FRAGMENT,
|
fragmentShader: MANDELBULB_FRAGMENT,
|
||||||
|
|||||||
126
src/app/pages/algorithms/path-tracing/path-tracing-shader.ts
Normal file
126
src/app/pages/algorithms/path-tracing/path-tracing-shader.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// path-tracing-shader.ts
|
||||||
|
|
||||||
|
export const PATH_TRACING_VERTEX = `
|
||||||
|
precision highp float;
|
||||||
|
attribute vec3 position;
|
||||||
|
attribute vec2 uv;
|
||||||
|
varying vec2 vUV;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
vUV = uv;
|
||||||
|
gl_Position = vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const PATH_TRACING_FRAGMENT = `
|
||||||
|
precision highp float;
|
||||||
|
varying vec2 vUV;
|
||||||
|
|
||||||
|
uniform vec2 resolution;
|
||||||
|
uniform vec3 cameraPosition;
|
||||||
|
uniform vec3 cameraForward;
|
||||||
|
uniform vec3 cameraRight;
|
||||||
|
uniform vec3 cameraUp;
|
||||||
|
uniform float frameCount;
|
||||||
|
uniform float time;
|
||||||
|
|
||||||
|
uniform sampler2D accumulationBuffer;
|
||||||
|
|
||||||
|
struct Sphere {
|
||||||
|
vec3 center;
|
||||||
|
float radius;
|
||||||
|
vec3 color;
|
||||||
|
vec3 emission;
|
||||||
|
float materialType;
|
||||||
|
};
|
||||||
|
|
||||||
|
float random(inout float seed) {
|
||||||
|
seed = fract(sin(seed) * 43758.5453123);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 randomHemisphereDir(vec3 normal, inout float seed) {
|
||||||
|
float phi = 2.0 * 3.14159265 * random(seed);
|
||||||
|
float cosTheta = random(seed);
|
||||||
|
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
|
||||||
|
vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||||
|
vec3 tangent = normalize(cross(up, normal));
|
||||||
|
vec3 bitangent = cross(normal, tangent);
|
||||||
|
return normalize(tangent * cos(phi) * sinTheta + bitangent * sin(phi) * sinTheta + normal * cosTheta);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sphere getSphere(int index) {
|
||||||
|
if (index == 0) { return Sphere(vec3(-100.5, 0.0, 0.0), 100.0, vec3(0.8, 0.1, 0.1), vec3(0.0), 0.0); }
|
||||||
|
if (index == 1) { return Sphere(vec3( 100.5, 0.0, 0.0), 100.0, vec3(0.1, 0.8, 0.1), vec3(0.0), 0.0); }
|
||||||
|
if (index == 2) { return Sphere(vec3(0.0, 100.5, 0.0), 100.0, vec3(0.8, 0.8, 0.8), vec3(0.0), 0.0); }
|
||||||
|
if (index == 3) { return Sphere(vec3(0.0, -100.5, 0.0), 100.0, vec3(0.8, 0.8, 0.8), vec3(0.0), 0.0); }
|
||||||
|
if (index == 4) { return Sphere(vec3(0.0, 0.0, 100.5), 100.0, vec3(0.8, 0.8, 0.8), vec3(0.0), 0.0); }
|
||||||
|
if (index == 5) { return Sphere(vec3(0.0, 1.4, 0.0), 0.25, vec3(1.0), vec3(20.0), 0.0); }
|
||||||
|
if (index == 6) { return Sphere(vec3(-0.4, -0.4, -0.1), 0.25, vec3(1.0), vec3(0.0), 1.0); }
|
||||||
|
return Sphere(vec3(0.4, -0.4, 0.3), 0.25, vec3(0.2, 0.4, 0.9), vec3(0.0), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
float seed = dot(vUV, vec2(12.9898, 78.233)) + time;
|
||||||
|
vec2 jitter = vec2(random(seed), random(seed)) / resolution;
|
||||||
|
vec2 screenPos = (vUV + jitter) * 2.0 - 1.0;
|
||||||
|
float aspect = resolution.x / resolution.y;
|
||||||
|
|
||||||
|
vec3 ro = cameraPosition;
|
||||||
|
vec3 rd = normalize(cameraForward + cameraRight * screenPos.x * aspect + cameraUp * screenPos.y);
|
||||||
|
|
||||||
|
vec3 incomingLight = vec3(0.0);
|
||||||
|
vec3 throughput = vec3(1.0);
|
||||||
|
|
||||||
|
for (int bounce = 0; bounce < 4; bounce++) {
|
||||||
|
float tMin = 10000.0;
|
||||||
|
int hitIdx = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
Sphere s = getSphere(i);
|
||||||
|
vec3 oc = ro - s.center;
|
||||||
|
float b = dot(oc, rd);
|
||||||
|
float c = dot(oc, oc) - s.radius * s.radius;
|
||||||
|
float h = b * b - c;
|
||||||
|
|
||||||
|
if (h > 0.0) {
|
||||||
|
float t = -b - sqrt(h);
|
||||||
|
if (t > 0.001 && t < tMin) {
|
||||||
|
tMin = t;
|
||||||
|
hitIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitIdx == -1) {
|
||||||
|
incomingLight += throughput * vec3(0.01);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sphere s = getSphere(hitIdx);
|
||||||
|
vec3 hitPoint = ro + rd * tMin;
|
||||||
|
vec3 normal = normalize(hitPoint - s.center);
|
||||||
|
|
||||||
|
incomingLight += s.emission * throughput;
|
||||||
|
throughput *= s.color;
|
||||||
|
|
||||||
|
ro = hitPoint;
|
||||||
|
if (s.materialType > 0.5) {
|
||||||
|
rd = reflect(rd, normal);
|
||||||
|
} else {
|
||||||
|
rd = randomHemisphereDir(normal, seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 prevColor = texture2D(accumulationBuffer, vUV).rgb;
|
||||||
|
|
||||||
|
// Akkumulation
|
||||||
|
if (frameCount < 1.0) {
|
||||||
|
gl_FragColor = vec4(incomingLight, 1.0);
|
||||||
|
} else {
|
||||||
|
float weight = 1.0 / (frameCount + 1.0);
|
||||||
|
vec3 final = mix(prevColor, incomingLight, weight);
|
||||||
|
gl_FragColor = vec4(final, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<mat-card class="container">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>{{ 'PATH_TRACING.TITLE' | translate }}</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<app-information [algorithmInformation]="algoInformation"/>
|
||||||
|
<app-babylon-canvas
|
||||||
|
[config]="config"
|
||||||
|
[renderCallback]="onRender"
|
||||||
|
/>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
130
src/app/pages/algorithms/path-tracing/path-tracing.component.ts
Normal file
130
src/app/pages/algorithms/path-tracing/path-tracing.component.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import {BabylonCanvas, RenderCallback, RenderConfig} from '../../../shared/rendering/canvas/babylon-canvas.component';
|
||||||
|
import {
|
||||||
|
Camera, RenderTargetTexture,
|
||||||
|
Scene, ShaderMaterial,
|
||||||
|
Vector3,
|
||||||
|
Constants, Layer, Vector2, Mesh
|
||||||
|
} from '@babylonjs/core';
|
||||||
|
import {PATH_TRACING_FRAGMENT, PATH_TRACING_VERTEX} from './path-tracing-shader';
|
||||||
|
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
|
||||||
|
import {Information} from '../information/information';
|
||||||
|
import {TranslatePipe} from '@ngx-translate/core';
|
||||||
|
import {AlgorithmInformation} from '../information/information.models';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-path-tracing',
|
||||||
|
imports: [BabylonCanvas, MatCardContent, MatCard, Information, MatCardTitle, MatCardHeader, TranslatePipe],
|
||||||
|
templateUrl: './path-tracing.component.html',
|
||||||
|
styleUrls: ['./path-tracing.component.scss'],
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class PathTracingComponent {
|
||||||
|
algoInformation: AlgorithmInformation = {
|
||||||
|
title: '',
|
||||||
|
entries: [],
|
||||||
|
disclaimer: '',
|
||||||
|
disclaimerBottom: '',
|
||||||
|
disclaimerListEntry: []
|
||||||
|
};
|
||||||
|
|
||||||
|
config: RenderConfig =
|
||||||
|
{ mode: '3D',
|
||||||
|
pipeline: 'Material',
|
||||||
|
initialViewSize: 4,
|
||||||
|
activeAccumulationBuffer: true,
|
||||||
|
vertexShader: PATH_TRACING_VERTEX,
|
||||||
|
fragmentShader: PATH_TRACING_FRAGMENT,
|
||||||
|
uniformNames: ["cameraForward", "cameraRight", "cameraUp", "time", "frameCount"]
|
||||||
|
};
|
||||||
|
|
||||||
|
private frameCount: number = 0;
|
||||||
|
private lastCamPos: Vector3 = new Vector3();
|
||||||
|
private rttA?: RenderTargetTexture;
|
||||||
|
private rttB?: RenderTargetTexture;
|
||||||
|
private isUsingA: boolean = true;
|
||||||
|
private displayLayer?: Layer;
|
||||||
|
private screenQuad?: Mesh;
|
||||||
|
|
||||||
|
onRender: RenderCallback = (material: ShaderMaterial, camera: Camera, canvas: HTMLCanvasElement, scene: Scene) => {
|
||||||
|
if (!material || !camera) return;
|
||||||
|
|
||||||
|
// 1. Setup beim ersten Frame
|
||||||
|
if (!this.rttA || !this.rttB) {
|
||||||
|
this.setupAccumulation(scene, canvas);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Kamera Bewegung Check
|
||||||
|
if (!camera.position.equals(this.lastCamPos)) {
|
||||||
|
this.frameCount = 0;
|
||||||
|
this.lastCamPos.copyFrom(camera.position);
|
||||||
|
} else {
|
||||||
|
this.frameCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const forward = camera.getForwardRay().direction;
|
||||||
|
const right = Vector3.Cross(forward, camera.upVector).normalize();
|
||||||
|
const up = Vector3.Cross(right, forward).normalize();
|
||||||
|
|
||||||
|
material.setVector3("cameraForward", forward);
|
||||||
|
material.setVector3("cameraRight", right);
|
||||||
|
material.setVector3("cameraUp", up);
|
||||||
|
material.setFloat("time", performance.now() / 1000.0);
|
||||||
|
material.setFloat("frameCount", this.frameCount);
|
||||||
|
material.setVector2("resolution", new Vector2(canvas.width, canvas.height));
|
||||||
|
|
||||||
|
// 3. Ping-Pong Zuweisung
|
||||||
|
const source = this.isUsingA ? this.rttA : this.rttB;
|
||||||
|
const target = this.isUsingA ? this.rttB : this.rttA;
|
||||||
|
|
||||||
|
// Shader liest aus alter Textur
|
||||||
|
material.setTexture("accumulationBuffer", source);
|
||||||
|
|
||||||
|
// Wenn das Mesh existiert, rendern wir es in die NEUE Textur
|
||||||
|
if (this.screenQuad) {
|
||||||
|
// WICHTIG: Das Mesh muss im RTT Mode sichtbar sein
|
||||||
|
this.screenQuad.isVisible = true;
|
||||||
|
|
||||||
|
// Rendert den Shader auf das Quad und speichert es in 'target'
|
||||||
|
target.render();
|
||||||
|
|
||||||
|
// Verstecken für den normalen Screen-Render-Pass
|
||||||
|
this.screenQuad.isVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Anzeige auf dem Bildschirm
|
||||||
|
if (this.displayLayer) {
|
||||||
|
this.displayLayer.texture = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isUsingA = !this.isUsingA;
|
||||||
|
};
|
||||||
|
|
||||||
|
private setupAccumulation(scene: Scene, canvas: HTMLCanvasElement): void {
|
||||||
|
const size = { width: canvas.width, height: canvas.height };
|
||||||
|
|
||||||
|
// Float Texturen sind entscheidend für Akkumulation
|
||||||
|
this.rttA = new RenderTargetTexture("rttA", size, scene, false, true, Constants.TEXTURETYPE_FLOAT);
|
||||||
|
this.rttB = new RenderTargetTexture("rttB", size, scene, false, true, Constants.TEXTURETYPE_FLOAT);
|
||||||
|
|
||||||
|
// Wir holen uns das Mesh ("background"), das in BabylonCanvas erstellt wurde
|
||||||
|
this.screenQuad = scene.getMeshByName("background") as Mesh;
|
||||||
|
|
||||||
|
// Sicherheits-Check falls Name abweicht
|
||||||
|
if (!this.screenQuad && scene.meshes.length > 0) {
|
||||||
|
this.screenQuad = scene.meshes[0] as Mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.screenQuad) {
|
||||||
|
// Wir sagen den Texturen: "Nur dieses Mesh rendern!"
|
||||||
|
this.rttA.renderList = [this.screenQuad];
|
||||||
|
this.rttB.renderList = [this.screenQuad];
|
||||||
|
this.screenQuad.isVisible = false; // Initial aus
|
||||||
|
} else {
|
||||||
|
console.error("Screen Quad not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displayLayer = new Layer("display", null, scene, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,6 +44,12 @@ export class AlgorithmsService {
|
|||||||
title: 'ALGORITHM.FRACTAL3D.TITLE',
|
title: 'ALGORITHM.FRACTAL3D.TITLE',
|
||||||
description: 'ALGORITHM.FRACTAL3D.DESCRIPTION',
|
description: 'ALGORITHM.FRACTAL3D.DESCRIPTION',
|
||||||
routerLink: RouterConstants.FRACTAL3d.LINK
|
routerLink: RouterConstants.FRACTAL3d.LINK
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pathTracing',
|
||||||
|
title: 'ALGORITHM.PATH_TRACING.TITLE',
|
||||||
|
description: 'ALGORITHM.PATH_TRACING.DESCRIPTION',
|
||||||
|
routerLink: RouterConstants.PATH_TRACING.LINK
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import {ArcRotateCamera, Camera, Engine, MeshBuilder, Scene, ShaderMaterial, Vec
|
|||||||
|
|
||||||
export interface RenderConfig {
|
export interface RenderConfig {
|
||||||
mode: '2D' | '3D';
|
mode: '2D' | '3D';
|
||||||
|
pipeline: 'Material' | 'Compute';
|
||||||
initialViewSize: number;
|
initialViewSize: number;
|
||||||
vertexShader: string;
|
activeAccumulationBuffer: boolean;
|
||||||
fragmentShader: string;
|
vertexShader?: string;
|
||||||
uniformNames: string[];
|
fragmentShader?: string;
|
||||||
|
uniformNames?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RenderCallback = (material: ShaderMaterial, camera: Camera, canvas: HTMLCanvasElement, scene: Scene) => void;
|
export type RenderCallback = (material: ShaderMaterial, camera: Camera, canvas: HTMLCanvasElement, scene: Scene) => void;
|
||||||
@@ -25,7 +27,7 @@ export class BabylonCanvas implements AfterViewInit, OnDestroy {
|
|||||||
@Input({ required: true }) config!: RenderConfig;
|
@Input({ required: true }) config!: RenderConfig;
|
||||||
@Input() renderCallback?: RenderCallback;
|
@Input() renderCallback?: RenderCallback;
|
||||||
|
|
||||||
@Output() sceneReady = new EventEmitter<Scene>();
|
@Output() sceneReady = new EventEmitter<{scene: Scene, engine: Engine}>();
|
||||||
|
|
||||||
private engine!: Engine;
|
private engine!: Engine;
|
||||||
private scene!: Scene;
|
private scene!: Scene;
|
||||||
@@ -53,14 +55,26 @@ export class BabylonCanvas implements AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initBabylon(): void {
|
private initBabylon(): void {
|
||||||
|
if (!navigator.gpu) {
|
||||||
|
alert("Your browser does not support webgpu, maybe you have activate the hardware acceleration.!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const canvas = this.canvasRef.nativeElement;
|
const canvas = this.canvasRef.nativeElement;
|
||||||
this.engine = new Engine(canvas, true);
|
this.engine = new Engine(canvas, true, {
|
||||||
|
preserveDrawingBuffer: true,
|
||||||
|
stencil: true
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.scene = new Scene(this.engine);
|
this.scene = new Scene(this.engine);
|
||||||
this.setupCamera(canvas);
|
this.setupCamera(canvas);
|
||||||
canvas.addEventListener('wheel', (evt: WheelEvent) => evt.preventDefault(), { passive: false });
|
canvas.addEventListener('wheel', (evt: WheelEvent) => evt.preventDefault(), { passive: false });
|
||||||
this.createShaderMaterial();
|
if (this.config.pipeline !== 'Compute') {
|
||||||
this.createFullScreenRect();
|
this.createShaderMaterial();
|
||||||
this.sceneReady.emit(this.scene);
|
this.createFullScreenRect();
|
||||||
|
}
|
||||||
|
this.sceneReady.emit({ scene: this.scene, engine: this.engine });
|
||||||
this.addRenderLoop(canvas);
|
this.addRenderLoop(canvas);
|
||||||
this.addResizeHandler();
|
this.addResizeHandler();
|
||||||
}
|
}
|
||||||
@@ -102,6 +116,8 @@ export class BabylonCanvas implements AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createFullScreenRect() {
|
private createFullScreenRect() {
|
||||||
|
if (!this.shaderMaterial) return;
|
||||||
|
|
||||||
const plane = MeshBuilder.CreatePlane("plane", {size: 110}, this.scene);
|
const plane = MeshBuilder.CreatePlane("plane", {size: 110}, this.scene);
|
||||||
|
|
||||||
if (this.config.mode === '3D') {
|
if (this.config.mode === '3D') {
|
||||||
@@ -111,11 +127,19 @@ export class BabylonCanvas implements AfterViewInit, OnDestroy {
|
|||||||
plane.lookAt(this.camera.position);
|
plane.lookAt(this.camera.position);
|
||||||
}
|
}
|
||||||
plane.alwaysSelectAsActiveMesh = true;
|
plane.alwaysSelectAsActiveMesh = true;
|
||||||
|
plane.infiniteDistance = true;
|
||||||
|
|
||||||
plane.material = this.shaderMaterial;
|
plane.material = this.shaderMaterial;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createShaderMaterial() {
|
private createShaderMaterial() {
|
||||||
|
|
||||||
|
if (!this.config.vertexShader || !this.config.fragmentShader)
|
||||||
|
{
|
||||||
|
console.warn("Bablyon canvas needs a vertex shader, a fragment shader and a uniforms array.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.shaderMaterial = new ShaderMaterial(
|
this.shaderMaterial = new ShaderMaterial(
|
||||||
"shaderMaterial",
|
"shaderMaterial",
|
||||||
this.scene,
|
this.scene,
|
||||||
@@ -123,26 +147,36 @@ export class BabylonCanvas implements AfterViewInit, OnDestroy {
|
|||||||
vertexSource: this.config.vertexShader,
|
vertexSource: this.config.vertexShader,
|
||||||
fragmentSource: this.config.fragmentShader
|
fragmentSource: this.config.fragmentShader
|
||||||
},
|
},
|
||||||
{
|
this.getOptions()
|
||||||
attributes: ["position", "uv"],
|
|
||||||
uniforms: ["resolution", "cameraPosition", ...this.config.uniformNames]
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
this.shaderMaterial.disableDepthWrite = true;
|
this.shaderMaterial.disableDepthWrite = true;
|
||||||
this.shaderMaterial.backFaceCulling = false;
|
this.shaderMaterial.backFaceCulling = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getOptions() {
|
||||||
|
const uniforms = ["resolution", "cameraPosition", ...(this.config.uniformNames || [])];
|
||||||
|
const samplers = this.config.activeAccumulationBuffer ? ["accumulationBuffer"] : [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
attributes: ["position", "uv"],
|
||||||
|
uniforms: uniforms,
|
||||||
|
samplers: samplers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private addRenderLoop(canvas: HTMLCanvasElement) {
|
private addRenderLoop(canvas: HTMLCanvasElement) {
|
||||||
this.engine.runRenderLoop(() => {
|
this.engine.runRenderLoop(() => {
|
||||||
|
|
||||||
// callback call to call specific uniforms
|
|
||||||
if (this.renderCallback) {
|
if (this.renderCallback) {
|
||||||
|
// callback call to call specific uniforms
|
||||||
this.renderCallback(this.shaderMaterial, this.camera, canvas, this.scene);
|
this.renderCallback(this.shaderMaterial, this.camera, canvas, this.scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
// default uniforms which maybe each scene has
|
if (this.shaderMaterial) {
|
||||||
this.shaderMaterial.setVector2("resolution", new Vector2(canvas.width, canvas.height));
|
// default uniforms which maybe each material scene has
|
||||||
this.shaderMaterial.setVector3("cameraPosition", this.camera.position);
|
this.shaderMaterial.setVector2("resolution", new Vector2(canvas.width, canvas.height));
|
||||||
|
this.shaderMaterial.setVector3("cameraPosition", this.camera.position);
|
||||||
|
}
|
||||||
|
|
||||||
this.scene.render();
|
this.scene.render();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -415,6 +415,9 @@
|
|||||||
"DISCLAIMER_4": "Licht & Schatten: Um die Tiefe sichtbar zu machen, werden Lichtreflexionen und Schatten (Ambient Occlusion) basierend auf der Krümmung der Formel simuliert."
|
"DISCLAIMER_4": "Licht & Schatten: Um die Tiefe sichtbar zu machen, werden Lichtreflexionen und Schatten (Ambient Occlusion) basierend auf der Krümmung der Formel simuliert."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"PATH_TRACING": {
|
||||||
|
"TITLE": "Path Tracing"
|
||||||
|
},
|
||||||
"ALGORITHM": {
|
"ALGORITHM": {
|
||||||
"TITLE": "Algorithmen",
|
"TITLE": "Algorithmen",
|
||||||
"PATHFINDING": {
|
"PATHFINDING": {
|
||||||
@@ -441,6 +444,10 @@
|
|||||||
"TITLE": "Fraktale 3D",
|
"TITLE": "Fraktale 3D",
|
||||||
"DESCRIPTION": "3D-Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)."
|
"DESCRIPTION": "3D-Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)."
|
||||||
},
|
},
|
||||||
|
"PATH_TRACING": {
|
||||||
|
"TITLE": "Path Tracing ",
|
||||||
|
"DESCRIPTION": "Path Tracing ist ein Algorithmus zur Bildsynthese, der die Simulation der globalen Beleuchtung ermöglicht."
|
||||||
|
},
|
||||||
"NOTE": "HINWEIS",
|
"NOTE": "HINWEIS",
|
||||||
"GRID_HEIGHT": "Höhe",
|
"GRID_HEIGHT": "Höhe",
|
||||||
"GRID_WIDTH": "Beite"
|
"GRID_WIDTH": "Beite"
|
||||||
|
|||||||
@@ -414,6 +414,9 @@
|
|||||||
"DISCLAIMER_4": "Light & Shadow: To visualize depth, light reflections and shadows (Ambient Occlusion) are simulated based on the curvature of the formula."
|
"DISCLAIMER_4": "Light & Shadow: To visualize depth, light reflections and shadows (Ambient Occlusion) are simulated based on the curvature of the formula."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"PATH_TRACING": {
|
||||||
|
"TITLE": "Path Tracing"
|
||||||
|
},
|
||||||
"ALGORITHM": {
|
"ALGORITHM": {
|
||||||
"TITLE": "Algorithms",
|
"TITLE": "Algorithms",
|
||||||
"PATHFINDING": {
|
"PATHFINDING": {
|
||||||
@@ -440,6 +443,10 @@
|
|||||||
"TITLE": "Fractals 3D",
|
"TITLE": "Fractals 3D",
|
||||||
"DESCRIPTION": "3D Visualisation of complex geometric patterns that resemble each other on increasingly smaller scales (self-similarity)."
|
"DESCRIPTION": "3D Visualisation of complex geometric patterns that resemble each other on increasingly smaller scales (self-similarity)."
|
||||||
},
|
},
|
||||||
|
"PATH_TRACING": {
|
||||||
|
"TITLE": "Path Tracing ",
|
||||||
|
"DESCRIPTION": "Path tracing is a rendering algorithm in computer graphics that simulates how light interacts with objects, voxels, and participating media to generate realistic (physically plausible) images."
|
||||||
|
},
|
||||||
"NOTE": "Note",
|
"NOTE": "Note",
|
||||||
"GRID_HEIGHT": "Height",
|
"GRID_HEIGHT": "Height",
|
||||||
"GRID_WIDTH": "Width"
|
"GRID_WIDTH": "Width"
|
||||||
|
|||||||
Reference in New Issue
Block a user