Created
October 19, 2008 16:00
-
-
Save syoyo/17872 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
var IMAGE_WIDTH = 128 | |
var IMAGE_HEIGHT = 128 | |
var NSUBSAMPLES = 2 | |
var NAO_SAMPLES = 4 | |
function vec(x, y, z) | |
{ | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
} | |
function vadd(a, b) | |
{ | |
return new vec(a.x + b.x, a.y + b.y, a.z + b.z); | |
} | |
function vsub(a, b) | |
{ | |
return new vec(a.x - b.x, a.y - b.y, a.z - b.z); | |
} | |
function vcross(a, b) | |
{ | |
return new vec(a.y * b.z - a.z * b.y, | |
a.z * b.x - a.x * b.z, | |
a.x * b.y - a.y * b.x); | |
} | |
function vdot(a, b) | |
{ | |
return (a.x * b.x + a.y * b.y + a.z * b.z); | |
} | |
function vlength(a) | |
{ | |
return Math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z); | |
} | |
function vnormalize(a) | |
{ | |
var len = vlength(a); | |
var v = new vec(a.x, a.y, a.z); | |
if (Math.abs(len) > 1.0e-17) { | |
v.x /= len; | |
v.y /= len; | |
v.z /= len; | |
} | |
return v; | |
} | |
function Sphere(center, radius) | |
{ | |
this.center = center; | |
this.radius = radius; | |
this.intersect = function (ray, isect) { | |
// rs = ray.org - sphere.center | |
var rs = vsub(ray.org, this.center); | |
var B = vdot(rs, ray.dir); | |
var C = vdot(rs, rs) - (this.radius * this.radius); | |
var D = B * B - C; | |
if (D > 0.0) { | |
var t = -B - Math.sqrt(D); | |
if ( (t > 0.0) && (t < isect.t) ) { | |
isect.t = t; | |
isect.hit = true; | |
isect.p = new vec(ray.org.x + ray.dir.x * t, | |
ray.org.y + ray.dir.y * t, | |
ray.org.z + ray.dir.z * t); | |
// calculate normal. | |
var n = vsub(isect.p, this.center); | |
isect.n = vnormalize(n); | |
} | |
} | |
} | |
} | |
function Plane(p, n) | |
{ | |
this.p = p; | |
this.n = n; | |
this.intersect = function (ray, isect) { | |
var d = -vdot(this.p, this.n); | |
var v = vdot(ray.dir, this.n); | |
if (Math.abs(v) < 1.0e-17) return; // no hit | |
var t = -(vdot(ray.org, n) + d) / v; | |
if ( (t > 0.0) && (t < isect.t) ) { | |
isect.hit = true; | |
isect.t = t; | |
isect.n = this.n; | |
isect.p = new vec( ray.org.x + t * ray.dir.x, | |
ray.org.y + t * ray.dir.y, | |
ray.org.z + t * ray.dir.z ); | |
} | |
} | |
} | |
function Ray(org, dir) | |
{ | |
this.org = org; | |
this.dir = dir; | |
} | |
function Isect() | |
{ | |
this.t = 1000000.0; // far away | |
this.hit = false; | |
this.p = new vec(0.0, 0.0, 0.0) | |
this.n = new vec(0.0, 0.0, 0.0) | |
} | |
function clamp(f) | |
{ | |
i = f * 255.5; | |
if (i > 255.0) i = 255.0; | |
if (i < 0.0) i = 0.0; | |
return Math.round(i) | |
} | |
function orthoBasis(basis, n) | |
{ | |
basis[2] = new vec(n.x, n.y, n.z) | |
basis[1] = new vec(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] = vcross(basis[1], basis[2]); | |
basis[0] = vnormalize(basis[0]); | |
basis[1] = vcross(basis[2], basis[0]); | |
basis[1] = vnormalize(basis[1]); | |
} | |
var spheres; | |
var plane; | |
function init_scene() | |
{ | |
spheres = new Array(3); | |
spheres[0] = new Sphere(new vec(-2.0, 0.0, -3.5), 0.5); | |
spheres[1] = new Sphere(new vec(-0.5, 0.0, -3.0), 0.5); | |
spheres[2] = new Sphere(new vec(1.0, 0.0, -2.2), 0.5); | |
plane = new Plane(new vec(0.0, -0.5, 0.0), new vec(0.0, 1.0, 0.0)); | |
} | |
function ambient_occlusion(isect) | |
{ | |
var basis = new Array(3); | |
orthoBasis(basis, isect.n); | |
var ntheta = NAO_SAMPLES; | |
var nphi = NAO_SAMPLES; | |
var eps = 0.0001; | |
var occlusion = 0.0; | |
var p = new vec(isect.p.x + eps * isect.n.x, | |
isect.p.y + eps * isect.n.y, | |
isect.p.z + eps * isect.n.z); | |
for (j = 0; j < nphi; j++) { | |
for (i = 0; i < ntheta; i++) { | |
var r = Math.random(); | |
var phi = 2.0 * Math.PI * Math.random(); | |
var x = Math.cos(phi) * Math.sqrt(1.0 - r); | |
var y = Math.sin(phi) * Math.sqrt(1.0 - r); | |
var z = Math.sqrt(r); | |
// local -> global | |
var rx = x * basis[0].x + y * basis[1].x + z * basis[2].x; | |
var ry = x * basis[0].y + y * basis[1].y + z * basis[2].y; | |
var rz = x * basis[0].z + y * basis[1].z + z * basis[2].z; | |
var raydir = new vec(rx, ry, rz); | |
var ray = new Ray(p, raydir); | |
var occIsect = new Isect(); | |
spheres[0].intersect(ray, occIsect); | |
spheres[1].intersect(ray, occIsect); | |
spheres[2].intersect(ray, occIsect); | |
plane.intersect(ray, occIsect); | |
if (occIsect.hit) occlusion += 1.0; | |
} | |
} | |
// [0.0, 1.0] | |
occlusion = (ntheta * nphi - occlusion) / (ntheta * nphi); | |
return new vec(occlusion, occlusion, occlusion); | |
} | |
function render(ctx, w, h, nsubsamples) | |
{ | |
cnt = 0; | |
for (y = 0; y < h; y++) { | |
for (x = 0; x < w; x++) { | |
rad = new vec(0.0, 0.0, 0.0); | |
// subsampling | |
for (v = 0; v < nsubsamples; v++) { | |
for (u = 0; u < nsubsamples; u++) { | |
cnt++; | |
px = (x + (u / nsubsamples) - (w / 2.0))/(w / 2.0); | |
py = -(y + (v / nsubsamples) - (h / 2.0))/(h / 2.0); | |
eye = vnormalize(new vec(px, py, -1.0)); | |
ray = new Ray(new vec(0.0, 0.0, 0.0), eye); | |
isect = new Isect(); | |
spheres[0].intersect(ray, isect); | |
spheres[1].intersect(ray, isect); | |
spheres[2].intersect(ray, isect); | |
plane.intersect(ray, isect); | |
if (isect.hit) { | |
col = ambient_occlusion(isect); | |
rad.x += col.x; | |
rad.y += col.y; | |
rad.z += col.z; | |
} | |
} | |
} | |
r = rad.x / (nsubsamples * nsubsamples); | |
g = rad.y / (nsubsamples * nsubsamples); | |
b = rad.z / (nsubsamples * nsubsamples); | |
// use fill rect | |
ctx.fillStyle = "rgb(" + clamp(r) + "," + clamp(g) + "," + clamp(b) + ")"; | |
ctx.fillRect (x, y, 1, 1); | |
} | |
} | |
} | |
function page_onload() | |
{ | |
var canvas = document.getElementById("box"); | |
var ctx = canvas.getContext("2d"); | |
var elapsed = 0; | |
var start = new Date(); | |
init_scene(); | |
render(ctx, IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES) | |
elapsed = new Date() - start; | |
document.elapform.elapsec.value = elapsed / 1000.0 | |
//img = ctx.getImageData(10, 10, 50, 50) | |
//document.write(img.data[41]); | |
//ret = ctx.putImagedata(img, 10, 10); | |
//print(ret); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment