Skip to content

Instantly share code, notes, and snippets.

@williame
Created July 18, 2012 23:10
Show Gist options
  • Save williame/3139606 to your computer and use it in GitHub Desktop.
Save williame/3139606 to your computer and use it in GitHub Desktop.
<html>
<head>
<title>Will's quick mandelbulb GLSL</title>
<script type="application/javascript">
var canvas, gl, program;
function start() {
window.onerror = function(msg, url, lineno) {
alert(url + '(' + lineno + '): ' + msg);
};
canvas = document.getElementById("game-canvas");
try {
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
} catch(e) {}
if(!gl) {
alert("Unable to initialize WebGL. Your browser may not support it.");
return;
}
gl.clearColor(0,0,0,1);
var vs =
'uniform mat4 inv_mvp;\n' +
'attribute vec2 vertex;\n' +
'varying vec2 pos;\n' +
'varying vec3 near, far;\n'+
'void main() {\n'+
' gl_Position = vec4(vertex,0,1);\n' +
' pos = vertex;\n' +
' vec4 p = (inv_mvp * vec4(pos,0,1));\n' +
' near = p.xyz / p.w;\n' +
' p = (inv_mvp * vec4(pos,1,1));\n' +
' far = p.xyz / p.w;\n' +
'}\n';
var fs =
'precision mediump float;\n' +
'varying vec2 pos;\n' +
'varying vec3 near, far;\n' +
'const float Power = 8.0;\n' +
'const float BailOut = 7.0;\n' +
'const int Iterations = 5;\n' +
'float DE(vec3 pos) {\n' +
' vec3 z = pos;\n' +
' float dr = 1.0;\n' +
' float r = 0.0;\n' +
' for(int i = 0; i < Iterations; i++) {\n' +
' r = length(z);\n' +
' if (r>BailOut) break;\n' +
' // convert to polar coordinates\n' +
' float theta = acos(z.z/r);\n' +
' float phi = atan(z.y,z.x);\n' +
' dr = pow(r,Power-1.0)*Power*dr + 1.0;\n' +
' // scale and rotate the point\n' +
' float zr = pow(r,Power);\n' +
' theta = theta*Power;\n' +
' phi = phi*Power;\n' +
' // convert back to cartesian coordinates\n' +
' z = zr*vec3(sin(theta)*cos(phi), sin(phi)*sin(theta), cos(theta));\n' +
' z+=pos;\n' +
' }\n' +
' return 0.5*log(r)*r/dr;\n' +
'}\n' +
'const int MaxRaySteps = 100;\n' +
'const float MinimumDistance = 0.00001;\n' +
'void main() {\n' +
' vec3 direction = far-near;\n' +
' float maximumDistance = length(direction);\n' +
' direction = normalize(direction);\n' +
' float totalDistance = 0.0, distance;\n' +
' int steps = 0;\n' +
' for(int i=0; i<MaxRaySteps; i++) {\n' +
' distance = DE(near + totalDistance * direction);\n' +
' totalDistance += distance;\n' +
' if(distance < MinimumDistance) break;\n' +
' if(totalDistance > maximumDistance) discard;\n' +
' steps++;\n' +
' }\n' +
' // super simple colouring based on number of steps\n' +
' distance = 1.0-float(steps)/float(MaxRaySteps);\n' +
' gl_FragColor = vec4(distance,distance,distance,1.0);\n' +
'}\n';
program = createProgram(vs,fs);
gl.useProgram(program);
var vertexPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexPosBuffer);
var vertices = [-1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1]; // two triangles that fill the screen, promise
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
program.vertexPosAttrib = gl.getAttribLocation(program,'vertex');
gl.enableVertexAttribArray(program.vertexPosAttrib);
gl.vertexAttribPointer(program.vertexPosAttrib,2,gl.FLOAT,false,0,0);
render();
}
var start_time = (new Date()).getTime();
function render() {
var elapsed = (((new Date()).getTime() - start_time) / 1000) / 2;
gl.clear(gl.COLOR_BUFFER_BIT);
// we swing and spin a bit
var distance = 2,
modelview = createLookAt([Math.cos(elapsed)*Math.sin(elapsed)*distance,Math.sin(elapsed)*distance,Math.cos(elapsed)*1.1],[0,0,0],[0,0,1]),
perspective = createPerspective(60.0,canvas.width/canvas.height,0.1,distance+2),
inv_mvp = mat4_inverse(mat4_multiply(perspective,modelview));
gl.uniformMatrix4fv(gl.getUniformLocation(program,"inv_mvp"),false,new Float32Array(inv_mvp));
gl.drawArrays(gl.TRIANGLES,0,6);
window.requestAnimFrame(render);
}
function createShader(str,type) {
var shader = gl.createShader(type);
gl.shaderSource(shader,str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader,gl.COMPILE_STATUS))
throw gl.getShaderInfoLog(shader);
return shader;
}
function createProgram(vstr,fstr) {
var program = gl.createProgram();
var vshader = createShader(vstr,gl.VERTEX_SHADER);
var fshader = createShader(fstr,gl.FRAGMENT_SHADER);
gl.attachShader(program,vshader);
gl.attachShader(program,fshader);
gl.linkProgram(program);
return program;
}
function createLookAt(eye,centre,up) {
if (eye[0] == centre[0] && eye[1] == centre[1] && eye[2] == centre[2])
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
var z0,z1,z2,x0,x1,x2,y0,y1,y2,len;
//vec3.direction(eye, center, z);
z0 = eye[0] - centre[0];
z1 = eye[1] - centre[1];
z2 = eye[2] - centre[2];
// normalize (no check needed for 0 because of early return)
len = 1/Math.sqrt(z0*z0 + z1*z1 + z2*z2);
z0 *= len;
z1 *= len;
z2 *= len;
//vec3.normalize(vec3.cross(up, z, x));
x0 = up[1]*z2 - up[2]*z1;
x1 = up[2]*z0 - up[0]*z2;
x2 = up[0]*z1 - up[1]*z0;
len = Math.sqrt(x0*x0 + x1*x1 + x2*x2);
if (!len) {
x0 = 0;
x1 = 0;
x2 = 0;
} else {
len = 1/len;
x0 *= len;
x1 *= len;
x2 *= len;
};
//vec3.normalize(vec3.cross(z, x, y));
y0 = z1*x2 - z2*x1;
y1 = z2*x0 - z0*x2;
y2 = z0*x1 - z1*x0;
len = Math.sqrt(y0*y0 + y1*y1 + y2*y2);
if (!len) {
y0 = 0;
y1 = 0;
y2 = 0;
} else {
len = 1/len;
y0 *= len;
y1 *= len;
y2 *= len;
}
return [x0, y0, z0, 0,
x1, y1, z1, 0,
x2, y2, z2, 0,
-(x0*eye[0] + x1*eye[1] + x2*eye[2]), -(y0*eye[0] + y1*eye[1] + y2*eye[2]), -(z0*eye[0] + z1*eye[1] + z2*eye[2]), 1];
}
function createPerspective(fovy,aspect,near,far) {
var top = near*Math.tan(fovy*Math.PI/360.0);
var right = top*aspect, left = -right, bottom = -top;
var rl = (right-left);
var tb = (top-bottom);
var fn = (far-near);
return [(near*2)/rl, 0, 0, 0,
0, (near*2)/tb, 0, 0,
(right+left)/rl, (top+bottom)/tb, -(far+near)/fn, -1,
0, 0, -(far*near*2)/fn, 0];
}
function mat4_multiply(a,b) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
var b00 = b[0], b01 = b[1], b02 = b[2], b03 = b[3];
var b10 = b[4], b11 = b[5], b12 = b[6], b13 = b[7];
var b20 = b[8], b21 = b[9], b22 = b[10], b23 = b[11];
var b30 = b[12], b31 = b[13], b32 = b[14], b33 = b[15];
return [b00*a00 + b01*a10 + b02*a20 + b03*a30,
b00*a01 + b01*a11 + b02*a21 + b03*a31,
b00*a02 + b01*a12 + b02*a22 + b03*a32,
b00*a03 + b01*a13 + b02*a23 + b03*a33,
b10*a00 + b11*a10 + b12*a20 + b13*a30,
b10*a01 + b11*a11 + b12*a21 + b13*a31,
b10*a02 + b11*a12 + b12*a22 + b13*a32,
b10*a03 + b11*a13 + b12*a23 + b13*a33,
b20*a00 + b21*a10 + b22*a20 + b23*a30,
b20*a01 + b21*a11 + b22*a21 + b23*a31,
b20*a02 + b21*a12 + b22*a22 + b23*a32,
b20*a03 + b21*a13 + b22*a23 + b23*a33,
b30*a00 + b31*a10 + b32*a20 + b33*a30,
b30*a01 + b31*a11 + b32*a21 + b33*a31,
b30*a02 + b31*a12 + b32*a22 + b33*a32,
b30*a03 + b31*a13 + b32*a23 + b33*a33];
}
function mat4_inverse(mat) {
var a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3];
var a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7];
var a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11];
var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15];
var b00 = a00*a11 - a01*a10;
var b01 = a00*a12 - a02*a10;
var b02 = a00*a13 - a03*a10;
var b03 = a01*a12 - a02*a11;
var b04 = a01*a13 - a03*a11;
var b05 = a02*a13 - a03*a12;
var b06 = a20*a31 - a21*a30;
var b07 = a20*a32 - a22*a30;
var b08 = a20*a33 - a23*a30;
var b09 = a21*a32 - a22*a31;
var b10 = a21*a33 - a23*a31;
var b11 = a22*a33 - a23*a32;
var invDet = 1/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
return [
(a11*b11 - a12*b10 + a13*b09)*invDet,
(-a01*b11 + a02*b10 - a03*b09)*invDet,
(a31*b05 - a32*b04 + a33*b03)*invDet,
(-a21*b05 + a22*b04 - a23*b03)*invDet,
(-a10*b11 + a12*b08 - a13*b07)*invDet,
(a00*b11 - a02*b08 + a03*b07)*invDet,
(-a30*b05 + a32*b02 - a33*b01)*invDet,
(a20*b05 - a22*b02 + a23*b01)*invDet,
(a10*b10 - a11*b08 + a13*b06)*invDet,
(-a00*b10 + a01*b08 - a03*b06)*invDet,
(a30*b04 - a31*b02 + a33*b00)*invDet,
(-a20*b04 + a21*b02 - a23*b00)*invDet,
(-a10*b09 + a11*b07 - a12*b06)*invDet,
(a00*b09 - a01*b07 + a02*b06)*invDet,
(-a30*b03 + a31*b01 - a32*b00)*invDet,
(a20*b03 - a21*b01 + a22*b00)*invDet];
}
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 0);
};
})();
</script>
</head>
<body onload="start()">
<canvas id="game-canvas" style="width:100%;height:100%;">
Your browser does not support WebGL :(
</canvas>
</body>
</html>
@adammaj1
Copy link

it works on firefox 38.0.5 and ubuntu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment