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/platform-browser": "~21.1.0",
|
||||
"@angular/router": "~21.1.0",
|
||||
"@babylonjs/core": "^8.50.5",
|
||||
"@ngx-translate/core": "~17.0.0",
|
||||
"@ngx-translate/http-loader": "~17.0.0",
|
||||
"babylonjs": "^8.50.5",
|
||||
"inquirer": "^13.2.2",
|
||||
"rxjs": "~7.8.2",
|
||||
"swiper": "~12.1.0",
|
||||
@@ -1150,6 +1150,12 @@
|
||||
"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": {
|
||||
"version": "1.8.1",
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
"@angular/material": "~21.1.0",
|
||||
"@angular/platform-browser": "~21.1.0",
|
||||
"@angular/router": "~21.1.0",
|
||||
"@babylonjs/core": "^8.50.5",
|
||||
"@ngx-translate/core": "~17.0.0",
|
||||
"@ngx-translate/http-loader": "~17.0.0",
|
||||
"babylonjs": "^8.50.5",
|
||||
"inquirer": "^13.2.2",
|
||||
"rxjs": "~7.8.2",
|
||||
"swiper": "~12.1.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ export const routes: Routes = [
|
||||
{ path: RouterConstants.IMPRINT.PATH, component: RouterConstants.IMPRINT.COMPONENT},
|
||||
{ path: RouterConstants.GOL.PATH, component: RouterConstants.GOL.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 {LabyrinthComponent} from '../pages/algorithms/pathfinding/labyrinth/labyrinth.component';
|
||||
import {FractalComponent} from '../pages/algorithms/fractal/fractal.component';
|
||||
import {Fractal3dComponent} from '../pages/algorithms/fractal3d/fractal3d.component';
|
||||
|
||||
export class RouterConstants {
|
||||
|
||||
@@ -58,6 +59,12 @@ export class RouterConstants {
|
||||
COMPONENT: FractalComponent
|
||||
};
|
||||
|
||||
static readonly FRACTAL3d = {
|
||||
PATH: 'algorithms/fractal3d',
|
||||
LINK: '/algorithms/fractal3d',
|
||||
COMPONENT: Fractal3dComponent
|
||||
};
|
||||
|
||||
static readonly IMPRINT = {
|
||||
PATH: 'imprint',
|
||||
LINK: '/imprint',
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
const mandelbulbFragmentShader = `
|
||||
precision highp float;
|
||||
export const MANDELBULB_VERTEX = /* glsl */`
|
||||
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 vec2 resolution;
|
||||
uniform vec3 cameraPosition;
|
||||
uniform vec3 targetPosition;
|
||||
uniform float power; // Die "Potenz" des Fraktals (z.B. 8.0)
|
||||
uniform float power;
|
||||
|
||||
// Hilfsfunktion: Rotation
|
||||
mat2 rot(float a) {
|
||||
float s = sin(a), c = cos(a);
|
||||
return mat2(c, -s, s, c);
|
||||
@@ -20,26 +31,21 @@ float map(vec3 pos) {
|
||||
float dr = 1.0;
|
||||
float r = 0.0;
|
||||
|
||||
// Die Fraktal-Iteration
|
||||
for (int i = 0; i < 8; i++) {
|
||||
r = length(z);
|
||||
if (r > 2.0) break;
|
||||
|
||||
// Konvertierung in Polarkoordinaten
|
||||
float theta = acos(z.y / r);
|
||||
float phi = atan(z.z, z.x);
|
||||
|
||||
dr = pow(r, power - 1.0) * power * dr + 1.0;
|
||||
|
||||
// Skalieren und Rotieren (Power)
|
||||
float zr = pow(r, power);
|
||||
theta = theta * power;
|
||||
phi = phi * power;
|
||||
|
||||
// Zurück zu Kartesischen Koordinaten
|
||||
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;
|
||||
}
|
||||
return 0.5 * log(r) * r / dr;
|
||||
@@ -51,14 +57,13 @@ float raymarch(vec3 ro, vec3 rd) {
|
||||
for(int i = 0; i < 100; i++) {
|
||||
vec3 pos = ro + t * rd;
|
||||
float d = map(pos);
|
||||
if(d < 0.001) return t; // Getroffen!
|
||||
if(t > 10.0) break; // Zu weit weg
|
||||
if(d < 0.001) return t;
|
||||
if(t > 10.0) break;
|
||||
t += d;
|
||||
}
|
||||
return -1.0; // Nichts getroffen
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
// --- Normalenberechnung für Licht ---
|
||||
vec3 getNormal(vec3 p) {
|
||||
float d = map(p);
|
||||
vec2 e = vec2(0.001, 0.0);
|
||||
@@ -70,10 +75,8 @@ vec3 getNormal(vec3 p) {
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
// UV Koordinaten zentrieren (-1 bis 1) und Aspekt korrigieren
|
||||
vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
|
||||
|
||||
// Kamera Setup (LookAt)
|
||||
vec3 ro = cameraPosition; // Ray Origin
|
||||
vec3 ta = targetPosition; // Target LookAt
|
||||
|
||||
@@ -83,21 +86,19 @@ void main(void) {
|
||||
|
||||
vec3 rd = normalize(fwd + uv.x * right + uv.y * up); // Ray Direction
|
||||
|
||||
// Rendern
|
||||
float t = raymarch(ro, rd);
|
||||
|
||||
vec3 color = vec3(0.0); // Hintergrund Schwarz
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
if(t > 0.0) {
|
||||
vec3 pos = ro + t * rd;
|
||||
vec3 nor = getNormal(pos);
|
||||
|
||||
// Einfaches Licht (Phong)
|
||||
//easy phong light
|
||||
vec3 lightDir = normalize(vec3(1.0, 1.0, -1.0));
|
||||
float diff = max(dot(nor, lightDir), 0.0);
|
||||
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);
|
||||
|
||||
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({
|
||||
selector: 'app-fractal3d',
|
||||
@@ -6,6 +8,78 @@ import { Component } from '@angular/core';
|
||||
templateUrl: './fractal3d.component.html',
|
||||
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',
|
||||
description: 'ALGORITHM.FRACTAL.DESCRIPTION',
|
||||
routerLink: RouterConstants.FRACTAL.LINK
|
||||
},
|
||||
{
|
||||
id: 'fractal3d',
|
||||
title: 'ALGORITHM.FRACTAL3D.TITLE',
|
||||
description: 'ALGORITHM.FRACTAL3D.DESCRIPTION',
|
||||
routerLink: RouterConstants.FRACTAL3d.LINK
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -417,6 +417,10 @@
|
||||
"TITLE": "Fraktale",
|
||||
"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",
|
||||
"GRID_HEIGHT": "Höhe",
|
||||
"GRID_WIDTH": "Beite"
|
||||
|
||||
@@ -417,6 +417,10 @@
|
||||
"TITLE": "Fractals",
|
||||
"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",
|
||||
"GRID_HEIGHT": "Height",
|
||||
"GRID_WIDTH": "Width"
|
||||
|
||||
Reference in New Issue
Block a user