First image of fractal
Next the nasty stuff like movement and ui :-D
This commit is contained in:
15
package-lock.json
generated
15
package-lock.json
generated
@@ -17,9 +17,9 @@
|
|||||||
"@angular/material": "~21.1.0",
|
"@angular/material": "~21.1.0",
|
||||||
"@angular/platform-browser": "~21.1.0",
|
"@angular/platform-browser": "~21.1.0",
|
||||||
"@angular/router": "~21.1.0",
|
"@angular/router": "~21.1.0",
|
||||||
|
"@babylonjs/core": "^8.50.5",
|
||||||
"@ngx-translate/core": "~17.0.0",
|
"@ngx-translate/core": "~17.0.0",
|
||||||
"@ngx-translate/http-loader": "~17.0.0",
|
"@ngx-translate/http-loader": "~17.0.0",
|
||||||
"babylonjs": "^8.50.5",
|
|
||||||
"inquirer": "^13.2.2",
|
"inquirer": "^13.2.2",
|
||||||
"rxjs": "~7.8.2",
|
"rxjs": "~7.8.2",
|
||||||
"swiper": "~12.1.0",
|
"swiper": "~12.1.0",
|
||||||
@@ -1150,6 +1150,12 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babylonjs/core": {
|
||||||
|
"version": "8.50.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.50.5.tgz",
|
||||||
|
"integrity": "sha512-rrHAYJmsZOuvC6wdNhGNgdn9JlMxBhk30YhBWGPSxYnnUD7/n/U9Pb3a8k0lpTHipbCfjJ+J5r6GdXtPEikhEg==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/@emnapi/core": {
|
"node_modules/@emnapi/core": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
|
||||||
@@ -6449,13 +6455,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/babylonjs": {
|
|
||||||
"version": "8.50.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-8.50.5.tgz",
|
|
||||||
"integrity": "sha512-r4CvDBY798k0E8FqOrj2pg5If7MJhELACIpQw8r1TT4TXNWNXxlqrNmKtSzxauWFQgankwVbKhe41YA8k+tQMw==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
"@angular/material": "~21.1.0",
|
"@angular/material": "~21.1.0",
|
||||||
"@angular/platform-browser": "~21.1.0",
|
"@angular/platform-browser": "~21.1.0",
|
||||||
"@angular/router": "~21.1.0",
|
"@angular/router": "~21.1.0",
|
||||||
|
"@babylonjs/core": "^8.50.5",
|
||||||
"@ngx-translate/core": "~17.0.0",
|
"@ngx-translate/core": "~17.0.0",
|
||||||
"@ngx-translate/http-loader": "~17.0.0",
|
"@ngx-translate/http-loader": "~17.0.0",
|
||||||
"babylonjs": "^8.50.5",
|
|
||||||
"inquirer": "^13.2.2",
|
"inquirer": "^13.2.2",
|
||||||
"rxjs": "~7.8.2",
|
"rxjs": "~7.8.2",
|
||||||
"swiper": "~12.1.0",
|
"swiper": "~12.1.0",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const routes: Routes = [
|
|||||||
{ path: RouterConstants.IMPRINT.PATH, component: RouterConstants.IMPRINT.COMPONENT},
|
{ path: RouterConstants.IMPRINT.PATH, component: RouterConstants.IMPRINT.COMPONENT},
|
||||||
{ 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}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {SortingComponent} from '../pages/algorithms/sorting/sorting.component';
|
|||||||
import {ConwayGolComponent} from '../pages/algorithms/conway-gol/conway-gol.component';
|
import {ConwayGolComponent} from '../pages/algorithms/conway-gol/conway-gol.component';
|
||||||
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';
|
||||||
|
|
||||||
export class RouterConstants {
|
export class RouterConstants {
|
||||||
|
|
||||||
@@ -58,6 +59,12 @@ export class RouterConstants {
|
|||||||
COMPONENT: FractalComponent
|
COMPONENT: FractalComponent
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static readonly FRACTAL3d = {
|
||||||
|
PATH: 'algorithms/fractal3d',
|
||||||
|
LINK: '/algorithms/fractal3d',
|
||||||
|
COMPONENT: Fractal3dComponent
|
||||||
|
};
|
||||||
|
|
||||||
static readonly IMPRINT = {
|
static readonly IMPRINT = {
|
||||||
PATH: 'imprint',
|
PATH: 'imprint',
|
||||||
LINK: '/imprint',
|
LINK: '/imprint',
|
||||||
|
|||||||
@@ -1,14 +1,25 @@
|
|||||||
const mandelbulbFragmentShader = `
|
export const MANDELBULB_VERTEX = /* glsl */`
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
attribute vec3 position;
|
||||||
|
attribute vec2 uv;
|
||||||
|
varying vec2 vUV;
|
||||||
|
|
||||||
// Uniforms (Werte von der CPU)
|
void main(void) {
|
||||||
|
gl_Position = vec4(position, 1.0);
|
||||||
|
vUV = uv;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MANDELBULB_FRAGMENT = /* glsl */`
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
// Uniforms
|
||||||
uniform float time;
|
uniform float time;
|
||||||
uniform vec2 resolution;
|
uniform vec2 resolution;
|
||||||
uniform vec3 cameraPosition;
|
uniform vec3 cameraPosition;
|
||||||
uniform vec3 targetPosition;
|
uniform vec3 targetPosition;
|
||||||
uniform float power; // Die "Potenz" des Fraktals (z.B. 8.0)
|
uniform float power;
|
||||||
|
|
||||||
// Hilfsfunktion: Rotation
|
|
||||||
mat2 rot(float a) {
|
mat2 rot(float a) {
|
||||||
float s = sin(a), c = cos(a);
|
float s = sin(a), c = cos(a);
|
||||||
return mat2(c, -s, s, c);
|
return mat2(c, -s, s, c);
|
||||||
@@ -20,26 +31,21 @@ float map(vec3 pos) {
|
|||||||
float dr = 1.0;
|
float dr = 1.0;
|
||||||
float r = 0.0;
|
float r = 0.0;
|
||||||
|
|
||||||
// Die Fraktal-Iteration
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
r = length(z);
|
r = length(z);
|
||||||
if (r > 2.0) break;
|
if (r > 2.0) break;
|
||||||
|
|
||||||
// Konvertierung in Polarkoordinaten
|
|
||||||
float theta = acos(z.y / r);
|
float theta = acos(z.y / r);
|
||||||
float phi = atan(z.z, z.x);
|
float phi = atan(z.z, z.x);
|
||||||
|
|
||||||
dr = pow(r, power - 1.0) * power * dr + 1.0;
|
dr = pow(r, power - 1.0) * power * dr + 1.0;
|
||||||
|
|
||||||
// Skalieren und Rotieren (Power)
|
|
||||||
float zr = pow(r, power);
|
float zr = pow(r, power);
|
||||||
theta = theta * power;
|
theta = theta * power;
|
||||||
phi = phi * power;
|
phi = phi * power;
|
||||||
|
|
||||||
// Zurück zu Kartesischen Koordinaten
|
|
||||||
z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
|
z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
|
||||||
|
|
||||||
// Den ursprünglichen Punkt addieren (wie beim Mandelbrot z = z^2 + c)
|
|
||||||
z += pos;
|
z += pos;
|
||||||
}
|
}
|
||||||
return 0.5 * log(r) * r / dr;
|
return 0.5 * log(r) * r / dr;
|
||||||
@@ -51,14 +57,13 @@ float raymarch(vec3 ro, vec3 rd) {
|
|||||||
for(int i = 0; i < 100; i++) {
|
for(int i = 0; i < 100; i++) {
|
||||||
vec3 pos = ro + t * rd;
|
vec3 pos = ro + t * rd;
|
||||||
float d = map(pos);
|
float d = map(pos);
|
||||||
if(d < 0.001) return t; // Getroffen!
|
if(d < 0.001) return t;
|
||||||
if(t > 10.0) break; // Zu weit weg
|
if(t > 10.0) break;
|
||||||
t += d;
|
t += d;
|
||||||
}
|
}
|
||||||
return -1.0; // Nichts getroffen
|
return -1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Normalenberechnung für Licht ---
|
|
||||||
vec3 getNormal(vec3 p) {
|
vec3 getNormal(vec3 p) {
|
||||||
float d = map(p);
|
float d = map(p);
|
||||||
vec2 e = vec2(0.001, 0.0);
|
vec2 e = vec2(0.001, 0.0);
|
||||||
@@ -70,10 +75,8 @@ vec3 getNormal(vec3 p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
// UV Koordinaten zentrieren (-1 bis 1) und Aspekt korrigieren
|
|
||||||
vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
|
vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
|
||||||
|
|
||||||
// Kamera Setup (LookAt)
|
|
||||||
vec3 ro = cameraPosition; // Ray Origin
|
vec3 ro = cameraPosition; // Ray Origin
|
||||||
vec3 ta = targetPosition; // Target LookAt
|
vec3 ta = targetPosition; // Target LookAt
|
||||||
|
|
||||||
@@ -83,21 +86,19 @@ void main(void) {
|
|||||||
|
|
||||||
vec3 rd = normalize(fwd + uv.x * right + uv.y * up); // Ray Direction
|
vec3 rd = normalize(fwd + uv.x * right + uv.y * up); // Ray Direction
|
||||||
|
|
||||||
// Rendern
|
|
||||||
float t = raymarch(ro, rd);
|
float t = raymarch(ro, rd);
|
||||||
|
|
||||||
vec3 color = vec3(0.0); // Hintergrund Schwarz
|
vec3 color = vec3(0.0);
|
||||||
|
|
||||||
if(t > 0.0) {
|
if(t > 0.0) {
|
||||||
vec3 pos = ro + t * rd;
|
vec3 pos = ro + t * rd;
|
||||||
vec3 nor = getNormal(pos);
|
vec3 nor = getNormal(pos);
|
||||||
|
|
||||||
// Einfaches Licht (Phong)
|
//easy phong light
|
||||||
vec3 lightDir = normalize(vec3(1.0, 1.0, -1.0));
|
vec3 lightDir = normalize(vec3(1.0, 1.0, -1.0));
|
||||||
float diff = max(dot(nor, lightDir), 0.0);
|
float diff = max(dot(nor, lightDir), 0.0);
|
||||||
float amb = 0.2; // Ambient
|
float amb = 0.2; // Ambient
|
||||||
|
|
||||||
// Farbe basierend auf Position (gibt diesen Alien-Look)
|
|
||||||
vec3 baseColor = vec3(0.5) + 0.5 * cos(vec3(0.0, 0.4, 0.8) + length(pos) * 2.0);
|
vec3 baseColor = vec3(0.5) + 0.5 * cos(vec3(0.0, 0.4, 0.8) + length(pos) * 2.0);
|
||||||
|
|
||||||
color = baseColor * (diff + amb);
|
color = baseColor * (diff + amb);
|
||||||
@@ -1 +1,3 @@
|
|||||||
<p>fractal3d works!</p>
|
<div class="canvas-container">
|
||||||
|
<canvas #renderCanvas></canvas>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
.canvas-container { width: 100%; height: 600px; overflow: hidden; }
|
||||||
|
canvas { width: 100%; height: 100%; touch-action: none; }
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Component } from '@angular/core';
|
import {AfterViewInit, Component, ElementRef, inject, NgZone, OnDestroy, ViewChild} from '@angular/core';
|
||||||
|
import {Engine, FreeCamera, MeshBuilder, Scene, ShaderMaterial, Vector2, Vector3} from '@babylonjs/core';
|
||||||
|
import {MANDELBULB_FRAGMENT, MANDELBULB_VERTEX} from './fractal.shader';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-fractal3d',
|
selector: 'app-fractal3d',
|
||||||
@@ -6,6 +8,78 @@ import { Component } from '@angular/core';
|
|||||||
templateUrl: './fractal3d.component.html',
|
templateUrl: './fractal3d.component.html',
|
||||||
styleUrl: './fractal3d.component.scss',
|
styleUrl: './fractal3d.component.scss',
|
||||||
})
|
})
|
||||||
export class Fractal3dComponent {
|
export class Fractal3dComponent implements AfterViewInit, OnDestroy {
|
||||||
|
readonly ngZone = inject(NgZone);
|
||||||
|
|
||||||
|
@ViewChild('renderCanvas') canvasRef!: ElementRef<HTMLCanvasElement>;
|
||||||
|
|
||||||
|
private engine!: Engine;
|
||||||
|
private scene!: Scene;
|
||||||
|
private shaderMaterial!: ShaderMaterial;
|
||||||
|
|
||||||
|
private cameraPos = new Vector3(0, 0, -3.5);
|
||||||
|
private targetPos = new Vector3(0, 0, 0);
|
||||||
|
private fractalPower = 8.0;
|
||||||
|
private time = 0;
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.ngZone.runOutsideAngular(() => {
|
||||||
|
this.initBabylon();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private initBabylon(): void {
|
||||||
|
const canvas = this.canvasRef.nativeElement;
|
||||||
|
this.engine = new Engine(canvas, true);
|
||||||
|
this.scene = new Scene(this.engine);
|
||||||
|
|
||||||
|
const camera = new FreeCamera("camera1", new Vector3(0, 0, -1), this.scene);
|
||||||
|
camera.setTarget(Vector3.Zero());
|
||||||
|
|
||||||
|
const plane = MeshBuilder.CreatePlane("plane", { size: 2 }, this.scene);
|
||||||
|
|
||||||
|
this.shaderMaterial = new ShaderMaterial(
|
||||||
|
"mandelbulbShader",
|
||||||
|
this.scene,
|
||||||
|
{
|
||||||
|
vertexSource: MANDELBULB_VERTEX,
|
||||||
|
fragmentSource: MANDELBULB_FRAGMENT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributes: ["position", "uv"],
|
||||||
|
uniforms: ["time", "resolution", "cameraPosition", "targetPosition", "power"]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
plane.material = this.shaderMaterial;
|
||||||
|
|
||||||
|
this.engine.runRenderLoop(() => {
|
||||||
|
this.time += 0.01;
|
||||||
|
this.updateShaderUniforms(canvas);
|
||||||
|
this.scene.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => this.engine.resize());
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateShaderUniforms(canvas: HTMLCanvasElement): void {
|
||||||
|
if (!this.shaderMaterial) return;
|
||||||
|
|
||||||
|
this.shaderMaterial.setFloat("time", this.time);
|
||||||
|
this.shaderMaterial.setVector2("resolution", new Vector2(canvas.width, canvas.height));
|
||||||
|
this.shaderMaterial.setVector3("cameraPosition", this.cameraPos);
|
||||||
|
this.shaderMaterial.setVector3("targetPosition", this.targetPos);
|
||||||
|
this.shaderMaterial.setFloat("power", this.fractalPower);
|
||||||
|
|
||||||
|
// Optional: Hier könnte man cameraPos basierend auf Mausbewegung ändern (Orbiting)
|
||||||
|
// z.B.:
|
||||||
|
// this.cameraPos.x = Math.sin(this.time * 0.5) * 3.5;
|
||||||
|
// this.cameraPos.z = Math.cos(this.time * 0.5) * 3.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.engine) {
|
||||||
|
this.engine.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ export class AlgorithmsService {
|
|||||||
title: 'ALGORITHM.FRACTAL.TITLE',
|
title: 'ALGORITHM.FRACTAL.TITLE',
|
||||||
description: 'ALGORITHM.FRACTAL.DESCRIPTION',
|
description: 'ALGORITHM.FRACTAL.DESCRIPTION',
|
||||||
routerLink: RouterConstants.FRACTAL.LINK
|
routerLink: RouterConstants.FRACTAL.LINK
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fractal3d',
|
||||||
|
title: 'ALGORITHM.FRACTAL3D.TITLE',
|
||||||
|
description: 'ALGORITHM.FRACTAL3D.DESCRIPTION',
|
||||||
|
routerLink: RouterConstants.FRACTAL3d.LINK
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -417,6 +417,10 @@
|
|||||||
"TITLE": "Fraktale",
|
"TITLE": "Fraktale",
|
||||||
"DESCRIPTION": "Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)."
|
"DESCRIPTION": "Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)."
|
||||||
},
|
},
|
||||||
|
"FRACTAL3D": {
|
||||||
|
"TITLE": "Fraktale 3D",
|
||||||
|
"DESCRIPTION": "3D-Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)."
|
||||||
|
},
|
||||||
"NOTE": "HINWEIS",
|
"NOTE": "HINWEIS",
|
||||||
"GRID_HEIGHT": "Höhe",
|
"GRID_HEIGHT": "Höhe",
|
||||||
"GRID_WIDTH": "Beite"
|
"GRID_WIDTH": "Beite"
|
||||||
|
|||||||
@@ -417,6 +417,10 @@
|
|||||||
"TITLE": "Fractals",
|
"TITLE": "Fractals",
|
||||||
"DESCRIPTION": "Visualisation of complex geometric patterns that resemble each other on increasingly smaller scales (self-similarity)."
|
"DESCRIPTION": "Visualisation of complex geometric patterns that resemble each other on increasingly smaller scales (self-similarity)."
|
||||||
},
|
},
|
||||||
|
"FRACTAL3D": {
|
||||||
|
"TITLE": "Fractals 3D",
|
||||||
|
"DESCRIPTION": "3D Visualisation of complex geometric patterns that resemble each other on increasingly smaller scales (self-similarity)."
|
||||||
|
},
|
||||||
"NOTE": "Note",
|
"NOTE": "Note",
|
||||||
"GRID_HEIGHT": "Height",
|
"GRID_HEIGHT": "Height",
|
||||||
"GRID_WIDTH": "Width"
|
"GRID_WIDTH": "Width"
|
||||||
|
|||||||
Reference in New Issue
Block a user