Kleinian Inversion Fractal
Video Lecture
Section | Video Links |
---|---|
Kleinian Inversion Fractal | ![]() ![]() |
(Pay Per View)
You can use PayPal to purchase a one time viewing of this video for $1.49 USD.
Description
Also known as a Kleinian/Apollonian fractal.
These fractals are generated by repeatedly reflecting spheres inside other spheres to produce intricate nested spherical patterns.
-
Kleinian : A distance-estimated 3D fractal made from repeated inversions in spheres (and sometimes planes).
-
Apollonian : Refers to Apollonius of Perga (ancient Greek mathematician) and typically means a construction where circles (or spheres in 3D) are packed so that every gap is filled with more circles/spheres.
Working Example
Kleinian Inversion Algorithm
- Pass in a position coordinate.
- Initialise a
scale
factor that will track how much the space has been inverted (scaled) through iterations. - Initialise
minRadius
to a very large number. It will store the smallest radius encountered during the iterations. - Iterate items 5-11 until finished.
- Compute the
radiusSquared
distance of the position from the origin. - Checks if the point lies inside the inversion sphere.
- Compute the inversion factor
k
based on the Kleinian inversion formula. This effectively maps points inside the sphere to outside and vice versa. - Scales (invert) the position using the computed factor
k
. - Accumulate the
scale
transformation applied so far (needed for final distance calculation). - Perform box folding which mirrors the space at the box boundaries to create repeating fractal structure.
- Updates
minRadius
to store the smallest radius encountered across all inversions. - Return the distance adjusted by
scale
to account for the transformations, and multiplied by 0.5 for normalisation.
The combination of inversion plus folding creates the recursive structure typical of a Kleinian Inversion fractal.
Start Scripts
./src/SDFScene.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
|
./src/Kleinian.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
|
./src/main.ts
import './style.css'
import * as THREE from 'three/webgpu'
import { uniform } from 'three/tsl'
import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js'
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
import adjustDPR from './AdaptiveDPR'
import SDFScene from './SDFScene'
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
53,
window.innerWidth / window.innerHeight,
0.1,
10
)
camera.position.set(1, 1, 2.25)
const renderer = new THREE.WebGPURenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
renderer.setAnimationLoop(animate)
renderer.setPixelRatio(0.25) // start low for slower cards
window.addEventListener('resize', function () {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
})
const controls = new FirstPersonControls(camera, renderer.domElement)
controls.movementSpeed = 0.25
controls.lookSpeed = 0.2
controls.activeLook = false
renderer.domElement.addEventListener('mousedown', function () {
controls.activeLook = true
})
renderer.domElement.addEventListener('mouseup', function () {
controls.activeLook = false
})
const gui = new GUI()
SDFScene.setGUI(gui)
const camPos = uniform(new THREE.Vector3())
scene.backgroundNode = SDFScene.render(camPos.toVar())
const clock = new THREE.Clock()
let delta = 0
function animate() {
delta = clock.getDelta()
adjustDPR(renderer, delta) //Adaptive DPR
controls.update(delta)
camPos.value.copy(camera.position)
renderer.render(scene, camera)
}
Fog Settings
Property | Value |
---|---|
Amount | -0.001 |