thanks cKleinhuis that is helpful.
What I'm looking for is a tutorial explanation how to render a 3D mandelbox.
The simplest code I can find is from here: http://www.peternitsch.net/demo/mandelbox/
this is in a browser using WebGL pushing code onto the GPU.
So basically can any smart programming types break this program down and comment on what it's doing?
you know a breakdown of every line and variable?
I guess the shader code in var fShader = ["" I should be able to paste that out and put it in Fragmentarium?
It that code I want to truly deeply understand. I never realised you actually pasted SOURCE CODE into the GPU.
Must have it's own compiler in there !
/* WebGL Mandelbox by Peter Nitsch
http://www.peternitsch.net * credits / glsl fragment shader by Rrrola, Mandelbox formula by Tom Lowe
*
http://www.fractalforums.com/3d-fractal-generation/amazing-fractal */
const rotUnitToAngle = Math.PI / 180;
const fov = (75 *3.14159265358979/180);
var shaders = {};
var gl, canvas, sp, v;
var mvUniform, tex0Uniform, rotUniform, pUniform;
var key = Object();
var keyListener = null;
var keyIsDown;
var rotMatrix = $M([[1, 0, 0], [0, 0, -1.0], [0, 1.0, 0]]);
var pMatrix;
var fovX, fovY;
var a1, u2, u3, u4, u5, u6;
var scaleElement, iterElement, stepsElement;
var mouseButton = false;
var newMouseX = 0;
var newMouseY = 0;
var lastMouseX = 0;
var lastMouseY = 0;
var pitch = 0;
var pitchRate = 0;
var yaw = 0;
var yawRate = 0;
var camX = 0.0;
var camY = 11.0;
var camZ = 0.0;
var spd = 0.0;
var spdRate = 0.15;
var iters = 9;
var minIters = 4;
var maxIters=9;
var scale=2.8;
var maxSteps=140;
var lastIters = 4;
var lastScale = 2.4;
var lastMaxSteps = 140;
var vShader = [
"attribute vec2 pos;",
"uniform mat4 uMVMatrix;",
"uniform mat4 uPMatrix;",
"uniform mat4 uRotMatrix;",
"varying vec3 eye, dir;",
"uniform float fovX, fovY;",
"float fov2scale(float fov) { return tan(fov/2.0); }",
"void main(void) {",
" gl_Position = vec4(pos.x,pos.y,0.0,1.0);",
" eye = vec3(uMVMatrix[3]);",
" dir = vec3(uPMatrix*uMVMatrix*uRotMatrix*vec4(fov2scale(fovX)*gl_Position.x, fov2scale(fovY)*gl_Position.y, 1, 0) );",
"}"
].join("\n");
var fShader = [
"// Mandelbox shader by Rrrola",
"// Original formula by Tglad",
"// -
http://www.fractalforums.com/3d-fractal-generation/amazing-fractal",
"#ifdef GL_ES",
"precision highp float;",
"#endif",
"#define P0 p0 // standard Mandelbox",
"#define SCALE 2.8",
"#define MINRAD2 -1.77",
"#define DIST_MULTIPLIER 1.0",
"#define MAX_DIST 64.0",
"varying vec3 eye, dir;",
"//uniform vec2 par[10];",
"float",
" min_dist = 0.01, // Distance at which raymarching stops.",
" ao_eps = 0.00015, // Base distance at which ambient occlusion is estimated.",
" ao_strength = 0.1, // Strength of ambient occlusion.",
" glow_strength = 0.35, // How much glow is applied after maxSteps.",
" dist_to_color = 0.1; // How is background mixed with the surface color after maxSteps.",
"uniform int iters; // Number of fractal iterations.",
//" // color_iters = 9, // Number of fractal iterations for coloring.",
"uniform int maxSteps; // Maximum raymarching steps.",
"int color_iters = 9;",
"vec3 backgroundColor = vec3(0.0, 0.0, 0.0),",
" surfaceColor1 = vec3(0.15, 0.64, 0.91),",
" surfaceColor2 = vec3(0.9, 0.9, 0.9),",
" surfaceColor3 = vec3(0.15, 0.55, 0.55),",
" specularColor = vec3(1.0, 1.0, 0.5),",
" glowColor = vec3(0.3, 0.1, 0.3),",
" aoColor = vec3(1.0, 0, 0);",
"float minRad2 = clamp(MINRAD2, 1.0e-9, 1.0);",
"vec4 scale = vec4(SCALE, SCALE, SCALE, abs(SCALE)) / minRad2;",
"float absScalem1 = abs(SCALE - 1.0);",
"float AbsScaleRaisedTo1mIters = pow(abs(SCALE), float(1-iters));",
"float d(vec3 pos) {",
" vec4 p = vec4(pos,1), p0 = p; // p.w is the distance estimate",
" for (int i=0; i<9; i++) {",
" p.xyz = clamp(p.xyz, -1.0, 1.0) * 2.0 - p.xyz; // min;max;mad",
" float r2 = dot(p.xyz, p.xyz);",
" p *= clamp(max(minRad2/r2, minRad2), 0.0, 1.0); // dp3,div,max.sat,mul",
" p = p*scale + P0;",
" }",
" return ((length(p.xyz) - absScalem1) / p.w - AbsScaleRaisedTo1mIters) * DIST_MULTIPLIER;",
"}",
"vec3 color(vec3 pos) {",
" vec3 p = pos, p0 = p;",
" float trap = 1.0;",
" for (int i=0; i<9; i++) {",
" p.xyz = clamp(p.xyz, -1.0, 1.0) * 2.0 - p.xyz;",
" float r2 = dot(p.xyz, p.xyz);",
" p *= clamp(max(minRad2/r2, minRad2), 0.0, 1.0);",
" p = p*scale.xyz + P0.xyz;",
" trap = min(trap, r2);",
" }",
" vec2 c = clamp(vec2( 0.33*log(dot(p,p))-1.0, sqrt(trap) ), 0.0, 1.0);",
" return mix(mix(surfaceColor1, surfaceColor2, c.y), surfaceColor3, c.x);",
"}",
"float normal_eps = 0.00001;",
"vec3 normal(vec3 pos, float d_pos) {",
" vec4 Eps = vec4(0, normal_eps, 2.0*normal_eps, 3.0*normal_eps);",
" return normalize(vec3(",
" // 2-tap forward differences, error = O(eps)",
"// -d_pos+d(pos+Eps.yxx),",
"// -d_pos+d(pos+Eps.xyx),",
"// -d_pos+d(pos+Eps.xxy)",
" // 3-tap central differences, error = O(eps^2)",
" -d(pos-Eps.yxx)+d(pos+Eps.yxx),",
" -d(pos-Eps.xyx)+d(pos+Eps.xyx),",
" -d(pos-Eps.xxy)+d(pos+Eps.xxy)",
" // 4-tap forward differences, error = O(eps^3)",
"// -2.0*d(pos-Eps.yxx)-3.0*d_pos+6.0*d(pos+Eps.yxx)-d(pos+Eps.zxx),",
"// -2.0*d(pos-Eps.xyx)-3.0*d_pos+6.0*d(pos+Eps.xyx)-d(pos+Eps.xzx),",
"// -2.0*d(pos-Eps.xxy)-3.0*d_pos+6.0*d(pos+Eps.xxy)-d(pos+Eps.xxz)",
" // 5-tap central differences, error = O(eps^4)",
"// d(pos-Eps.zxx)-8.0*d(pos-Eps.yxx)+8.0*d(pos+Eps.yxx)-d(pos+Eps.zxx),",
"// d(pos-Eps.xzx)-8.0*d(pos-Eps.xyx)+8.0*d(pos+Eps.xyx)-d(pos+Eps.xzx),",
"// d(pos-Eps.xxz)-8.0*d(pos-Eps.xxy)+8.0*d(pos+Eps.xxy)-d(pos+Eps.xxz)",
" ));",
"}",
"vec3 blinn_phong(vec3 normal, vec3 view, vec3 light, vec3 diffuseColor) {",
" vec3 halfLV = normalize(light + view);",
" float spe = pow(max( dot(normal, halfLV), 0.0 ), 32.0);",
" float dif = dot(normal, light) * 0.5 + 0.75;",
" return dif*diffuseColor + spe*specularColor;",
"}",
"float ambient_occlusion(vec3 p, vec3 n) {",
" float ao = 1.0, w = ao_strength/ao_eps;",
" float dist = 2.0 * ao_eps;",
" for (int i=0; i<5; i++) {",
" float D = d(p + n*dist);",
" ao -= (dist-D) * w;",
" w *= 0.5;",
" dist = dist*2.0 - ao_eps; // 2,3,5,9,17",
" }",
" return clamp(ao, 0.0, 1.0);",
"}",
"void main() {",
" vec3 p = eye, dp = normalize(dir);",
" float totalD = 0.0, D = 3.4e38, extraD = 0.0, lastD;",
// " int steps;",
" for (int steps=0; steps<140; steps++) {",
" lastD = D;",
" D = d(p + totalD * dp);",
" if (extraD > 0.0 && D < extraD) {",
" totalD -= extraD;",
" extraD = 0.0;",
" D = 3.4e38;",
// " steps--;",
" continue;",
" }",
" if (D < min_dist || D > MAX_DIST) break;",
" totalD += D;",
" totalD += extraD = 0.096 * D*(D+extraD)/lastD;",
" }",
" p += totalD * dp;",
" vec3 col = backgroundColor;",
" if (D < MAX_DIST) {",
" vec3 n = normal(p, D);",
" col = color(p);",
" col = blinn_phong(n, -dp, normalize(eye+vec3(0,1,0)+dp), col);",
" col = mix(aoColor, col, ambient_occlusion(p, n));",
" if (D > min_dist) {",
" col = mix(col, backgroundColor, clamp(log(D/min_dist) * dist_to_color, 0.0, 1.0));",
" }",
" }",
" col = mix(col, glowColor, float(140)/float(maxSteps) * glow_strength);",
" gl_FragColor = vec4(col, 1);",
"}"
].join("\n");
function perspective(fovy, aspect, znear, zfar) {
pMatrix = makePerspective(fovy, aspect, znear, zfar)
}
function multMatrix(m) {
mvMatrix = mvMatrix.x(m);
}
function setMatrixParam(param, matrix) {
gl.uniformMatrix4fv(param, false, new Float32Array(matrix.make4x4().flatten()));
}
function doRotate(units, v) {
var angle = units * rotUnitToAngle;
v = rotMatrix.inv().x($V(v));
rotMatrix = rotMatrix.x(Matrix.Rotation(angle, v));
setMatrixParam(rotUniform, rotMatrix);
}
function init() {
canvas = document.getElementById("canvas");
gl = canvas.getContext("experimental-webgl");
fovX = Math.atan(Math.tan(fov/2)*canvas.width/canvas.height)*2;
fovY = fov;
if (!("sp" in shaders)) {
shaders.vs = gl.createShader(gl.VERTEX_SHADER);
shaders.fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shaders.vs, vShader);
gl.shaderSource(shaders.fs, fShader);
gl.compileShader(shaders.vs);
gl.compileShader(shaders.fs);
shaders.sp = gl.createProgram();
gl.attachShader(shaders.sp, shaders.vs);
gl.attachShader(shaders.sp, shaders.fs);
gl.linkProgram(shaders.sp);
if (!gl.getProgramParameter(shaders.sp, gl.LINK_STATUS)) {
alert(gl.getProgramInfoLog(shaders.sp));
}
gl.useProgram(shaders.sp);
}
sp = shaders.sp;
pUniform = gl.getUniformLocation(sp, "uPMatrix");
mvUniform = gl.getUniformLocation(sp, "uMVMatrix");
rotUniform = gl.getUniformLocation(sp, "uRotMatrix");
a1 = gl.getAttribLocation(shaders.sp, "pos");
u2 = gl.getUniformLocation(shaders.sp, "fovX");
u3 = gl.getUniformLocation(shaders.sp, "fovY");
u4 = gl.getUniformLocation(shaders.sp, "iters");
// u5 = gl.getUniformLocation(shaders.sp, "scale");
u6 = gl.getUniformLocation(shaders.sp, "maxSteps");
}
function initUI() {
scaleElement = document.getElementById("scale");
scale = scaleElement.value;
iterElement = document.getElementById("iterations");
maxIters = iterElement.value;
stepsElement = document.getElementById("maxSteps");
maxSteps = stepsElement.value;
scaleElement.addEventListener("change", function(ev) {
scale = scaleElement.value;
return true;
}, false);
iterElement.addEventListener("change", function(ev) {
maxIters = iterElement.value;
return true;
}, false);
stepsElement.addEventListener("change", function(ev) {
maxSteps = stepsElement.value;
return true;
}, false);
}
function initMouse() {
canvas.addEventListener("mousedown", function(ev) {
mouseButton = true;
return true;
}, false);
canvas.addEventListener("mousemove", function(ev) {
newMouseX = ev.clientX;
newMouseY = ev.clientY;
if (!mouseButton) {
lastMouseX = ev.clientX;
lastMouseY = ev.clientY;
}
return true;
}, false);
canvas.addEventListener("mouseup", function(ev) {
mouseButton = false;
}, false);
canvas.addEventListener("mouseout", function(ev) {
mouseButton = false;
}, false);
}
function initKeyboard() {
window.addEventListener("keydown", function(e) {
key[e.keyCode] = true;
}, false);
window.addEventListener("keyup", function(e) {
key[e.keyCode] = false;
}, false);
}
function renderStart() {
init();
initMouse();
initKeyboard();
//initUI();
var vertices = new Float32Array([ -1., -1., 1., -1., -1., 1., 1., -1., 1., 1., -1., 1.]);
v = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, v);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
setMatrixParam(rotUniform, rotMatrix);
perspective(100, 1, 0.1, 100.0);
setInterval(function() { draw(); }, 100);
}
function draw() {
iters = maxIters;
if (key[38] || key[87]) {
spd = spdRate;
iters = minIters;
} else if (key[40] || key[83]) {
spd = -spdRate;
iters = minIters;
} else {
spd = 0;
}
loadIdentity();
pushMatrix();
mvRotate(pitch, [1, 0, 0]);
mvRotate(yaw, [0, 0, 1]);
mvTranslate([camX, camY, camZ]);
var mDiffY = lastMouseY - newMouseY;
var mDiffX = lastMouseX - newMouseX;
yaw += yawRate;
pitch += pitchRate;
if (mouseButton) {
if ( mDiffY != 0 )
camZ += mDiffY / 120;
if ( mDiffX != 0 )
camX += mDiffX / 120;
iters = minIters;
}
if (spd != 0) {
camX += Math.sin(yaw / 180 * Math.PI) * spd;
camY += -Math.cos(yaw / 180 * Math.PI) * spd;
}
if(lastIters != maxIters || lastScale != scale || lastMaxSteps != maxSteps) {
setMatrixParam(pUniform, pMatrix);
setMatrixParam(mvUniform, mvMatrix);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shaders.sp);
gl.bindBuffer(gl.ARRAY_BUFFER, v);
gl.uniform1f(u2, fovX);
gl.uniform1f(u3, fovY);
gl.uniform1i(u4, iters);
gl.uniform1f(u5, scale);
gl.uniform1i(u6, maxSteps);
gl.vertexAttribPointer(a1, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a1);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.disableVertexAttribArray(a1);
}
lastMouseX = newMouseX;
lastMouseY = newMouseY;
lastIters = iters;
lastScale = scale;
lastMaxSteps = maxSteps;
}