Skip to content

Commit efe1a0a

Browse files
committed
Refined quaternion distance-estimator
1 parent 7801df0 commit efe1a0a

File tree

3 files changed

+81
-29
lines changed

3 files changed

+81
-29
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,27 @@ The open source library [CPAL](https://github.com/rustaudio/cpal) is used to ret
1616
### Demo
1717
##### deadmau5 Demo
1818
[![deadmau5 Demo](https://img.youtube.com/vi/UiJ_785hC60/0.jpg)](https://www.youtube.com/watch?v=UiJ_785hC60 "deadmau5 Demo")
19+
20+
### Controls
21+
| Key | Action |
22+
|:-:|----------|
23+
| **App-Window** | - |
24+
| F11 | Toggle window fullscreen |
25+
| ESC | If fullscreen, then enter windowed mode. Else, close the application |
26+
| ENTER | *Only Windows release builds:* Toggle the visibility of the output command prompt |
27+
| **Overlay-Window** | - |
28+
| F1 | Toggle visibility of this Help window |
29+
| C | Toggle visibility of the App Config window |
30+
| **Audio** | - |
31+
| R | Toggle the application's responsiveness to system audio |
32+
| **Visuals** | - |
33+
| SPACE | Toggle kaleidoscope effect on fractals |
34+
| J | Toggle 'jello' effect on particles (i.e., the fixing of particles to a position with spring tension) |
35+
| P | Toggle the rendering and updating of particles |
36+
| CAPS | Toggle negative-color effect for particles |
37+
| D | Toggle between 2D and 3D projections of the particles |
38+
| TAB | Cycle through particle color schemes. *Requires that all overlay windows are closed* |
39+
| 0 | Select the 'empty' fractal |
40+
| 1-6 | Select the fractal corresponding to the respective key |
41+
| MOUSE-BTTN | Holding the primary or secondary mouse button applies a repulsive or attractive force, respectively, at the cursor's position |
42+
| MOUSE-SCRL | Scrolling up or down changes the strength of the cursor's applied force |

shaders/ray_march.frag

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ vec3 safe_normalize(vec3 t) {
8282
}
8383
return normalize(t);
8484
}
85+
vec4 safe_normalize(vec4 t) {
86+
if(length(t) < 0.000001) {
87+
return vec4(0.0, 0.0, 0.0, 1.0);
88+
}
89+
return normalize(t);
90+
}
8591

8692
float getAngle(vec2 s) {
8793
float theta = 0.0;
@@ -112,11 +118,17 @@ float boundReflect(float x, float b) {
112118
}
113119
}
114120

121+
mat3 frame_constant_mandelbulb_colorRotato;
122+
mat3 frame_constant_klein_colorRotato;
123+
mat3 frame_constant_quaternion_colorRotato;
124+
vec4 frame_constant_quaternion_q;
125+
vec4 frame_constant_quaternion_c;
126+
115127
vec4 orbitTrap;
116128
float distanceEstimator(vec3 t) {
117129
orbitTrap = vec4(1.0, 1.0, 1.0, 1.0);
118130

119-
// Mandelbox
131+
// Mandelbox.
120132
if(runtime.distance_estimator_id == 1) {
121133
const int maxIterations = 5;
122134
const float reScale = 4.8;
@@ -152,7 +164,7 @@ float distanceEstimator(vec3 t) {
152164
}
153165
return (length(s)-BVR)/abs(DEfactor) / reScale;
154166
}
155-
// Mandelbulb
167+
// Mandelbulb.
156168
else if(runtime.distance_estimator_id == 2) {
157169
const int maxIterations = 3;
158170
const float reScale = 1.85;
@@ -162,8 +174,6 @@ float distanceEstimator(vec3 t) {
162174
float dr = 1.0;
163175
float r = 0.0;
164176

165-
mat3 colorRotato = buildRot3(safe_normalize(push.smooth_mids.xyz), 0.325*push.time);
166-
167177
for(int i = 0; i < maxIterations; i++) {
168178
r = length(s);
169179
const float b = 1.5;
@@ -180,10 +190,11 @@ float distanceEstimator(vec3 t) {
180190
s = r*vec3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta));
181191
s += t;
182192

183-
orbitTrap.xyz = min(orbitTrap.xyz, abs((s - (push.reactive_high.xyz + push.reactive_bass.xyz)/2.0) * colorRotato)/1.25);
193+
orbitTrap.xyz = min(orbitTrap.xyz, abs((s - (push.reactive_high.xyz + push.reactive_bass.xyz)/2.0) * frame_constant_mandelbulb_colorRotato)/1.25);
184194
}
185195
return min(0.5*log(r)*r/dr, 3.5) / reScale;
186196
}
197+
// Klein-inspired.
187198
else if(runtime.distance_estimator_id == 3) {
188199
const int maxIterations = 3;
189200
const float reScale = 0.8;
@@ -197,8 +208,6 @@ float distanceEstimator(vec3 t) {
197208
float st = sin(theta);
198209
mat2 rotato = mat2(ct, st, -st, ct);
199210

200-
mat3 colorRotato = buildRot3(safe_normalize(push.smooth_mids.xyz), 0.15*push.time);
201-
202211
for(int i = 0; i < maxIterations; i++) {
203212
if (i == 2) {
204213
s.xy *= rotato;
@@ -212,7 +221,7 @@ float distanceEstimator(vec3 t) {
212221
s *= k;
213222
scale *= k;
214223

215-
orbitTrap.xyz = min(orbitTrap.xyz, abs((s - (push.reactive_high.xyz + push.reactive_bass.xyz)/2.0) * colorRotato));
224+
orbitTrap.xyz = min(orbitTrap.xyz, abs((s - (push.reactive_high.xyz + push.reactive_bass.xyz)/2.0) * frame_constant_klein_colorRotato));
216225
}
217226

218227
return max((0.25*abs(s.z)/scale)/reScale, length(t/reScale)-0.62);
@@ -257,10 +266,11 @@ float distanceEstimator(vec3 t) {
257266
}
258267
return d/reScale;
259268
}
269+
// Sierpiński-inspired.
260270
else if(runtime.distance_estimator_id == 5) {
261271
const int maxIterations = 8;
262272
const float scale = 2.0;
263-
const float reScale = 1.4;
273+
const float reScale = 1.375;
264274

265275
t *= reScale;
266276
vec3 s = t;
@@ -296,47 +306,61 @@ float distanceEstimator(vec3 t) {
296306
// Quaternion Julia.
297307
else if(runtime.distance_estimator_id == 6) {
298308
const int maxIterations = 6;
299-
const float reScale = 2.25;
309+
const float reScale = 1.85;
300310
t *= reScale;
301-
float power = 3.0;
311+
float power = 4.0 + sin(0.025*push.time);
312+
302313
// Store the running derivative as a quaternion.
303314
float dr = 1.0;
304315
float r = 0.0;
305316

306-
mat3 colorRotato = buildRot3(safe_normalize(push.smooth_mids.xyz), 0.325*push.time);
307-
308317
// Create a quaternion from the position.
309-
//vec4 s = vec4(t, 0.25*sqrt(dot(push.smooth_bass, push.smooth_mids) + dot(push.smooth_bass, push.smooth_high) + dot(push.smooth_mids, push.smooth_high)));
310318
vec4 s = vec4(0.0, t);
311-
vec4 q = normalize(multiplyQuaternions(multiplyQuaternions(push.smooth_bass, push.smooth_mids), push.smooth_high));
312-
vec4 c = 0.75*q;
313319

314320
for(int i = 0; i < maxIterations; i++) {
315321
r = length(s);
316322
const float b = 1.5;
317323
if (r > b) break;
318324

319-
// Get the derivative, 2*s*ds.
320-
dr = length(q)*power*pow(r, power-1.0)*dr;
325+
// Get the derivative.
326+
dr = power*pow(r, power-1.0)*dr;
321327

322-
// s = s*s + c; using quaternion algebra.
323-
s = multiplyQuaternions(s, vec4(2.0*s.xyz*s.w, s.w*s.w - dot(s.xyz, s.xyz)));
324-
s = multiplyQuaternions(q, s) + c;
328+
// s = q*s^p + c; using quaternion algebra.
329+
float phi = acos(s.w/r);
330+
s = pow(r, power)*vec4(cos(power*phi), sin(power*phi)*s.xyz/length(s.xyz));
331+
s = multiplyQuaternions(frame_constant_quaternion_q, s) + frame_constant_quaternion_c;
325332

326-
orbitTrap.xyz = min(orbitTrap.xyz, abs((s.xyz - (push.reactive_high.xyz + push.reactive_bass.xyz)/2.0) * colorRotato)/1.25);
333+
orbitTrap.xyz = min(orbitTrap.xyz, abs((s.xyz - (push.reactive_high.xyz + push.reactive_bass.xyz)/2.0) * frame_constant_quaternion_colorRotato)/3.5);
327334
}
328-
orbitTrap = 0.95*sqrt(orbitTrap);
335+
orbitTrap.xyz = sqrt(sqrt(orbitTrap.xyz));
329336
return min(log(r)*r/dr, 3.5) / reScale;
330337
}
331338

332339
// If no fractal is selected, then escape to infinity.
333340
return 1024.0;
334341
}
335342

343+
void setFrameConstants() {
344+
// Mandelbulb.
345+
if(runtime.distance_estimator_id == 2) {
346+
frame_constant_mandelbulb_colorRotato = buildRot3(safe_normalize(push.smooth_mids.xyz), 0.325*push.time);
347+
}
348+
// Klein-inspired.
349+
else if (runtime.distance_estimator_id == 3) {
350+
frame_constant_klein_colorRotato = buildRot3(safe_normalize(push.smooth_mids.xyz), 0.15*push.time);
351+
}
352+
// Quaternion Julia.
353+
else if (runtime.distance_estimator_id == 6) {
354+
frame_constant_quaternion_colorRotato = buildRot3(safe_normalize(push.smooth_mids.xyz), 0.1*push.time);
355+
frame_constant_quaternion_q = safe_normalize(multiplyQuaternions(multiplyQuaternions(push.smooth_high, push.smooth_bass), push.smooth_mids));
356+
frame_constant_quaternion_c = 0.615*multiplyQuaternions(frame_constant_quaternion_q, safe_normalize(multiplyQuaternions(multiplyQuaternions(push.smooth_bass, push.smooth_mids), push.smooth_high)));
357+
}
358+
}
359+
336360
const float maxBrightness = 1.6;
337361
const float maxBrightnessR2 = maxBrightness*maxBrightness;
338362
vec3 scaleColor(float distanceRatio, float iterationRatio, vec3 col) {
339-
col *= pow(1.0 - distanceRatio, 1.2) * pow(1.0 - iterationRatio, 2.75);
363+
col *= pow(1.0 - distanceRatio, 1.2) * pow(1.0 - iterationRatio, runtime.distance_estimator_id != 6 ? 2.75 : 1.75);
340364
if(dot(col, col) > maxBrightnessR2) {
341365
col = maxBrightness*normalize(col);
342366
}
@@ -349,8 +373,12 @@ vec3 castRay(vec3 position, vec3 direction, float fovX, float fovY, out float tr
349373
const float maxDistance = 32.0;
350374
const float hitDistance = epsilon;
351375
float minTravel = 0.3;
376+
377+
// Set the values that will be constant for this frame between distance estimator calls.
378+
setFrameConstants();
379+
352380
if(runtime.distance_estimator_id == 1) {
353-
minTravel = minTravel + max(0.0, -0.75*cos(0.03 * push.time));
381+
minTravel += max(0.0, -0.75*cos(0.03 * push.time));
354382
}
355383

356384
float lastDistance = maxDistance;
@@ -371,12 +399,12 @@ vec3 castRay(vec3 position, vec3 direction, float fovX, float fovY, out float tr
371399
travel += dist;
372400
if(travel >= maxDistance) {
373401
if(!runtime.render_particles) {
374-
vec3 unmodDirection = normalize(vec3(coord.x*fovX, coord.y*fovY, 1.0));
402+
vec3 unmodDirection = normalize(vec3(coord.x*fovX, coord.y*fovY, -1.0));
375403
unmodDirection = rotateByQuaternion(unmodDirection, push.quaternion);
376404

377405
vec3 sinDir = sin(100.0*unmodDirection);
378406
vec3 base = vec3(exp(-2.9*length(sin(pi * push.reactive_bass.xyz + 1.0) - sinDir)), exp(-2.9*length(sin(e * push.reactive_mids.xyz + 1.3) - sinDir)), exp(-2.9*length(sin(9.6*push.reactive_high.xyz + 117.69420) - sinDir)));
379-
return (runtime.distance_estimator_id == 0 ? 0.8 : 0.575) * base;
407+
return (runtime.distance_estimator_id == 0 ? 0.8 : 0.55) * base;
380408
}
381409
break;
382410
}
@@ -425,4 +453,4 @@ void main(void) {
425453
}
426454

427455
fragColor = vec4(tFragColor, 1.0);
428-
}
456+
}

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ impl FractalSugar {
563563
}
564564
}
565565

566-
// Set different fractal types
566+
// Set different fractal types.
567567
VirtualKeyCode::Key0 => self.set_distance_estimate_id(0),
568568
VirtualKeyCode::Key1 => self.set_distance_estimate_id(1),
569569
VirtualKeyCode::Key2 => self.set_distance_estimate_id(2),

0 commit comments

Comments
 (0)