First image of fractal

Next the nasty stuff like movement and ui :-D
This commit is contained in:
2026-02-11 08:31:27 +01:00
parent 12ebbb09ce
commit ba3dc4d928
11 changed files with 133 additions and 33 deletions

View File

@@ -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);

View File

@@ -1 +1,3 @@
<p>fractal3d works!</p>
<div class="canvas-container">
<canvas #renderCanvas></canvas>
</div>

View File

@@ -0,0 +1,2 @@
.canvas-container { width: 100%; height: 600px; overflow: hidden; }
canvas { width: 100%; height: 100%; touch-action: none; }

View File

@@ -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();
}
}
}

View File

@@ -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
}
];