Created
July 18, 2012 23:10
-
-
Save williame/3139606 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
it works on firefox 38.0.5 and ubuntu