I am familiar with the idea of bailing for the Mandelbrot Set and then coloring based on the number of iterations. I can somewhat generalize the idea to the Mandelbox in 2D although it is less clear what the bailout value should be. I know that for the typical values (scale = 2.0, box fold = 1.0, min radius = 0.5, and fixed radius = 1.0) that the Mandelbox stretches from -6.0 to 6.0 along each dimension. This would make me think that a bailout value should be 6.0 (or 36.0 without taking the square root)
Also I have a fairly good understanding of sphere tracing and creating a Mandelbox in 3D. However what I don't understand is why the returned value based on length of the marching point and the running derivative works. Also it does not make sense to me why no bail condition is needed. Simpler shapes like the Euclidean ones Iñigo Quilez has on his websites make sense to me
I have two Processing sketches that are as similar as I could make them for posting here (they do not make use of much Processing specific code to make it easier to follow in this post). The 2D version iterates from -8.0 to 8.0 along x and y and uses a bail condition of 1024.0 because 36.0 doesn't seem to be large enough for whatever reason. The 3D version places the camera at (0.0, 0.0, -25.0), the Mandelbox is at the origin, and the rays fire down the z-axis
Can anyone explain the difference between 2D and 3D (specifically bailing)? Below are the two Processing sketches if anyone cares to take a look
2D Mandelbox:
float scale = 2.0;
int maxIterations = 20;
void setup() {
size(800, 800, P2D);
loadPixels();
noLoop();
}
void draw() {
int start = millis();
float delta = 16.0/max(width, height);
float cx = -8.0;
for (int i = 0; i < width; i++) {
float cy = -8.0;
for (int j = 0; j < height; j++) {
float zx = cx;
float zy = cy;
int k = 0;
for (; k < maxIterations; k++) {
if (zx > 1.0) zx = 2.0-zx;
else if (zx < -1.0) zx = -2.0-zx;
if (zy > 1.0) zy = 2.0-zy;
else if (zy < -1.0) zy = -2.0-zy;
float dot = zx*zx+zy*zy;
if (dot < 0.5) {
zx *= 2.0;
zy *= 2.0;
} else if (dot < 1.0) {
float inversion = 1.0/dot;
zx *= inversion;
zy *= inversion;
}
zx = scale*zx+cx;
zy = scale*zy+cy;
if (dot > 1024.0) break;
}
if (k == maxIterations) pixels[i+j*width] = 0;
else pixels[i+j*width] = 0xffffffff;
cy += delta;
}
cx += delta;
}
println("Time: "+(millis()-start));
updatePixels();
}
3D Mandelbox:
float scale = 2.0;
int maxIterations = 20;
float maxDist = 50.0;
float minDist = 0.0001;
void setup() {
size(800, 600, P2D);
loadPixels();
noLoop();
}
float mandelboxDist(float zx, float zy, float zz) {
float d = 1.0;
float cx = zx;
float cy = zy;
float cz = zz;
for (int i = 0; i < maxIterations; i++) {
if (zx > 1.0) zx = 2.0-zx;
else if (zx < -1.0) zx = -2.0-zx;
if (zy > 1.0) zy = 2.0-zy;
else if (zy < -1.0) zy = -2.0-zy;
if (zz > 1.0) zz = 2.0-zz;
else if (zz <- 1.0) zz = -2.0-zz;
float dot = zx*zx+zy*zy+zz*zz;
if (dot < 0.5) {
zx *= 2.0;
zy *= 2.0;
zz *= 2.0;
d *= 2.0;
} else if (dot < 1.0) {
float inversion = 1.0/dot;
zx *= inversion;
zy *= inversion;
zz *= inversion;
d *= inversion;
}
zx = scale*zx+cx;
zy = scale*zy+cy;
zz = scale*zz+cz;
d = d*scale+1.0;
}
return sqrt(zx*zx+zy*zy+zz*zz)/d;
}
void draw() {
int start = millis();
float ratio = (float)height/width;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
float cameraDirX = (float)i/width-0.5;
float cameraDirY = ((float)j/height-0.5)*ratio;
float cameraDirZ = 1.0;
float normMult = 1.0/sqrt(cameraDirX*cameraDirX+cameraDirY*cameraDirY+1.0);
cameraDirX *= normMult;
cameraDirY *= normMult;
cameraDirZ *= normMult;
float marchX = 0.0;
float marchY = 0.0;
float marchZ = -25.0;
float t = 0.0;
float currDist = maxDist;
int steps = 255;
for (; steps >= 0; steps--) {
currDist = mandelboxDist(marchX, marchY, marchZ);
if (currDist < minDist) break;
t += currDist;
if (t > maxDist) break;
marchX = cameraDirX*t;
marchY = cameraDirY*t;
marchZ = cameraDirZ*t-25.0;
}
if (currDist < minDist)
pixels[i+j*width] = 0xff000000|(steps<<16)|(steps<<8)|steps;
else pixels[i+j*width] = 0;
}
}
println("Time: "+(millis()-start));
updatePixels();
}