Finally added 3d fractals
This commit is contained in:
@@ -14,4 +14,7 @@
|
|||||||
static readonly JULIA_WIKI = 'https://de.wikipedia.org/wiki/Julia-Menge'
|
static readonly JULIA_WIKI = 'https://de.wikipedia.org/wiki/Julia-Menge'
|
||||||
static readonly NEWTON_FRACTAL_WIKI = 'https://de.wikipedia.org/wiki/Newtonfraktal'
|
static readonly NEWTON_FRACTAL_WIKI = 'https://de.wikipedia.org/wiki/Newtonfraktal'
|
||||||
static readonly BURNING_SHIP_WIKI = 'https://de.wikipedia.org/wiki/Burning_ship_(Fraktal)'
|
static readonly BURNING_SHIP_WIKI = 'https://de.wikipedia.org/wiki/Burning_ship_(Fraktal)'
|
||||||
|
static readonly MANDELBULB_WIKI = 'https://de.wikipedia.org/wiki/Mandelknolle'
|
||||||
|
static readonly MANDELBOX_WIKI = 'https://de.wikipedia.org/wiki/Mandelbox'
|
||||||
|
static readonly JULIA3D_WIKI = 'https://de.wikipedia.org/wiki/Mandelknolle'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,97 +13,208 @@
|
|||||||
export const MANDELBULB_FRAGMENT = /* glsl */`
|
export const MANDELBULB_FRAGMENT = /* glsl */`
|
||||||
precision highp float;
|
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;
|
||||||
uniform float power;
|
uniform int fractalType; // 0 = Bulb, 1 = Box, 2 = Julia
|
||||||
|
|
||||||
mat2 rot(float a) {
|
// --- Palettes ---
|
||||||
float s = sin(a), c = cos(a);
|
vec3 palette( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ) {
|
||||||
return mat2(c, -s, s, c);
|
return a + b*cos( 6.28318*(c*t+d) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Distance Estimator (Mandelbulb) ---
|
// Global trap for coloring
|
||||||
float map(vec3 pos) {
|
float minTrap = 1000.0;
|
||||||
vec3 z = pos;
|
|
||||||
float dr = 1.0;
|
|
||||||
float r = 0.0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
// --- Shape 1: Mandelbulb ---
|
||||||
r = length(z);
|
float mapMandelbulb(vec3 pos, out float trap) {
|
||||||
if (r > 2.0) break;
|
vec3 z = pos;
|
||||||
|
float dr = 1.0;
|
||||||
|
float r = 0.0;
|
||||||
|
trap = 1000.0;
|
||||||
|
|
||||||
float theta = acos(z.y / r);
|
for (int i = 0; i < 8; i++) {
|
||||||
float phi = atan(z.z, z.x);
|
r = length(z);
|
||||||
|
if (r > 100.0) break;
|
||||||
|
trap = min(trap, r);
|
||||||
|
|
||||||
dr = pow(r, power - 1.0) * power * dr + 1.0;
|
float theta = acos(z.y / r);
|
||||||
|
float phi = atan(z.z, z.x);
|
||||||
|
dr = pow(r, power - 1.0) * power * dr + 1.0;
|
||||||
|
float zr = pow(r, power);
|
||||||
|
theta = theta * power;
|
||||||
|
phi = phi * power;
|
||||||
|
z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
|
||||||
|
z += pos;
|
||||||
|
}
|
||||||
|
return 0.5 * log(r) * r / dr;
|
||||||
|
}
|
||||||
|
|
||||||
float zr = pow(r, power);
|
// --- Shape 2: Mandelbox ---
|
||||||
theta = theta * power;
|
float mapMandelbox(vec3 pos, out float trap) {
|
||||||
phi = phi * power;
|
vec3 z = pos;
|
||||||
|
float dr = 1.0;
|
||||||
|
float scale = 2.8; // Fixed scale for good look
|
||||||
|
trap = 1000.0;
|
||||||
|
|
||||||
z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
|
for (int i = 0; i < 15; i++) {
|
||||||
|
// Box fold
|
||||||
|
z = clamp(z, -1.0, 1.0) * 2.0 - z;
|
||||||
|
|
||||||
z += pos;
|
// Sphere fold
|
||||||
|
float r2 = dot(z, z);
|
||||||
|
trap = min(trap, r2); // Trap based on sphere fold
|
||||||
|
|
||||||
|
if (r2 < 0.25) {
|
||||||
|
z = z * 4.0;
|
||||||
|
dr = dr * 4.0;
|
||||||
|
} else if (r2 < 1.0) {
|
||||||
|
z = z / r2;
|
||||||
|
dr = dr / r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
z = z * scale + pos;
|
||||||
|
dr = dr * abs(scale) + 1.0;
|
||||||
|
}
|
||||||
|
return (length(z) - abs(scale - 1.0)) / dr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Shape 3: Julia Bulb ---
|
||||||
|
float mapJulia(vec3 pos, out float trap) {
|
||||||
|
vec3 z = pos;
|
||||||
|
float dr = 1.0;
|
||||||
|
float r = 0.0;
|
||||||
|
trap = 1000.0;
|
||||||
|
|
||||||
|
// Constant C for Julia set (animating slightly makes it alive)
|
||||||
|
vec3 c = vec3(0.35, 0.45, -0.1) + vec3(sin(time*0.1)*0.2);
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
r = length(z);
|
||||||
|
if (r > 100.0) break; // Higher escape radius for Julia
|
||||||
|
trap = min(trap, r);
|
||||||
|
|
||||||
|
float theta = acos(z.y / r);
|
||||||
|
float phi = atan(z.z, z.x);
|
||||||
|
dr = pow(r, power - 1.0) * power * dr + 1.0;
|
||||||
|
float zr = pow(r, power);
|
||||||
|
theta = theta * power;
|
||||||
|
phi = phi * power;
|
||||||
|
z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
|
||||||
|
z += c; // Add C instead of pos
|
||||||
|
}
|
||||||
|
return 0.5 * log(r) * r / dr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main Map Dispatcher ---
|
||||||
|
float map(vec3 pos) {
|
||||||
|
float d = 0.0;
|
||||||
|
float currentTrap = 0.0;
|
||||||
|
|
||||||
|
if (fractalType == 1) {
|
||||||
|
d = mapMandelbox(pos, currentTrap);
|
||||||
|
} else if (fractalType == 2) {
|
||||||
|
d = mapJulia(pos, currentTrap);
|
||||||
|
} else {
|
||||||
|
d = mapMandelbulb(pos, currentTrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
minTrap = currentTrap; // Update global
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Raymarching ---
|
||||||
|
bool intersectSphere(vec3 ro, vec3 rd, vec3 c, float r, out float t0, out float t1) {
|
||||||
|
vec3 oc = ro - c;
|
||||||
|
float b = dot(oc, rd);
|
||||||
|
float c2 = dot(oc, oc) - r * r;
|
||||||
|
float h = b*b - c2;
|
||||||
|
if (h < 0.0) return false;
|
||||||
|
h = sqrt(h);
|
||||||
|
t0 = -b - h;
|
||||||
|
t1 = -b + h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float raymarch(vec3 ro, vec3 rd) {
|
||||||
|
// Bounding sphere around fractal center (here: origin)
|
||||||
|
vec3 center = vec3(0.0);
|
||||||
|
float radius = 6.0;
|
||||||
|
|
||||||
|
float tEnter, tExit;
|
||||||
|
if (!intersectSphere(ro, rd, center, radius, tEnter, tExit)) {
|
||||||
|
return -1.0;
|
||||||
}
|
}
|
||||||
return 0.5 * log(r) * r / dr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Raymarching ---
|
float t = max(tEnter, 0.0);
|
||||||
float raymarch(vec3 ro, vec3 rd) {
|
float tMax = tExit;
|
||||||
float t = 0.0;
|
|
||||||
for(int i = 0; i < 100; i++) {
|
for (int i = 0; i < 128; 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;
|
|
||||||
if(t > 10.0) break;
|
// distance-based epsilon is more stable for zoom-out
|
||||||
t += d;
|
float eps = max(0.001, 0.0005 * t);
|
||||||
|
|
||||||
|
if (d < eps) return t;
|
||||||
|
t += d * 0.8; // safety factor against overshoot
|
||||||
|
if (t > tMax) break;
|
||||||
}
|
}
|
||||||
return -1.0;
|
return -1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
return normalize(vec3(
|
return normalize(vec3(
|
||||||
d - map(p - e.xyy),
|
d - map(p - e.xyy),
|
||||||
d - map(p - e.yxy),
|
d - map(p - e.yxy),
|
||||||
d - map(p - e.yyx)
|
d - map(p - e.yyx)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
|
vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
|
||||||
|
vec3 ro = cameraPosition;
|
||||||
|
vec3 ta = targetPosition;
|
||||||
|
|
||||||
vec3 ro = cameraPosition; // Ray Origin
|
vec3 fwd = normalize(ta - ro);
|
||||||
vec3 ta = targetPosition; // Target LookAt
|
vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), fwd));
|
||||||
|
vec3 up = normalize(cross(fwd, right));
|
||||||
|
vec3 rd = normalize(fwd + uv.x * right + uv.y * up);
|
||||||
|
|
||||||
vec3 fwd = normalize(ta - ro);
|
vec3 color = vec3(0.1);
|
||||||
vec3 right = normalize(cross(vec3(0,1,0), fwd));
|
|
||||||
vec3 up = normalize(cross(fwd, right));
|
|
||||||
|
|
||||||
vec3 rd = normalize(fwd + uv.x * right + uv.y * up); // Ray Direction
|
float t = raymarch(ro, rd);
|
||||||
|
|
||||||
float t = raymarch(ro, rd);
|
if(t > 0.0) {
|
||||||
|
vec3 pos = ro + t * rd;
|
||||||
|
vec3 nor = getNormal(pos);
|
||||||
|
|
||||||
vec3 color = vec3(0.0);
|
// Different colors for different shapes
|
||||||
|
vec3 colParamsA = vec3(0.5, 0.5, 0.5);
|
||||||
|
vec3 colParamsB = vec3(0.5, 0.5, 0.5);
|
||||||
|
vec3 colParamsC = vec3(1.0, 1.0, 1.0);
|
||||||
|
vec3 colParamsD = vec3(0.80, 0.90, 0.30);
|
||||||
|
|
||||||
if(t > 0.0) {
|
if (fractalType == 1) { // Box: Sci-Fi Blue/Grey
|
||||||
vec3 pos = ro + t * rd;
|
colParamsD = vec3(0.0, 0.1, 0.2);
|
||||||
vec3 nor = getNormal(pos);
|
}
|
||||||
|
if (fractalType == 2) { // Julia: Alien Green/Purple
|
||||||
|
colParamsD = vec3(0.8, 0.0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
//easy phong light
|
vec3 materialColor = palette(minTrap, colParamsA, colParamsB, colParamsC, colParamsD);
|
||||||
vec3 lightDir = normalize(vec3(1.0, 1.0, -1.0));
|
|
||||||
float diff = max(dot(nor, lightDir), 0.0);
|
|
||||||
float amb = 0.2; // Ambient
|
|
||||||
|
|
||||||
vec3 baseColor = vec3(0.5) + 0.5 * cos(vec3(0.0, 0.4, 0.8) + length(pos) * 2.0);
|
float camLight = max(0.0, dot(nor, -rd));
|
||||||
|
float ambient = 0.4;
|
||||||
|
vec3 lighting = vec3(1.0) * (camLight * 0.7 + ambient);
|
||||||
|
color = materialColor * lighting;
|
||||||
|
}
|
||||||
|
|
||||||
color = baseColor * (diff + amb);
|
color = pow(color, vec3(0.8));
|
||||||
}
|
gl_FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
gl_FragColor = vec4(color, 1.0);
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
<div class="canvas-container">
|
<mat-card class="container">
|
||||||
<canvas #renderCanvas></canvas>
|
<mat-card-header>
|
||||||
</div>
|
<mat-card-title>{{ 'FRACTAL3D.TITLE' | translate }}</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<app-information [algorithmInformation]="algoInformation"/>
|
||||||
|
<div class="controls-container">
|
||||||
|
<div class="controls-panel">
|
||||||
|
<button matButton="filled" (click)="onFractalTypeChange(0)">{{ 'FRACTAL3D.MANDELBULB' | translate }}</button>
|
||||||
|
<button matButton="filled" (click)="onFractalTypeChange(1)">{{ 'FRACTAL3D.MANDELBOX' | translate }}</button>
|
||||||
|
<button matButton="filled" (click)="onFractalTypeChange(2)">{{ 'FRACTAL3D.JULIA' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="canvas-container">
|
||||||
|
<canvas #renderCanvas></canvas>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
.canvas-container { width: 100%; height: 600px; overflow: hidden; }
|
.canvas-container { width: 100%; height: 1000px; }
|
||||||
canvas { width: 100%; height: 100%; touch-action: none; }
|
canvas { width: 100%; height: 100%; touch-action: none; border-width: 0; border-color: transparent; border-style: hidden; }
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
import {AfterViewInit, Component, ElementRef, inject, NgZone, OnDestroy, ViewChild} 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 {ArcRotateCamera, Engine, MeshBuilder, Scene, ShaderMaterial, Vector2, Vector3} from '@babylonjs/core';
|
||||||
import {MANDELBULB_FRAGMENT, MANDELBULB_VERTEX} from './fractal.shader';
|
import {MANDELBULB_FRAGMENT, MANDELBULB_VERTEX} from './fractal.shader';
|
||||||
|
import {Information} from '../information/information';
|
||||||
|
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
|
||||||
|
import {TranslatePipe} from '@ngx-translate/core';
|
||||||
|
import {AlgorithmInformation} from '../information/information.models';
|
||||||
|
import {UrlConstants} from '../../../constants/UrlConstants';
|
||||||
|
import {MatButton} from '@angular/material/button';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-fractal3d',
|
selector: 'app-fractal3d',
|
||||||
imports: [],
|
imports: [
|
||||||
|
Information,
|
||||||
|
MatCard,
|
||||||
|
MatCardContent,
|
||||||
|
MatCardHeader,
|
||||||
|
MatCardTitle,
|
||||||
|
TranslatePipe,
|
||||||
|
MatButton
|
||||||
|
],
|
||||||
templateUrl: './fractal3d.component.html',
|
templateUrl: './fractal3d.component.html',
|
||||||
styleUrl: './fractal3d.component.scss',
|
styleUrl: './fractal3d.component.scss',
|
||||||
})
|
})
|
||||||
@@ -13,14 +27,39 @@ export class Fractal3dComponent implements AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
@ViewChild('renderCanvas') canvasRef!: ElementRef<HTMLCanvasElement>;
|
@ViewChild('renderCanvas') canvasRef!: ElementRef<HTMLCanvasElement>;
|
||||||
|
|
||||||
|
algoInformation: AlgorithmInformation = {
|
||||||
|
title: 'FRACTAL3D.EXPLANATION.TITLE',
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
name: 'Mandel-Bulb',
|
||||||
|
description: 'FRACTAL3D.EXPLANATION.MANDELBULB_EXPLANATION',
|
||||||
|
link: UrlConstants.MANDELBULB_WIKI
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Mandelbox',
|
||||||
|
description: 'FRACTAL3D.EXPLANATION.MANDELBOX_EXPLANATION',
|
||||||
|
link: UrlConstants.MANDELBOX_WIKI
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Julia-Bulb',
|
||||||
|
description: 'FRACTAL3D.EXPLANATION.JULIA_EXPLANATION',
|
||||||
|
link: UrlConstants.JULIA3D_WIKI
|
||||||
|
}
|
||||||
|
],
|
||||||
|
disclaimer: 'FRACTAL3D.EXPLANATION.DISCLAIMER',
|
||||||
|
disclaimerBottom: '',
|
||||||
|
disclaimerListEntry: ['FRACTAL3D.EXPLANATION.DISCLAIMER_1', 'FRACTAL3D.EXPLANATION.DISCLAIMER_2', 'FRACTAL3D.EXPLANATION.DISCLAIMER_3', 'FRACTAL3D.EXPLANATION.DISCLAIMER_4']
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly fractalPower = 8;
|
||||||
private engine!: Engine;
|
private engine!: Engine;
|
||||||
private scene!: Scene;
|
private scene!: Scene;
|
||||||
private shaderMaterial!: ShaderMaterial;
|
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;
|
private time = 0;
|
||||||
|
private triggerCamUpdate = false;
|
||||||
|
private cameraPosition: number = 3.5;
|
||||||
|
public currentFractalType = 0;
|
||||||
|
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.ngZone.runOutsideAngular(() => {
|
this.ngZone.runOutsideAngular(() => {
|
||||||
@@ -33,10 +72,18 @@ export class Fractal3dComponent implements AfterViewInit, OnDestroy {
|
|||||||
this.engine = new Engine(canvas, true);
|
this.engine = new Engine(canvas, true);
|
||||||
this.scene = new Scene(this.engine);
|
this.scene = new Scene(this.engine);
|
||||||
|
|
||||||
const camera = new FreeCamera("camera1", new Vector3(0, 0, -1), this.scene);
|
const camera = new ArcRotateCamera("Camera", 0, Math.PI / 2, 4, Vector3.Zero(), this.scene);
|
||||||
camera.setTarget(Vector3.Zero());
|
camera.wheelPrecision = 100;
|
||||||
|
camera.minZ = 0.1;
|
||||||
|
camera.maxZ = 100;
|
||||||
|
camera.lowerRadiusLimit = 1.5;
|
||||||
|
camera.upperRadiusLimit = 20;
|
||||||
|
camera.attachControl(this.canvasRef.nativeElement, true);
|
||||||
|
|
||||||
const plane = MeshBuilder.CreatePlane("plane", { size: 2 }, this.scene);
|
const plane = MeshBuilder.CreatePlane("plane", { size: 10 }, this.scene);
|
||||||
|
plane.parent = camera;
|
||||||
|
plane.position.z = 1;
|
||||||
|
plane.alwaysSelectAsActiveMesh = true;
|
||||||
|
|
||||||
this.shaderMaterial = new ShaderMaterial(
|
this.shaderMaterial = new ShaderMaterial(
|
||||||
"mandelbulbShader",
|
"mandelbulbShader",
|
||||||
@@ -47,34 +94,48 @@ export class Fractal3dComponent implements AfterViewInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
attributes: ["position", "uv"],
|
attributes: ["position", "uv"],
|
||||||
uniforms: ["time", "resolution", "cameraPosition", "targetPosition", "power"]
|
uniforms: ["time", "resolution", "cameraPosition", "targetPosition", "power", "fractalType"]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
this.shaderMaterial.disableDepthWrite = true;
|
||||||
|
this.shaderMaterial.backFaceCulling = false;
|
||||||
|
|
||||||
plane.material = this.shaderMaterial;
|
plane.material = this.shaderMaterial;
|
||||||
|
|
||||||
this.engine.runRenderLoop(() => {
|
this.engine.runRenderLoop(() => {
|
||||||
this.time += 0.01;
|
this.time += 0.005;
|
||||||
this.updateShaderUniforms(canvas);
|
|
||||||
|
if (this.triggerCamUpdate)
|
||||||
|
{
|
||||||
|
this.triggerCamUpdate = false;
|
||||||
|
camera.radius = this.cameraPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.shaderMaterial) {
|
||||||
|
this.shaderMaterial.setFloat("time", this.time);
|
||||||
|
this.shaderMaterial.setVector2("resolution", new Vector2(canvas.width, canvas.height));
|
||||||
|
this.shaderMaterial.setVector3("cameraPosition", camera.position);
|
||||||
|
this.shaderMaterial.setVector3("targetPosition", camera.target);
|
||||||
|
this.shaderMaterial.setFloat("power", this.fractalPower);
|
||||||
|
this.shaderMaterial.setInt("fractalType", this.currentFractalType);
|
||||||
|
}
|
||||||
|
|
||||||
this.scene.render();
|
this.scene.render();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('resize', () => this.engine.resize());
|
window.addEventListener('resize', () => this.engine.resize());
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateShaderUniforms(canvas: HTMLCanvasElement): void {
|
onFractalTypeChange(type: number): void {
|
||||||
if (!this.shaderMaterial) return;
|
this.currentFractalType = type;
|
||||||
|
if (type === 0 ||type === 2)
|
||||||
this.shaderMaterial.setFloat("time", this.time);
|
{
|
||||||
this.shaderMaterial.setVector2("resolution", new Vector2(canvas.width, canvas.height));
|
this.cameraPosition = 4;
|
||||||
this.shaderMaterial.setVector3("cameraPosition", this.cameraPos);
|
}
|
||||||
this.shaderMaterial.setVector3("targetPosition", this.targetPos);
|
else {
|
||||||
this.shaderMaterial.setFloat("power", this.fractalPower);
|
this.cameraPosition = 15;
|
||||||
|
}
|
||||||
// Optional: Hier könnte man cameraPos basierend auf Mausbewegung ändern (Orbiting)
|
this.triggerCamUpdate = true;
|
||||||
// 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 {
|
ngOnDestroy(): void {
|
||||||
|
|||||||
@@ -395,6 +395,24 @@
|
|||||||
"DISCLAIMER_4": "Rechenintensität: Da für jeden Pixel hunderte Berechnungen durchgeführt werden, sind Fraktale ein klassischer Benchmark für die Performance von Grafikprozessoren (GPUs)."
|
"DISCLAIMER_4": "Rechenintensität: Da für jeden Pixel hunderte Berechnungen durchgeführt werden, sind Fraktale ein klassischer Benchmark für die Performance von Grafikprozessoren (GPUs)."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"FRACTAL3D": {
|
||||||
|
"TITLE": "3D Fraktale",
|
||||||
|
"ALGORITHM": "Algorithmen",
|
||||||
|
"MANDELBULB": "Mandelbulb",
|
||||||
|
"MANDELBOX": "Mandelbox",
|
||||||
|
"JULIA": "Julia",
|
||||||
|
"EXPLANATION": {
|
||||||
|
"TITLE": "3D Fraktale Welten",
|
||||||
|
"MANDELBULB_EXPLANATION": "gilt als der 'Heilige Gral' der 3D-Fraktale. Da komplexe Zahlen nur zweidimensional sind, nutzt dieses Fraktal sphärische Koordinaten und hohe Potenzen (meist v^8 + c), um die Mandelbrot-Menge in den Raum zu projizieren. Vorteil: Es entsteht eine organische, extrem detaillierte Struktur, die an Pflanzen, Korallen oder außerirdische Landschaften erinnert.",
|
||||||
|
"MANDELBOX_EXPLANATION": "basiert nicht auf glatten Kurven, sondern auf geometrischem 'Falten' und Skalieren (Box-Folding & Sphere-Folding). Der Raum wird wie Papier immer wieder gefaltet und gespiegelt. Vorteil: Erzeugt streng geometrische, mechanisch wirkende Strukturen, die wie endlose futuristische Städte, der Borg-Würfel oder komplexe Sci-Fi-Architektur aussehen.",
|
||||||
|
"JULIA_EXPLANATION": "ist das 3D-Pendant zur 2D-Julia-Menge. Während der Mandelbulb eine 'Karte' aller Fraktale ist, fixiert man hier den Parameter 'c' und variiert den Startpunkt zudem variiert es mit der Zeit. Vorteil: Anders als der massive Mandelbulb sind Julia-Bulbs oft hohle, komplexe Tunnelsysteme oder blasenartige Strukturen, die sich perfekt eignen, um mit der Kamera hindurchzufliegen.",
|
||||||
|
"DISCLAIMER": "Diese Visualisierung nutzt eine Technik namens 'Raymarching' (Sphere Tracing). Das bedeutet:",
|
||||||
|
"DISCLAIMER_1": "Keine Polygone: Es gibt keine Dreiecke oder Gitter. Die Form wird rein mathematisch für jeden Pixel in Echtzeit berechnet.",
|
||||||
|
"DISCLAIMER_2": "Distance Estimation: Der Algorithmus 'tastet' sich mit Lichtstrahlen voran, indem er berechnet, wie weit das nächste Objekt entfernt ist, ohne es sofort zu berühren.",
|
||||||
|
"DISCLAIMER_3": "Unendliche Details: Da die Oberfläche mathematisch definiert ist, verpixelt sie nicht beim Zoom – es erscheinen immer neue Strukturen.",
|
||||||
|
"DISCLAIMER_4": "Licht & Schatten: Um die Tiefe sichtbar zu machen, werden Lichtreflexionen und Schatten (Ambient Occlusion) basierend auf der Krümmung der Formel simuliert."
|
||||||
|
}
|
||||||
|
},
|
||||||
"ALGORITHM": {
|
"ALGORITHM": {
|
||||||
"TITLE": "Algorithmen",
|
"TITLE": "Algorithmen",
|
||||||
"PATHFINDING": {
|
"PATHFINDING": {
|
||||||
|
|||||||
@@ -395,6 +395,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
"FRACTAL3D": {
|
||||||
|
"TITLE": "3D Fractals",
|
||||||
|
"ALGORITHM": "Algorithms",
|
||||||
|
"MANDELBULB": "Mandelbulb",
|
||||||
|
"MANDELBOX": "Mandelbox",
|
||||||
|
"JULIA": "Julia",
|
||||||
|
"EXPLANATION": {
|
||||||
|
"TITLE": "3D Fractal Worlds",
|
||||||
|
"MANDELBULB_EXPLANATION": "is considered the 'Holy Grail' of 3D fractals. Since complex numbers are only two-dimensional, this fractal uses spherical coordinates and high powers (usually v^8 + c) to project the Mandelbrot set into 3D space. Benefit: Creates an organic, extremely detailed structure reminiscent of plants, coral reefs, or alien landscapes.",
|
||||||
|
"MANDELBOX_EXPLANATION": "is based not on smooth curves, but on geometric 'folding' and scaling (Box-Folding & Sphere-Folding). Space is repeatedly folded and mirrored like origami. Benefit: Produces strictly geometric, mechanical-looking structures that resemble endless futuristic cities, the Borg cube, or complex sci-fi architecture.",
|
||||||
|
"JULIA_EXPLANATION": "is the 3D counterpart to the 2D Julia set. While the Mandelbulb is a 'map' of all fractals, here we fix the parameter 'c' and vary the starting point and it changes over time. Benefit: Unlike the solid Mandelbulb, Julia Bulbs are often hollow, forming complex tunnel systems or bubble-like structures perfect for flying through with the camera.",
|
||||||
|
"DISCLAIMER": "This visualization uses a technique called 'Raymarching' (Sphere Tracing). This means:",
|
||||||
|
"DISCLAIMER_1": "No Polygons: There are no triangles or meshes. The shape is calculated mathematically for every pixel in real-time.",
|
||||||
|
"DISCLAIMER_2": "Distance Estimation: The algorithm 'marches' light rays forward by calculating the safe distance to the nearest object without hitting it immediately.",
|
||||||
|
"DISCLAIMER_3": "Infinite Detail: Since the surface is mathematically defined, it never pixelates when zooming in – new structures always emerge.",
|
||||||
|
"DISCLAIMER_4": "Light & Shadow: To visualize depth, light reflections and shadows (Ambient Occlusion) are simulated based on the curvature of the formula."
|
||||||
|
}
|
||||||
|
},
|
||||||
"ALGORITHM": {
|
"ALGORITHM": {
|
||||||
"TITLE": "Algorithms",
|
"TITLE": "Algorithms",
|
||||||
"PATHFINDING": {
|
"PATHFINDING": {
|
||||||
|
|||||||
Reference in New Issue
Block a user