many many changes
@ -1,10 +1,44 @@
|
||||
window.addEventListener('load', function() {
|
||||
if (!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||
fetch('assets/ascii.txt')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const asciiArtContainer = document.getElementById('ascii-art-container');
|
||||
asciiArtContainer.innerText = data;
|
||||
});
|
||||
}
|
||||
fetch('assets/ascii.txt')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const asciiArtContainer = document.getElementById('ascii-art-container');
|
||||
asciiArtContainer.innerText = data;
|
||||
initAsciiEffect(data);
|
||||
});
|
||||
});
|
||||
function adjustFontSize() {
|
||||
const asciiArtContainer = document.getElementById('ascii-art-container');
|
||||
const windowWidth = window.innerWidth;
|
||||
const windowHeight = window.innerHeight;
|
||||
const baseFontSize = Math.min(windowWidth, windowHeight) / 150;
|
||||
asciiArtContainer.style.fontSize = `${baseFontSize}px`;
|
||||
asciiArtContainer.style.top = `${windowHeight / 2}px`;
|
||||
asciiArtContainer.style.left = `${windowWidth / 2}px`;
|
||||
asciiArtContainer.style.transform = 'translate(-50%, -50%)';
|
||||
}
|
||||
window.addEventListener('resize', adjustFontSize);
|
||||
window.addEventListener('load', adjustFontSize);
|
||||
function initAsciiEffect(asciiArt) {
|
||||
const asciiArtContainer = document.getElementById('ascii-art-container');
|
||||
const chars = asciiArt.split('');
|
||||
const updateFrequency = 1;
|
||||
function getRandomIndex() {
|
||||
return Math.floor(Math.random() * chars.length);
|
||||
}
|
||||
function flipBit() {
|
||||
const index = getRandomIndex();
|
||||
const char = chars[index];
|
||||
if (char === '0') {
|
||||
chars[index] = '1';
|
||||
} else if (char === '1') {
|
||||
chars[index] = '0';
|
||||
} else if (char === '@') {
|
||||
chars[index] = '#';
|
||||
} else if (char === '#') {
|
||||
chars[index] = '@';
|
||||
}
|
||||
asciiArtContainer.innerText = chars.join('');
|
||||
}
|
||||
setInterval(flipBit, updateFrequency);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
font-family: 'workbench';
|
||||
src: url('../assets/font.woff2') format('woff2');
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #333;
|
||||
background-image:
|
||||
@ -14,7 +13,6 @@ body {
|
||||
background-size: 100% 3px;
|
||||
background-repeat: repeat-y;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background:
|
||||
radial-gradient(
|
||||
@ -30,14 +28,12 @@ body {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: 'workbench', sans-serif;
|
||||
font-size: 1.5rem;
|
||||
color: #33ccffca;
|
||||
text-shadow: 0 0 10px #33ccffbe;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
@ -45,20 +41,17 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-links li {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
font-family: 'workbench', sans-serif;
|
||||
font-size: 1.2rem;
|
||||
color: #33ccffab; /* a pale blue color reminiscent of old CRTs */
|
||||
color: #33ccffab;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
text-shadow: 0 0 3px #33ccffaf;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: #33ccffda;
|
||||
}
|
||||
@ -67,20 +60,28 @@ body {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: calc(5px + 0.005vw); /* Dynamically adjust the font size based on the viewport width */
|
||||
font-size: 10vmin;
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #33ccffab;
|
||||
text-shadow: 0 0 3px #33ccffaf;
|
||||
padding: 10px;
|
||||
white-space: pre;
|
||||
pointer-events: none;
|
||||
max-height: calc(100vh - 80px); /* Subtract the max possible navbar height to restrict container size */
|
||||
overflow: auto; /* Add scrollbar to container if content exceeds its max-height */
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
overflow: hidden;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.navbar {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.ascii-art-container {
|
||||
display: none;
|
||||
.nav-links {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.nav-links li {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
117
bulb/bulb.js
@ -1,117 +0,0 @@
|
||||
const init = () => {
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ antialias:true });
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
|
||||
const orthographiCamera = new THREE.OrthographicCamera(window.innerWidth / -2.0, window.innerWidth / +2.0, window.innerHeight / +2.0, window.innerHeight / -2.0, 0.0, 1.0);
|
||||
const perspectiveCamera = new THREE.PerspectiveCamera(45.0, window.innerWidth / window.innerHeight, 0.1, 1000.0);
|
||||
const controls = new THREE.OrbitControls(perspectiveCamera, renderer.domElement);
|
||||
const clock = new THREE.Clock();
|
||||
|
||||
perspectiveCamera.position.set(0.0, 0.0, 5.0);
|
||||
perspectiveCamera.lookAt(new THREE.Vector3(0.0, 0.0, 0.0));
|
||||
|
||||
const geometry = new THREE.PlaneBufferGeometry(window.innerWidth, window.innerHeight);
|
||||
|
||||
const uniforms = {
|
||||
uApp: {
|
||||
value: {
|
||||
time: clock.getElapsedTime(),
|
||||
resolution: new THREE.Vector2(window.innerWidth, window.innerHeight)
|
||||
}
|
||||
},
|
||||
uCamera: {
|
||||
value: {
|
||||
position: perspectiveCamera.position,
|
||||
viewMatrix: perspectiveCamera.matrixWorldInverse,
|
||||
projectionMatrix: perspectiveCamera.projectionMatrix
|
||||
}
|
||||
},
|
||||
uParams: {
|
||||
value: {
|
||||
numIterations: 30,
|
||||
convergenceCriteria: 0.0001,
|
||||
finiteDifferenceEpsilon: 0.0001
|
||||
}
|
||||
},
|
||||
uScene: {
|
||||
value: {
|
||||
backgroundColor: new THREE.Vector3(0.0, 0.0, 0.0),
|
||||
lights: [
|
||||
{
|
||||
direction: new THREE.Vector3(1.0, 1.0, 1.0),
|
||||
ambientColor: new THREE.Vector3(1.0, 1.0, 1.0),
|
||||
diffuseColor: new THREE.Vector3(1.0, 1.0, 0.0),
|
||||
specularColor: new THREE.Vector3(1.0, 1.0, 0.0)
|
||||
},
|
||||
{
|
||||
direction: new THREE.Vector3(-1.0, -1.0, -1.0),
|
||||
ambientColor: new THREE.Vector3(1.0, 1.0, 1.0),
|
||||
diffuseColor: new THREE.Vector3(1.0, 0.0, 1.0),
|
||||
specularColor: new THREE.Vector3(1.0, 0.0, 1.0)
|
||||
}
|
||||
],
|
||||
material: {
|
||||
ambientColor: new THREE.Vector3(0.05, 0.05, 0.05),
|
||||
diffuseColor: new THREE.Vector3(0.5, 0.5, 0.5),
|
||||
specularColor: new THREE.Vector3(1.0, 1.0, 1.0),
|
||||
emissionColor: new THREE.Vector3(0.0, 0.0, 0.0),
|
||||
shininess: 64.0
|
||||
},
|
||||
bound: {
|
||||
position: new THREE.Vector3(0.0, 0.0, 0.0),
|
||||
radius: 2.0
|
||||
},
|
||||
fractal: {
|
||||
power: 10,
|
||||
numIterations: 4,
|
||||
escapeCriteria: 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
vertexShader: document.getElementById('vertexShader').textContent,
|
||||
fragmentShader: document.getElementById('fragmentShader').textContent,
|
||||
uniforms: uniforms
|
||||
});
|
||||
|
||||
scene.add(new THREE.Mesh(geometry, material));
|
||||
|
||||
const onWindowResize = (event) => {
|
||||
uniforms.uApp.value.resolution.x = window.innerWidth * window.devicePixelRatio;
|
||||
uniforms.uApp.value.resolution.y = window.innerHeight * window.devicePixelRatio;
|
||||
// NOTE: https://ics.media/tutorial-three/renderer_resize/
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
perspectiveCamera.aspect = window.innerWidth / window.innerHeight;
|
||||
perspectiveCamera.updateProjectionMatrix();
|
||||
}
|
||||
onWindowResize();
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
|
||||
const animate = () => {
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
const update = () => {
|
||||
controls.update();
|
||||
perspectiveCamera.lookAt(new THREE.Vector3(0.0, 0.0, 0.0));
|
||||
uniforms.uApp.value.time = clock.getElapsedTime();
|
||||
uniforms.uCamera.value.position = perspectiveCamera.position;
|
||||
uniforms.uCamera.value.viewMatrix = perspectiveCamera.matrixWorldInverse;
|
||||
uniforms.uCamera.value.projectionMatrix = perspectiveCamera.projectionMatrix;
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
renderer.render(scene, orthographiCamera);
|
||||
};
|
||||
|
||||
animate();
|
||||
}
|
||||
|
||||
window.addEventListener("load", init);
|
889
bulb/index.html
@ -1,889 +0,0 @@
|
||||
<script id="vertexShader" type="x-shader/x-vertex">
|
||||
|
||||
void main(){
|
||||
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script id="fragmentShader" type="x-shader/x-fragment">
|
||||
|
||||
#define SDF_DERIVATIVE_TYPE 0
|
||||
#define NORMAL_DERIVATIVE_TYPE 0
|
||||
|
||||
// ================ variables ================ //
|
||||
|
||||
// ---------------- application ---------------- //
|
||||
|
||||
struct App
|
||||
{
|
||||
float time;
|
||||
vec2 resolution;
|
||||
};
|
||||
|
||||
struct Camera
|
||||
{
|
||||
vec3 position;
|
||||
mat4 viewMatrix;
|
||||
mat4 projectionMatrix;
|
||||
};
|
||||
|
||||
struct Params
|
||||
{
|
||||
int numIterations;
|
||||
float convergenceCriteria;
|
||||
float finiteDifferenceEpsilon;
|
||||
};
|
||||
|
||||
// ---------------- lighting ---------------- //
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
vec3 position;
|
||||
vec3 ambientColor;
|
||||
vec3 diffuseColor;
|
||||
vec3 specularColor;
|
||||
};
|
||||
|
||||
struct DirectionalLight
|
||||
{
|
||||
vec3 direction;
|
||||
vec3 ambientColor;
|
||||
vec3 diffuseColor;
|
||||
vec3 specularColor;
|
||||
};
|
||||
|
||||
struct Material
|
||||
{
|
||||
vec3 ambientColor;
|
||||
vec3 diffuseColor;
|
||||
vec3 specularColor;
|
||||
vec3 emissionColor;
|
||||
float shininess;
|
||||
};
|
||||
|
||||
// ---------------- primitives ---------------- //
|
||||
|
||||
struct Line
|
||||
{
|
||||
vec3 position;
|
||||
vec3 direction;
|
||||
};
|
||||
|
||||
struct Sphere
|
||||
{
|
||||
vec3 position;
|
||||
float radius;
|
||||
};
|
||||
|
||||
struct Intersection
|
||||
{
|
||||
vec3 position;
|
||||
bool intersected;
|
||||
};
|
||||
|
||||
// ---------------- scene ---------------- //
|
||||
|
||||
const int numLights = 2;
|
||||
|
||||
struct Fractal
|
||||
{
|
||||
int power;
|
||||
int numIterations;
|
||||
float escapeCriteria;
|
||||
};
|
||||
|
||||
struct Scene
|
||||
{
|
||||
vec3 backgroundColor;
|
||||
DirectionalLight lights[numLights];
|
||||
Material material;
|
||||
Sphere bound;
|
||||
Fractal fractal;
|
||||
};
|
||||
|
||||
// ---------------- uniform ---------------- //
|
||||
|
||||
uniform App uApp;
|
||||
uniform Camera uCamera;
|
||||
uniform Params uParams;
|
||||
uniform Scene uScene;
|
||||
|
||||
// ================ functions ================ //
|
||||
|
||||
// ---------------- utilities ---------------- //
|
||||
|
||||
vec2 linmap(vec2 in_val, vec2 in_min, vec2 in_max, vec2 out_min, vec2 out_max)
|
||||
{
|
||||
return (in_val - in_min) / (in_max - in_min) * (out_max - out_min) + out_min;
|
||||
}
|
||||
|
||||
// ---------------- primitives ---------------- //
|
||||
|
||||
float sdfSphere(Sphere sphere, vec3 position)
|
||||
{
|
||||
return length(position - sphere.position) - sphere.radius;
|
||||
}
|
||||
|
||||
vec3 normalSphere(Sphere sphere, vec3 position)
|
||||
{
|
||||
return normalize(position - sphere.position);
|
||||
}
|
||||
|
||||
Intersection intersectionSphereLine(Sphere sphere, Line line)
|
||||
{
|
||||
vec3 difference = line.position - sphere.position;
|
||||
float a = dot(line.direction, line.direction);
|
||||
float b = 2.0 * dot(difference, line.direction);
|
||||
float c = dot(difference, difference) - pow(sphere.radius, 2.0);
|
||||
float d = pow(b, 2.0) - 4.0 * a * c;
|
||||
float t = (-b - sqrt(d)) / (2.0 * a);
|
||||
return Intersection(line.position + t * line.direction, d >= 0.0);
|
||||
}
|
||||
|
||||
// ---------------- constructive solid geometry ---------------- //
|
||||
|
||||
float csgUnion(float sd1, float sd2) { return min(sd1, sd2); }
|
||||
|
||||
float csgSubtraction(float sd1, float sd2) { return max(-sd1, sd2); }
|
||||
|
||||
float csgIntersection(float sd1, float sd2) { return max(sd1, sd2); }
|
||||
|
||||
// ---------------- complex ---------------- //
|
||||
|
||||
vec2 cAdd(vec2 c1, vec2 c2)
|
||||
{
|
||||
// return vec2(c1.x + c2.x, c1.y + c2.y);
|
||||
return c1 + c2;
|
||||
}
|
||||
|
||||
vec2 cSub(vec2 c1, vec2 c2)
|
||||
{
|
||||
// return vec2(c1.x - c2.x, c1.y - c2.y);
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
vec2 cMul(vec2 c1, vec2 c2)
|
||||
{
|
||||
return vec2(c1.x * c2.x - c1.y * c2.y, c1.y * c2.x + c1.x * c2.y);
|
||||
}
|
||||
|
||||
vec2 cConj(vec2 c)
|
||||
{
|
||||
return vec2(c.x, -c.y);
|
||||
}
|
||||
|
||||
float cNorm(vec2 c)
|
||||
{
|
||||
// return sqrt(cMul(c, cConj(c)).x);
|
||||
return length(c);
|
||||
}
|
||||
|
||||
vec2 cInv(vec2 c)
|
||||
{
|
||||
return cConj(c) / pow(cNorm(c), 2.0);
|
||||
}
|
||||
|
||||
vec2 cDiv(vec2 c1, vec2 c2)
|
||||
{
|
||||
return cMul(c1, cInv(c2));
|
||||
}
|
||||
|
||||
vec2 cPow(vec2 c, int n)
|
||||
{
|
||||
vec2 p = vec2(1.0, 0.0);
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
p = cMul(p, c);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// ---------------- quaternion ---------------- //
|
||||
|
||||
vec4 qAdd(vec4 q1, vec4 q2)
|
||||
{
|
||||
// return vec4(q1.x + q2.x, q1.yzw + q2.yzw);
|
||||
return q1 + q2;
|
||||
}
|
||||
|
||||
vec4 qSub(vec4 q1, vec4 q2)
|
||||
{
|
||||
// return vec4(q1.x - q2.x, q1.yzw - q2.yzw);
|
||||
return q1 - q2;
|
||||
}
|
||||
|
||||
vec4 qMul(vec4 q1, vec4 q2)
|
||||
{
|
||||
return vec4(q1.x * q2.x - dot(q1.yzw, q2.yzw), q2.x * q1.yzw + q1.x * q2.yzw + cross(q1.yzw, q2.yzw));
|
||||
}
|
||||
|
||||
vec4 qConj(vec4 q)
|
||||
{
|
||||
return vec4(q.x, -q.yzw);
|
||||
}
|
||||
|
||||
float qNorm(vec4 q)
|
||||
{
|
||||
// return sqrt(qMul(q, qConj(q)).x);
|
||||
return length(q);
|
||||
}
|
||||
|
||||
vec4 qInv(vec4 q)
|
||||
{
|
||||
return qConj(q) / pow(qNorm(q), 2.0);
|
||||
}
|
||||
|
||||
vec4 qDiv(vec4 q1, vec4 q2)
|
||||
{
|
||||
return qMul(q1, qInv(q2));
|
||||
}
|
||||
|
||||
vec4 qPow(vec4 q, int n)
|
||||
{
|
||||
vec4 p = vec4(1.0, vec3(0.0));
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
p = qMul(p, q);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// ---------------- trinion ---------------- //
|
||||
|
||||
vec3 tAdd(vec3 t1, vec3 t2)
|
||||
{
|
||||
return t1 + t2;
|
||||
}
|
||||
|
||||
vec3 tSub(vec3 t1, vec3 t2)
|
||||
{
|
||||
return t1 - t2;
|
||||
}
|
||||
|
||||
vec3 tMul(vec3 t1, vec3 t2)
|
||||
{
|
||||
float r1 = length(t1);
|
||||
float r2 = length(t2);
|
||||
|
||||
if (r1 > 0.0 && r2 > 0.0)
|
||||
{
|
||||
float a1 = asin(t1.z / r1);
|
||||
float a2 = asin(t2.z / r2);
|
||||
|
||||
float b1 = atan(t1.y, t1.x);
|
||||
float b2 = atan(t2.y, t2.x);
|
||||
|
||||
float r = r1 * r2;
|
||||
float a = a1 + a2;
|
||||
float b = b1 + b2;
|
||||
|
||||
float x = r * cos(a) * cos(b);
|
||||
float y = r * cos(a) * sin(b);
|
||||
float z = r * sin(a);
|
||||
|
||||
return vec3(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
return vec3(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
vec3 tPow(vec3 t, int n)
|
||||
{
|
||||
/* NOTE: This does not work
|
||||
|
||||
vec3 p = vec3(1.0, vec2(0.0));
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
p = tMul(p, t);
|
||||
}
|
||||
return p;
|
||||
|
||||
*/
|
||||
|
||||
float r = length(t);
|
||||
|
||||
if (r > 0.0)
|
||||
{
|
||||
float a = asin(t.z / r);
|
||||
float b = atan(t.y, t.x);
|
||||
|
||||
float pr = pow(r, float(n));
|
||||
float pa = a * float(n);
|
||||
float pb = b * float(n);
|
||||
|
||||
float x = pr * cos(pa) * cos(pb);
|
||||
float y = pr * cos(pa) * sin(pb);
|
||||
float z = pr * sin(pa);
|
||||
|
||||
return vec3(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
return vec3(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- dual ---------------- //
|
||||
|
||||
struct DualQ
|
||||
{
|
||||
vec4 q;
|
||||
vec4 d;
|
||||
};
|
||||
|
||||
DualQ dqAdd(DualQ dq1, DualQ dq2)
|
||||
{
|
||||
return DualQ(qAdd(dq1.q, dq2.q), qAdd(dq1.d, dq2.d));
|
||||
}
|
||||
|
||||
DualQ dqSub(DualQ dq1, DualQ dq2)
|
||||
{
|
||||
return DualQ(qSub(dq1.q, dq2.q), qSub(dq1.d, dq2.d));
|
||||
}
|
||||
|
||||
DualQ dqMul(DualQ dq1, DualQ dq2)
|
||||
{
|
||||
return DualQ(qMul(dq1.q, dq2.q), qAdd(qMul(dq1.d, dq2.q), qMul(dq1.q, dq2.d)));
|
||||
}
|
||||
|
||||
DualQ dqDiv(DualQ dq1, DualQ dq2)
|
||||
{
|
||||
return DualQ(qDiv(dq1.q, dq2.q), qDiv(qSub(qMul(dq1.d, dq2.q), qMul(dq1.q, dq2.d)), qMul(dq2.q, dq2.q)));
|
||||
}
|
||||
|
||||
DualQ dqPow(DualQ dq, int n)
|
||||
{
|
||||
DualQ dp = DualQ(vec4(1.0, vec3(0.0)), vec4(0.0, vec3(0.0)));
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
dp = dqMul(dp, dq);
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
struct DualS
|
||||
{
|
||||
float s;
|
||||
float d;
|
||||
};
|
||||
|
||||
DualS dAdd(DualS d1, DualS d2)
|
||||
{
|
||||
return DualS(d1.s + d2.s, d1.d + d2.d);
|
||||
}
|
||||
|
||||
DualS dSub(DualS d1, DualS d2)
|
||||
{
|
||||
return DualS(d1.s - d2.s, d1.d - d2.d);
|
||||
}
|
||||
|
||||
DualS dMul(DualS d1, DualS d2)
|
||||
{
|
||||
return DualS(d1.s * d2.s, d1.d * d2.s + d1.s * d2.d);
|
||||
}
|
||||
|
||||
DualS dDiv(DualS d1, DualS d2)
|
||||
{
|
||||
return DualS(d1.s / d2.s, (d1.d * d2.s - d1.s * d2.d) / (d2.s * d2.s));
|
||||
}
|
||||
|
||||
DualS dPow(DualS d, float n)
|
||||
{
|
||||
return DualS(pow(d.s, n), d.d * n * pow(d.s, n - 1.0));
|
||||
}
|
||||
|
||||
DualS dSqrt(DualS d)
|
||||
{
|
||||
return DualS(sqrt(d.s), d.d * 0.5 / sqrt(d.s));
|
||||
}
|
||||
|
||||
DualS dSin(DualS d)
|
||||
{
|
||||
return DualS(sin(d.s), d.d * cos(d.s));
|
||||
}
|
||||
|
||||
DualS dCos(DualS d)
|
||||
{
|
||||
return DualS(cos(d.s), d.d * -sin(d.s));
|
||||
}
|
||||
|
||||
DualS dTan(DualS d)
|
||||
{
|
||||
return DualS(tan(d.s), d.d / pow(cos(d.s), 2.0));
|
||||
}
|
||||
|
||||
DualS dArcSin(DualS d)
|
||||
{
|
||||
return DualS(asin(d.s), d.d / sqrt(1.0 - pow(d.s, 2.0)));
|
||||
}
|
||||
|
||||
DualS dArcCos(DualS d)
|
||||
{
|
||||
return DualS(acos(d.s), d.d / -sqrt(1.0 - pow(d.s, 2.0)));
|
||||
}
|
||||
|
||||
DualS dArcTan(DualS d)
|
||||
{
|
||||
return DualS(atan(d.s), d.d / (1.0 + pow(d.s, 2.0)));
|
||||
}
|
||||
|
||||
struct DualT
|
||||
{
|
||||
vec3 t;
|
||||
vec3 d;
|
||||
};
|
||||
|
||||
DualT dtAdd(DualT dt1, DualT dt2)
|
||||
{
|
||||
return DualT(dt1.t + dt2.t, dt1.d + dt2.d);
|
||||
}
|
||||
|
||||
DualT dtSub(DualT dt1, DualT dt2)
|
||||
{
|
||||
return DualT(dt1.t - dt2.t, dt1.d - dt2.d);
|
||||
}
|
||||
|
||||
DualT dtMul(DualT dt1, DualT dt2)
|
||||
{
|
||||
// return DualT(tMul(dt1.t, dt2.t), tAdd(tMul(dt1.d, dt2.t), tMul(dt1.t, dt2.d)));
|
||||
|
||||
DualS dx1 = DualS(dt1.t.x, dt1.d.x);
|
||||
DualS dy1 = DualS(dt1.t.y, dt1.d.y);
|
||||
DualS dz1 = DualS(dt1.t.z, dt1.d.z);
|
||||
|
||||
DualS dx2 = DualS(dt2.t.x, dt2.d.x);
|
||||
DualS dy2 = DualS(dt2.t.y, dt2.d.y);
|
||||
DualS dz2 = DualS(dt2.t.z, dt2.d.z);
|
||||
|
||||
DualS dr1 = dSqrt(dAdd(dPow(dx1, 2.0), dAdd(dPow(dy1, 2.0), dPow(dz1, 2.0))));
|
||||
DualS dr2 = dSqrt(dAdd(dPow(dx2, 2.0), dAdd(dPow(dy2, 2.0), dPow(dz2, 2.0))));
|
||||
|
||||
if (dr1.s > 0.0 && dr2.s > 0.0)
|
||||
{
|
||||
DualS da1 = dArcSin(dDiv(dz1, dr1));
|
||||
DualS da2 = dArcSin(dDiv(dz2, dr2));
|
||||
|
||||
DualS db1 = dArcTan(dDiv(dy1, dx1));
|
||||
DualS db2 = dArcTan(dDiv(dy2, dx2));
|
||||
|
||||
DualS dr = dMul(dr1, dr2);
|
||||
DualS da = dAdd(da1, da2);
|
||||
DualS db = dAdd(db1, db2);
|
||||
|
||||
DualS dx = dMul(dr, dMul(dCos(da), dCos(db)));
|
||||
DualS dy = dMul(dr, dMul(dCos(da), dSin(db)));
|
||||
DualS dz = dMul(dr, dSin(da));
|
||||
|
||||
return DualT(vec3(dx.s, dy.s, dz.s), vec3(dx.d, dy.d, dz.d));
|
||||
}
|
||||
else
|
||||
{
|
||||
return DualT(vec3(0.0), vec3(0.0));
|
||||
}
|
||||
}
|
||||
|
||||
DualT dtPow(DualT dt, int n)
|
||||
{
|
||||
/* NOTE: This does not work
|
||||
|
||||
DualT dp = DualT(vec3(1.0, vec2(0.0)), vec3(0.0, vec2(0.0)));
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
dp = dtMul(dp, dt);
|
||||
}
|
||||
return dp;
|
||||
|
||||
*/
|
||||
|
||||
DualS dx = DualS(dt.t.x, dt.d.x);
|
||||
DualS dy = DualS(dt.t.y, dt.d.y);
|
||||
DualS dz = DualS(dt.t.z, dt.d.z);
|
||||
|
||||
DualS dr = dSqrt(dAdd(dPow(dx, 2.0), dAdd(dPow(dy, 2.0), dPow(dz, 2.0))));
|
||||
|
||||
if (dr.s > 0.0)
|
||||
{
|
||||
DualS da = dArcSin(dDiv(dz, dr));
|
||||
DualS db = dArcTan(dDiv(dy, dx));
|
||||
|
||||
DualS dpr = dPow(dr, float(n));
|
||||
DualS dpa = dMul(da, DualS(float(n), 0.0));
|
||||
DualS dpb = dMul(db, DualS(float(n), 0.0));
|
||||
|
||||
DualS dx = dMul(dpr, dMul(dCos(dpa), dCos(dpb)));
|
||||
DualS dy = dMul(dpr, dMul(dCos(dpa), dSin(dpb)));
|
||||
DualS dz = dMul(dpr, dSin(dpa));
|
||||
|
||||
return DualT(vec3(dx.s, dy.s, dz.s), vec3(dx.d, dy.d, dz.d));
|
||||
}
|
||||
else
|
||||
{
|
||||
return DualT(vec3(0.0), vec3(0.0));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- fractals ---------------- //
|
||||
|
||||
#if SDF_DERIVATIVE_TYPE == 0
|
||||
float sdfJulia(Fractal fractal, vec4 z, vec4 c)
|
||||
{
|
||||
DualQ dzx = DualQ(z, vec4(1.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzy = DualQ(z, vec4(0.0, 1.0, 0.0, 0.0));
|
||||
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 1.0, 0.0));
|
||||
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
DualQ dcx = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcy = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
// forward-mode automatic differentiation
|
||||
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
|
||||
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
|
||||
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
|
||||
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
|
||||
|
||||
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
|
||||
return (qNorm(dzx.q) * log(qNorm(dzx.q))) / (2.0 * qNorm(normalize(dzx.q) * J));
|
||||
}
|
||||
float sdfMandelbrot(Fractal fractal, vec4 c, vec4 z)
|
||||
{
|
||||
DualQ dzx = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzy = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
DualQ dcx = DualQ(c, vec4(1.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcy = DualQ(c, vec4(0.0, 1.0, 0.0, 0.0));
|
||||
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 1.0, 0.0));
|
||||
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
// forward-mode automatic differentiation
|
||||
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
|
||||
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
|
||||
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
|
||||
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
|
||||
|
||||
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
|
||||
return (qNorm(dzx.q) * log(qNorm(dzx.q))) / (2.0 * qNorm(normalize(dzx.q) * J));
|
||||
}
|
||||
float sdfMandelbulb(Fractal fractal, vec3 c, vec3 z)
|
||||
{
|
||||
DualT dzx = DualT(z, vec3(0.0, 0.0, 0.0));
|
||||
DualT dzy = DualT(z, vec3(0.0, 0.0, 0.0));
|
||||
DualT dzz = DualT(z, vec3(0.0, 0.0, 0.0));
|
||||
|
||||
DualT dcx = DualT(c, vec3(1.0, 0.0, 0.0));
|
||||
DualT dcy = DualT(c, vec3(0.0, 1.0, 0.0));
|
||||
DualT dcz = DualT(c, vec3(0.0, 0.0, 1.0));
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
// forward-mode automatic differentiation
|
||||
dzx = dtAdd(dtPow(dzx, fractal.power), dcx);
|
||||
dzy = dtAdd(dtPow(dzy, fractal.power), dcy);
|
||||
dzz = dtAdd(dtPow(dzz, fractal.power), dcz);
|
||||
|
||||
if (length(dzx.t) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat3 J = mat3(dzx.d, dzy.d, dzz.d);
|
||||
return (length(dzx.t) * log(length(dzx.t))) / (2.0 * length(normalize(dzx.t) * J));
|
||||
}
|
||||
#elif SDF_DERIVATIVE_TYPE == 1
|
||||
float sdfJulia(Fractal fractal, vec4 z, vec4 c)
|
||||
{
|
||||
vec4 dzx = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzy = vec4(0.0, 1.0, 0.0, 0.0);
|
||||
vec4 dzz = vec4(0.0, 0.0, 1.0, 0.0);
|
||||
vec4 dzw = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
vec4 dcx = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcy = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcz = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcw = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
vec4 zp = qPow(z, fractal.power - 1);
|
||||
|
||||
// forward-mode manual differentiation
|
||||
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
|
||||
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
|
||||
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
|
||||
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
|
||||
|
||||
z = qAdd(qMul(zp, z), c);
|
||||
|
||||
if (qNorm(z) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx, dzy, dzz, dzw);
|
||||
return (qNorm(z) * log(qNorm(z))) / (2.0 * qNorm(normalize(z) * J));
|
||||
}
|
||||
float sdfMandelbrot(Fractal fractal, vec4 c, vec4 z)
|
||||
{
|
||||
vec4 dzx = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzy = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzz = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzw = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
vec4 dcx = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcy = vec4(0.0, 1.0, 0.0, 0.0);
|
||||
vec4 dcz = vec4(0.0, 0.0, 1.0, 0.0);
|
||||
vec4 dcw = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
vec4 zp = qPow(z, fractal.power - 1);
|
||||
|
||||
// forward-mode manual differentiation
|
||||
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
|
||||
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
|
||||
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
|
||||
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
|
||||
|
||||
z = qAdd(qMul(zp, z), c);
|
||||
|
||||
if (qNorm(z) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx, dzy, dzz, dzw);
|
||||
return (qNorm(z) * log(qNorm(z))) / (2.0 * qNorm(normalize(z) * J));
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
|
||||
#if NORMAL_DERIVATIVE_TYPE == 0
|
||||
vec4 normalJulia(Fractal fractal, vec4 z, vec4 c)
|
||||
{
|
||||
DualQ dzx = DualQ(z, vec4(1.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzy = DualQ(z, vec4(0.0, 1.0, 0.0, 0.0));
|
||||
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 1.0, 0.0));
|
||||
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
DualQ dcx = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcy = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
// forward-mode automatic differentiation
|
||||
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
|
||||
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
|
||||
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
|
||||
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
|
||||
|
||||
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
|
||||
return dzx.q * J;
|
||||
}
|
||||
vec4 normalMandelbrot(Fractal fractal, vec4 c, vec4 z)
|
||||
{
|
||||
DualQ dzx = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzy = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
DualQ dcx = DualQ(c, vec4(1.0, 0.0, 0.0, 0.0));
|
||||
DualQ dcy = DualQ(c, vec4(0.0, 1.0, 0.0, 0.0));
|
||||
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 1.0, 0.0));
|
||||
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
// forward-mode automatic differentiation
|
||||
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
|
||||
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
|
||||
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
|
||||
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
|
||||
|
||||
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
|
||||
return dzx.q * J;
|
||||
}
|
||||
vec3 normalMandelbulb(Fractal fractal, vec3 c, vec3 z)
|
||||
{
|
||||
DualT dzx = DualT(z, vec3(0.0, 0.0, 0.0));
|
||||
DualT dzy = DualT(z, vec3(0.0, 0.0, 0.0));
|
||||
DualT dzz = DualT(z, vec3(0.0, 0.0, 0.0));
|
||||
|
||||
DualT dcx = DualT(c, vec3(1.0, 0.0, 0.0));
|
||||
DualT dcy = DualT(c, vec3(0.0, 1.0, 0.0));
|
||||
DualT dcz = DualT(c, vec3(0.0, 0.0, 1.0));
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
// forward-mode automatic differentiation
|
||||
dzx = dtAdd(dtPow(dzx, fractal.power), dcx);
|
||||
dzy = dtAdd(dtPow(dzy, fractal.power), dcy);
|
||||
dzz = dtAdd(dtPow(dzz, fractal.power), dcz);
|
||||
|
||||
if (length(dzx.t) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat3 J = mat3(dzx.d, dzy.d, dzz.d);
|
||||
return dzx.t * J;
|
||||
}
|
||||
#elif NORMAL_DERIVATIVE_TYPE == 1
|
||||
vec4 normalJulia(Fractal fractal, vec4 z, vec4 c)
|
||||
{
|
||||
vec4 dzx = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzy = vec4(0.0, 1.0, 0.0, 0.0);
|
||||
vec4 dzz = vec4(0.0, 0.0, 1.0, 0.0);
|
||||
vec4 dzw = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
vec4 dcx = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcy = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcz = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcw = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
vec4 zp = qPow(z, fractal.power - 1);
|
||||
|
||||
// forward-mode manual differentiation
|
||||
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
|
||||
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
|
||||
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
|
||||
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
|
||||
|
||||
z = qAdd(qMul(zp, z), c);
|
||||
|
||||
if (qNorm(z) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx, dzy, dzz, dzw);
|
||||
return z * J;
|
||||
}
|
||||
vec4 normalMandelbrot(Fractal fractal, vec4 c, vec4 z)
|
||||
{
|
||||
vec4 dzx = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzy = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzz = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dzw = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
vec4 dcx = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
vec4 dcy = vec4(0.0, 1.0, 0.0, 0.0);
|
||||
vec4 dcz = vec4(0.0, 0.0, 1.0, 0.0);
|
||||
vec4 dcw = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
for (int i = 0; i < fractal.numIterations; ++i)
|
||||
{
|
||||
vec4 zp = qPow(z, fractal.power - 1);
|
||||
|
||||
// forward-mode manual differentiation
|
||||
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
|
||||
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
|
||||
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
|
||||
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
|
||||
|
||||
z = qAdd(qMul(zp, z), c);
|
||||
|
||||
if (qNorm(z) > fractal.escapeCriteria) break;
|
||||
}
|
||||
|
||||
mat4 J = mat4(dzx, dzy, dzz, dzw);
|
||||
return z * J;
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
|
||||
// ---------------- reflection ---------------- //
|
||||
|
||||
vec3 phongReflection(vec3 surfaceNormal, vec3 viewDirection, DirectionalLight lights[numLights], Material material)
|
||||
{
|
||||
surfaceNormal = normalize(surfaceNormal);
|
||||
viewDirection = normalize(viewDirection);
|
||||
|
||||
vec3 ambientColor = vec3(0.0);
|
||||
vec3 diffuseColor = vec3(0.0);
|
||||
vec3 specularColor = vec3(0.0);
|
||||
|
||||
for (int i = 0; i < lights.length(); ++i)
|
||||
{
|
||||
vec3 lightDirection = normalize(lights[i].direction);
|
||||
vec3 reflectedDirection = reflect(lightDirection, surfaceNormal);
|
||||
float diffuseCoefficient = max(dot(-lightDirection, surfaceNormal), 0.0);
|
||||
float specularCoefficient = pow(max(dot(reflectedDirection, -viewDirection), 0.0), material.shininess);
|
||||
ambientColor += lights[i].ambientColor * material.ambientColor;
|
||||
diffuseColor += lights[i].diffuseColor * material.diffuseColor * diffuseCoefficient;
|
||||
specularColor += lights[i].specularColor * material.specularColor * specularCoefficient;
|
||||
}
|
||||
|
||||
vec3 color = clamp(ambientColor + diffuseColor + specularColor + material.emissionColor, 0.0, 1.0);
|
||||
return color;
|
||||
}
|
||||
|
||||
// ---------------- sphere tracing ---------------- //
|
||||
|
||||
vec3 sphereTracing(App app, Scene scene, Params params, Line ray)
|
||||
{
|
||||
Intersection intersection = intersectionSphereLine(scene.bound, ray);
|
||||
|
||||
if (intersection.intersected)
|
||||
{
|
||||
ray.position = intersection.position;
|
||||
|
||||
// The hyperparameter from Inigo Quilez (https://www.shadertoy.com/view/MsfGRr)
|
||||
vec4 juliaType = 0.45 * cos(vec4(0.5, 3.9, 1.4, 1.1) + app.time * 0.15 * vec4(1.2, 1.7, 1.3, 2.5)) - vec4(0.3, 0.0, 0.0, 0.0);
|
||||
vec4 criticalPoint = vec4(0.0);
|
||||
|
||||
for (int i = 0; i < params.numIterations; ++i)
|
||||
{
|
||||
float sd = sdfMandelbulb(scene.fractal, ray.position, criticalPoint.xyz);
|
||||
|
||||
// ray marching
|
||||
ray.position += sd * ray.direction;
|
||||
|
||||
// collision detection
|
||||
if (abs(sd) < params.convergenceCriteria)
|
||||
{
|
||||
vec3 surfaceNormal = normalize(normalMandelbulb(scene.fractal, ray.position, criticalPoint.xyz));
|
||||
|
||||
vec3 fragColor = phongReflection(surfaceNormal, ray.direction, scene.lights, scene.material);
|
||||
|
||||
return fragColor;
|
||||
}
|
||||
|
||||
if (sdfSphere(scene.bound, ray.position) > 0.0) break;
|
||||
}
|
||||
}
|
||||
|
||||
return scene.backgroundColor;
|
||||
}
|
||||
|
||||
// ---------------- main ---------------- //
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 fragCoord = linmap(gl_FragCoord.xy, vec2(0, 0), uApp.resolution, vec2(-1.0, -1.0), vec2(1.0, 1.0));
|
||||
vec3 rayDirection = normalize(inverse(mat3(uCamera.projectionMatrix) * mat3(uCamera.viewMatrix)) * vec3(fragCoord, 1.0));
|
||||
|
||||
Line ray = Line(uCamera.position, rayDirection);
|
||||
vec3 fragColor = sphereTracing(uApp, uScene, uParams, ray);
|
||||
gl_FragColor = vec4(fragColor, 1.0);
|
||||
}
|
||||
|
||||
</script>
|
||||
<script src="bulb.js"></script>
|
||||
<script src="https://unpkg.com/three@0.139.2/build/three.min.js"></script>
|
||||
<script src="https://unpkg.com/three@0.139.2/examples/js/controls/OrbitControls.js"></script>
|
188
demo.html
@ -1,188 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Mandelbulb ASCII Art</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Courier New', monospace;
|
||||
white-space: pre;
|
||||
background: #000;
|
||||
color: #0f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#asciiArt {
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
font-size: 7px;
|
||||
line-height: 7px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.controls {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
color: #0f0;
|
||||
}
|
||||
input[type='number'] {
|
||||
width: 60px;
|
||||
background: #222;
|
||||
border: 1px solid #333;
|
||||
color: #0f0;
|
||||
margin: 2px 0;
|
||||
}
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="controls">
|
||||
<label>Power: <input type="number" id="power" min="2" max="12" step="0.1" value="8"></label><br>
|
||||
<label>Detail: <input type="number" step="0.01" id="detail" value="0.25"></label><br>
|
||||
<label>Columns: <input type="number" id="columns" value="80"></label>
|
||||
</div>
|
||||
<pre id="asciiArt"></pre>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// Mandelbulb Calculation and Rendering
|
||||
class Mandelbulb {
|
||||
constructor(power, detail, columns, rows) {
|
||||
this.power = power;
|
||||
this.detail = detail;
|
||||
this.columns = columns;
|
||||
this.rows = rows;
|
||||
this.charset = " .:░▒▓█"; // Use extended charset for ASCII view detail
|
||||
}
|
||||
|
||||
// Normalized rotation using yaw and pitch for 3D rotation
|
||||
rotate(x, y, z, sinA, cosA, sinB, cosB) {
|
||||
const yr = y * cosA - z * sinA;
|
||||
const zr = y * sinA + z * cosA;
|
||||
const xr = x * cosB - zr * sinB;
|
||||
const zrr = x * sinB + zr * cosB;
|
||||
return [xr, yr, zrr];
|
||||
}
|
||||
|
||||
// 3D Distance Estimation iterative formula for the Mandelbulb fractal
|
||||
computeMandelbulbDistance(x0, y0, z0, maxIterations, escapeRadius) {
|
||||
let x = x0, y = y0, z = z0, dr = 1.0;
|
||||
let r = 0;
|
||||
for (let i = 0; i < maxIterations; i++) {
|
||||
r = Math.sqrt(x * x + y * y + z * z);
|
||||
if (r > escapeRadius) break;
|
||||
|
||||
// Convert to polar coordinates
|
||||
const theta = Math.atan2(Math.sqrt(x * x + y * y), z);
|
||||
const phi = Math.atan2(y, x);
|
||||
const zr = Math.pow(r, this.power - 1);
|
||||
dr = Math.pow(r, this.power - 1) * this.power * dr + 1.0;
|
||||
const thetaR = theta * this.power;
|
||||
const phiR = phi * this.power;
|
||||
|
||||
// Convert back to cartesian coordinates
|
||||
x = zr * Math.sin(thetaR) * Math.cos(phiR) + x0;
|
||||
y = zr * Math.sin(thetaR) * Math.sin(phiR) + y0;
|
||||
z = zr * Math.cos(thetaR) + z0;
|
||||
}
|
||||
return 0.5 * Math.log(r) * r / dr;
|
||||
}
|
||||
|
||||
render(angleX, angleY) {
|
||||
const output = Array.from({ length: this.rows }, () => []);
|
||||
const maxIterations = 256;
|
||||
const escapeRadius = 10;
|
||||
const voxelSize = 2.5 / Math.min(this.columns, this.rows);
|
||||
const fov = 1.0 / Math.tan(0.5);
|
||||
const cosA = Math.cos(angleX), sinA = Math.sin(angleX);
|
||||
const cosB = Math.cos(angleY), sinB = Math.sin(angleY);
|
||||
const cameraZ = -2; // Camera position tweak for better fractal appearance
|
||||
|
||||
for (let row = 0; row < this.rows; row++) {
|
||||
for (let col = 0; col < this.columns; col++) {
|
||||
const px = (col - this.columns / 2) * voxelSize;
|
||||
const py = (row - this.rows / 2) * voxelSize;
|
||||
const pz = fov;
|
||||
|
||||
const [dx, dy, dz] = this.rotate(px, py, pz, sinA, cosA, sinB, cosB);
|
||||
const [sx, sy, sz] = this.rotate(0, 0, cameraZ, sinA, cosA, sinB, cosB);
|
||||
|
||||
let depth = this.rayMarch(sx, sy, sz, dx, dy, dz, maxIterations, escapeRadius);
|
||||
output[row][col] = this.depthToAscii(depth);
|
||||
}
|
||||
}
|
||||
return output.map(line => line.join('')).join('\n');
|
||||
}
|
||||
|
||||
// Ray marching for the fractal
|
||||
rayMarch(x, y, z, dx, dy, dz, maxIterations, escapeRadius) {
|
||||
let depth = 0.0;
|
||||
for (let i = 0; i < maxIterations; i++) {
|
||||
const distance = this.computeMandelbulbDistance(x, y, z, maxIterations, escapeRadius);
|
||||
if (distance < this.detail) break;
|
||||
depth += distance;
|
||||
x += dx * distance;
|
||||
y += dy * distance;
|
||||
z += dz * distance;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
// Map depth into ASCII character
|
||||
depthToAscii(depth) {
|
||||
const index = Math.min(Math.floor(depth * 7 / this.detail), this.charset.length - 1);
|
||||
return this.charset[index];
|
||||
}
|
||||
}
|
||||
|
||||
let asciiArt = document.getElementById('asciiArt');
|
||||
let powerControl = document.getElementById('power');
|
||||
let detailControl = document.getElementById('detail');
|
||||
let columnsControl = document.getElementById('columns');
|
||||
|
||||
// Initial values
|
||||
let power = parseFloat(powerControl.value);
|
||||
let detail = parseFloat(detailControl.value);
|
||||
let columns = parseInt(columnsControl.value);
|
||||
let rows = Math.floor(columns / 2);
|
||||
|
||||
let mandelbulb = new Mandelbulb(power, detail, columns, rows);
|
||||
let angleX = 0;
|
||||
let angleY = 0;
|
||||
|
||||
function render() {
|
||||
asciiArt.textContent = mandelbulb.render(angleX, angleY);
|
||||
angleX += 0.04;
|
||||
angleY += 0.03;
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
powerControl.addEventListener('input', () => {
|
||||
power = parseFloat(powerControl.value);
|
||||
mandelbulb = new Mandelbulb(power, detail, columns, rows);
|
||||
});
|
||||
|
||||
detailControl.addEventListener('input', () => {
|
||||
detail = parseFloat(detailControl.value);
|
||||
mandelbulb = new Mandelbulb(power, detail, columns, rows);
|
||||
});
|
||||
|
||||
columnsControl.addEventListener('input', () => {
|
||||
columns = parseInt(columnsControl.value);
|
||||
rows = Math.floor(columns / 2);
|
||||
mandelbulb = new Mandelbulb(power, detail, columns, rows);
|
||||
});
|
||||
|
||||
render();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
icons/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
icons/android-chrome-256x256.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
icons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 43 KiB |
9
icons/browserconfig.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="icons/mstile-150x150.png"/>
|
||||
<TileColor>#33ccff</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
BIN
icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
icons/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
icons/mstile-150x150.png
Normal file
After Width: | Height: | Size: 26 KiB |
38
icons/safari-pinned-tab.svg
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="256.000000pt" height="256.000000pt" viewBox="0 0 256.000000 256.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,256.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M1420 2448 c-25 -11 -52 -26 -60 -33 -8 -7 -29 -16 -47 -20 -18 -3
|
||||
-48 -17 -67 -30 -65 -44 -72 -46 -109 -30 -19 8 -37 12 -40 8 -4 -3 -14 -2
|
||||
-24 2 -39 17 -60 11 -112 -28 -29 -23 -55 -47 -58 -54 -3 -7 -21 -21 -41 -30
|
||||
-20 -10 -44 -25 -53 -34 -9 -9 -23 -21 -30 -25 -8 -5 -21 -22 -29 -39 -9 -16
|
||||
-26 -34 -38 -40 -12 -5 -22 -13 -22 -17 0 -5 -6 -8 -13 -8 -7 0 -24 -8 -37
|
||||
-17 -24 -17 -72 -38 -115 -50 -35 -9 -124 -54 -135 -68 -6 -7 -36 -23 -68 -36
|
||||
-31 -12 -59 -26 -62 -29 -3 -3 -19 -12 -37 -19 -42 -17 -60 -38 -45 -53 6 -6
|
||||
8 -16 5 -23 -2 -8 0 -23 6 -34 6 -11 13 -39 16 -63 7 -58 14 -78 37 -111 11
|
||||
-16 24 -24 31 -20 7 4 6 -1 -2 -11 -22 -26 -23 -74 -4 -131 20 -60 23 -199 4
|
||||
-233 -19 -35 -17 -101 4 -116 17 -12 17 -13 -2 -18 -23 -6 -53 -64 -57 -111
|
||||
-2 -17 -5 -39 -8 -47 -3 -8 -10 -32 -16 -53 l-11 -39 62 -30 c34 -16 76 -38
|
||||
92 -48 17 -10 40 -19 52 -19 24 -1 53 -35 53 -63 0 -9 10 -19 23 -23 25 -7 34
|
||||
-11 127 -53 36 -16 106 -47 155 -68 50 -21 155 -71 233 -112 107 -55 156 -75
|
||||
190 -77 26 -2 72 -16 102 -31 51 -25 166 -74 299 -126 l54 -22 16 21 c9 12 27
|
||||
35 40 53 13 17 32 45 41 63 9 18 27 37 38 43 12 6 32 24 44 40 12 16 25 31 29
|
||||
34 3 3 10 16 14 30 13 47 47 110 58 110 7 0 25 7 41 15 16 8 34 14 40 14 14
|
||||
-3 34 5 121 46 39 19 95 45 126 59 l56 24 -8 49 c-4 26 -8 69 -8 95 -1 47 -17
|
||||
97 -36 110 -7 5 -5 15 4 30 7 12 13 42 13 67 -1 27 5 56 15 71 9 14 16 39 15
|
||||
56 -2 26 4 35 33 55 37 26 95 98 95 121 0 7 12 24 26 37 14 13 32 37 40 53 15
|
||||
28 14 31 -14 57 -16 16 -31 35 -35 44 -3 9 -24 27 -46 41 -25 16 -43 37 -51
|
||||
60 -6 19 -22 42 -36 50 -13 9 -24 22 -24 29 0 7 -13 17 -28 23 -26 10 -29 16
|
||||
-30 59 -1 26 -9 59 -17 74 -7 14 -17 56 -20 94 -7 67 -7 67 -48 87 -22 11 -50
|
||||
20 -62 20 -12 0 -30 9 -40 20 -10 11 -22 20 -26 20 -4 0 -20 5 -36 11 -15 7
|
||||
-42 11 -60 10 -71 -5 -192 84 -193 141 0 14 -58 64 -108 93 -17 11 -34 26 -37
|
||||
35 -4 14 -89 91 -97 89 -2 0 -23 -10 -48 -21z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
19
icons/site.webmanifest
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "elia.network",
|
||||
"short_name": "elia.network",
|
||||
"icons": [
|
||||
{
|
||||
"src": "android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "android-chrome-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#33ccff",
|
||||
"background_color": "#33ccff",
|
||||
"display": "standalone"
|
||||
}
|
12
index.html
@ -1,15 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- SEO Tags -->
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="elia.network">
|
||||
<meta name="description" content="swiss hosted services">
|
||||
<meta name="keywords" content="ich, hasse, mein, leben">
|
||||
<meta name="author" content="elia">
|
||||
|
||||
<!-- Social Tags for Embeds -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||
<link rel="manifest" href="icons/site.webmanifest">
|
||||
<link rel="mask-icon" href="icons/safari-pinned-tab.svg" color="#33ccffaf">
|
||||
<link rel="shortcut icon" href="icons/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#33ccff">
|
||||
<meta name="msapplication-config" content="icons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#33ccff">
|
||||
<meta property="og:title" content="elia.network">
|
||||
<meta property="og:description" content="swiss hosted services">
|
||||
<meta property="og:url" content="https://elia.network">
|
||||
|