Created
September 23, 2014 17:30
-
-
Save kioku-systemk/97dde853001ec7323115 to your computer and use it in GitHub Desktop.
Interactive WebGL aobench
This file contains 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> | |
<title>WebGL aobench</title> | |
<!-- | |
aobench project : https://code.google.com/p/aobench/ | |
original GLSL version http://kioku.sys-k.net/4kgfxmon/ | |
based on ported WebGL version by h013 : http://glsl.heroku.com/e#3159.0 | |
// porting to QC by george toledo, then some tweaks on random func | |
// by psonice to get rid of abberant dots, then some more tweaks | |
// to get rid of vertex shader dependencies and transfer to webGL, george toledo. | |
Update by kioku. [2014/09/23] | |
--> | |
<script id="shader" type="x-shader/x-fragment"> | |
#ifdef GL_ES | |
precision highp float; | |
#endif | |
uniform float time; | |
uniform vec2 resolution; | |
uniform vec2 eyerot; | |
struct Ray | |
{ | |
vec3 org; | |
vec3 dir; | |
}; | |
struct Sphere | |
{ | |
vec3 center; | |
float radius; | |
}; | |
struct Plane | |
{ | |
vec3 p; | |
vec3 n; | |
}; | |
struct Intersection | |
{ | |
float t; | |
vec3 p; // hit point | |
vec3 n; // normal | |
int hit; | |
}; | |
void shpere_intersect(Sphere s, Ray ray, inout Intersection isect) | |
{ | |
// rs = ray.org - sphere.center | |
vec3 rs = ray.org - s.center; | |
float B = dot(rs, ray.dir); | |
float C = dot(rs, rs) - (s.radius * s.radius); | |
float D = B * B - C; | |
if (D > 0.0) | |
{ | |
float t = -B - sqrt(D); | |
if ( (t > 0.0) && (t < isect.t) ) | |
{ | |
isect.t = t; | |
isect.hit = 1; | |
// calculate normal. | |
vec3 p = vec3(ray.org.x + ray.dir.x * t, | |
ray.org.y + ray.dir.y * t, | |
ray.org.z + ray.dir.z * t); | |
vec3 n = p - s.center; | |
n = normalize(n); | |
isect.n = n; | |
isect.p = p; | |
} | |
} | |
} | |
void plane_intersect(Plane pl, Ray ray, inout Intersection isect) | |
{ | |
// d = -(p . n) | |
// t = -(ray.org . n + d) / (ray.dir . n) | |
float d = -dot(pl.p, pl.n); | |
float v = dot(ray.dir, pl.n); | |
if (abs(v) < 1.0e-6) | |
return; // the plane is parallel to the ray. | |
float t = -(dot(ray.org, pl.n) + d) / v; | |
if ( (t > 0.0) && (t < isect.t) ) | |
{ | |
isect.hit = 1; | |
isect.t = t; | |
isect.n = pl.n; | |
vec3 p = vec3(ray.org.x + t * ray.dir.x, | |
ray.org.y + t * ray.dir.y, | |
ray.org.z + t * ray.dir.z); | |
isect.p = p; | |
} | |
} | |
Sphere sphere[3]; | |
Plane plane; | |
void Intersect(Ray r, inout Intersection i) | |
{ | |
for (int c = 0; c < 3; c++) | |
{ | |
shpere_intersect(sphere[c], r, i); | |
} | |
plane_intersect(plane, r, i); | |
} | |
void orthoBasis(out vec3 basis[3], vec3 n) | |
{ | |
basis[2] = vec3(n.x, n.y, n.z); | |
basis[1] = vec3(0.0, 0.0, 0.0); | |
if ((n.x < 0.6) && (n.x > -0.6)) | |
basis[1].x = 1.0; | |
else if ((n.y < 0.6) && (n.y > -0.6)) | |
basis[1].y = 1.0; | |
else if ((n.z < 0.6) && (n.z > -0.6)) | |
basis[1].z = 1.0; | |
else | |
basis[1].x = 1.0; | |
basis[0] = cross(basis[1], basis[2]); | |
basis[0] = normalize(basis[0]); | |
basis[1] = cross(basis[2], basis[0]); | |
basis[1] = normalize(basis[1]); | |
} | |
float random(float seed) | |
{ | |
float seed2 = (mod((seed)*1364.0+626.0, 509.0)); | |
return seed2/509.0; | |
} | |
vec3 computeAO(inout Intersection isect) | |
{ | |
int ntheta = 6;//irritating, guess I cant use a variable in a loop in webGL? Maybe I heard this already. -gtoledo | |
int nphi = 6; | |
float eps = 0.0001; | |
// Slightly move ray org towards ray dir to avoid numerical probrem. | |
vec3 p = vec3(isect.p.x + eps * isect.n.x, | |
isect.p.y + eps * isect.n.y, | |
isect.p.z + eps * isect.n.z); | |
// Calculate orthogonal basis. | |
vec3 basis[3]; | |
orthoBasis(basis, isect.n); | |
float occlusion = 0.0; | |
for (int j = 0; j < 6; j++)//woulda used ntheta.-gtoledo | |
{ | |
for (int i = 0; i < 6; i++)//woulda used nphi.-gtoledo | |
{ | |
float s = isect.p.x += isect.p.y * 57. + isect.p.z * 21.; | |
s = sin(cos(s) * s)*.5+.5; | |
// Pick a random ray direction with importance sampling. | |
// p = cos(theta) / 3.141592 | |
float r = random(s*0.7); | |
float phi = 2.0 * 3.141592 * random(s*3000.); | |
vec3 ref; | |
ref.x = cos(phi) * sqrt(1.0 - r); | |
ref.y = sin(phi) * sqrt(1.0 - r); | |
ref.z = sqrt(r); | |
// local -> global | |
vec3 rray; | |
rray.x = ref.x * basis[0].x + ref.y * basis[1].x + ref.z * basis[2].x; | |
rray.y = ref.x * basis[0].y + ref.y * basis[1].y + ref.z * basis[2].y; | |
rray.z = ref.x * basis[0].z + ref.y * basis[1].z + ref.z * basis[2].z; | |
vec3 raydir = vec3(rray.x, rray.y, rray.z); | |
Ray ray; | |
ray.org = p; | |
ray.dir = raydir; | |
Intersection occIsect; | |
occIsect.hit = 0; | |
occIsect.t = 1.0e+30; | |
occIsect.n = occIsect.p = vec3(0, 0, 0); | |
Intersect(ray, occIsect); | |
if (occIsect.hit != 0) | |
occlusion += 1.0; | |
} | |
} | |
// [0.0, 1.0] | |
occlusion = (float(ntheta * nphi) - occlusion) / float(ntheta * nphi); | |
return vec3(occlusion, occlusion, occlusion); | |
} | |
void main() | |
{ | |
sphere[0].center = vec3(-2.0, 0.0, -0.5); | |
sphere[0].radius = 0.5; | |
sphere[1].center = vec3(-0.5, 0.0, 0.0); | |
sphere[1].radius = 0.5; | |
sphere[2].center = vec3(1.0, 0.0, 0.8); | |
sphere[2].radius = 0.5; | |
plane.p = vec3(0,-0.5, 0); | |
plane.n = vec3(0, 1.0, 0); | |
Intersection i; | |
i.hit = 0; | |
i.t = 1.0e+30; | |
i.n = i.p = vec3(0, 0, 0); | |
vec3 org = vec3(0,0,3); | |
vec2 pixel = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy; | |
float asp = resolution.x / resolution.y; | |
vec3 dir = normalize(vec3(asp*pixel.x, pixel.y, 2.0) - org); | |
float xcs = cos(-eyerot.x / 180.0 * 3.1415926); | |
float xsn = sin(-eyerot.x / 180.0 * 3.1415926); | |
float ycs = cos(-eyerot.y / 180.0 * 3.1415926); | |
float ysn = sin(-eyerot.y / 180.0 * 3.1415926); | |
mat3 rotX = mat3(1.0, 0.0, 0.0, | |
0.0, ycs, ysn, | |
0.0,-ysn, ycs); | |
mat3 rotY = mat3(xcs, 0.0, -xsn, | |
0.0, 1.0, 0.0, | |
xsn, 0.0, xcs); | |
dir = rotY * rotX * dir; | |
org = rotY * rotX * org; | |
Ray r; | |
r.org = org; | |
r.dir = normalize(dir); | |
vec4 col = vec4(0,0,0,0); | |
Intersect(r, i); | |
if (i.hit != 0) | |
{ | |
col.rgb = computeAO(i); | |
} | |
gl_FragColor = col; | |
gl_FragColor.a =1.0; | |
} | |
</script> | |
<script> | |
var mx, my, | |
ex = 0, ey = 0, | |
mpress = false; | |
window.onmousedown = function (e) { | |
mx = e.clientX; | |
my = e.clientY; | |
mpress = true; | |
} | |
window.onmouseup = function (e) { | |
mpress = false; | |
} | |
window.onmousemove = function (e) { | |
if (mpress) { | |
ex += e.clientX - mx; | |
ey += e.clientY - my; | |
} | |
mx = e.clientX; | |
my = e.clientY; | |
} | |
window.ontouchstart = function (e) { | |
e.preventDefault(); | |
mx = e.touches[0].pageX; | |
my = e.touches[0].pageY; | |
mpress = true; | |
} | |
window.ontouchend = function (e) { | |
e.preventDefault(); | |
mpress = false; | |
} | |
window.ontouchmove = function (e) { | |
e.preventDefault(); | |
if (mpress) { | |
ex += e.touches[0].pageX - mx; | |
ey += e.touches[0].pageY - my; | |
} | |
mx = e.touches[0].pageX; | |
my = e.touches[0].pageY; | |
} | |
window.onload = function(){ | |
var c = document.getElementById('c'), | |
g = c.getContext('webgl') || c.getContext('experimental-webgl'); | |
c.style.position = "fixed"; | |
c.style.left = c.style.top = 0; | |
with(g){ | |
var fs= createShader(FRAGMENT_SHADER), | |
vs= createShader(VERTEX_SHADER), | |
shader = document.getElementById("shader").text; | |
shaderSource(vs, "attribute vec4 p;void main(){gl_Position=p;}"); | |
shaderSource(fs, shader); | |
compileShader(vs); | |
compileShader(fs); | |
if(!g.getShaderParameter(fs, g.COMPILE_STATUS)){ | |
console.log(g.getShaderInfoLog(fs)); | |
//return; | |
} | |
pr = createProgram(); | |
attachShader(pr, vs); | |
attachShader(pr, fs); | |
linkProgram(pr); | |
bindBuffer(ARRAY_BUFFER, createBuffer()); | |
bufferData(ARRAY_BUFFER, new Float32Array([1,1,1,-3,-3,1]), STATIC_DRAW); | |
var stm = new Date().getTime(); | |
var mW, mH, frameCount = 0, oldDate = new Date(); | |
function draw(){with(g){ | |
var w = window.innerWidth, | |
h = window.innerHeight, | |
date = new Date(), | |
fps = document.getElementById('fps'); | |
// fps | |
frameCount = frameCount + 1; | |
if (date - oldDate > 1000) { | |
fps.innerHTML = frameCount + 'fps'; | |
frameCount = 0; | |
oldDate = date; | |
} | |
if (w != mW || h != mH) { | |
mW = c.width = w; | |
mH = c.height = h; | |
g.viewport(0, 0, w, h); | |
} | |
useProgram(pr); | |
uniform2fv(getUniformLocation(pr,"resolution"),[mW,mH]); | |
uniform1f(getUniformLocation(pr,"time"),(new Date().getTime() - stm)/1000.0); | |
uniform2fv(getUniformLocation(pr,"eyerot"),[ex,ey]); | |
enableVertexAttribArray(0); | |
vertexAttribPointer(0, 2, FLOAT, false, 0, 0); | |
drawArrays(TRIANGLES, 0, 3); | |
}} | |
window.requestAnimFrame = (function(){ | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
function( callback ){ | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
(function animloop(){ | |
requestAnimFrame(animloop); | |
draw(); | |
})(); | |
} | |
} | |
</script> | |
</head> | |
<body> | |
<canvas id="c" ></canvas> | |
<p id="fps" style="position:absolute;left:5px;top:5px;color:red">0fps</p> | |
<p style="position:absolute;right:5px;bottom:5px">Interactive WebGL aobench [coded by <a href="https://twitter.com/kioku_systemk">@kioku_systemk</a>] / | |
<a href="https://code.google.com/p/aobench/" >about aobench</a> | |
</p> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment