| <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> |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Show comment
Hide comment
adammaj1
commented
Jun 28, 2015
|
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
it works on firefox 38.0.5 and ubuntu