var cvs = document.getElementById("viewport");
var ctx = cvs.getContext("2d");
var h = cvs.height;
var w = cvs.width;
var data = ctx.getImageData(0,0, w, h);
var scene ={};
//object for all the stuff and parameters in the scene
scene.skybox = {
color: {
x:150,
y:200,
z:250,
}
}
scene.camera = {
point: {
x: 0,
y: 0,
z: 10,
},
fieldOfView: 45,
vector: {
x: 0,
y: 0,
z: 0,
},
};
scene.lights = [
{
point:{
x: 0,
y: -5,
z: 0,
},
j:50,
},
// {
// x:5,
// y:-5,
// z:-5,
// }
];
scene.objects = [
{
type: "sphere",
point: {
x: 0,
y: 0,
z: 0,
},
color: {
x: 0,
y: 0,
z: 0,
},
radius: 1,
ambient:0,
lambert:0.5,
specular:0,
diffuse:0,
},
{
type: "plane",
point: {
x: 0,
y: 1,
z: 0,
},
vector:{
x:0,
y:-1,
z:0,
},
color: {
x: 255,
y: 255,
z: 255,
},
ambient:0,
lambert:0.5,
specular:0,
diffuse:0,
},
// {
// type: "triangle",
// a:{x:3,y:1,z:-3},
// b:{x:-2,y:0,z:-2},
// c:{x:2,y:-2,z:-2},
// color:{
// x:0,
// y:100,
// z:100
// },
// ambient:0.1,
// lambert:1,
// specular:0,
// },
];
window.onload=init();
function init(){
var left_arrow = 37;
var right_arrow = 39;
var up_arrow = 38;
var down_arrow = 40;
var w_key = 87;
var a_key = 65;
var s_key = 83;
var d_key = 68;
render();
window.onkeydown= function(gfg){
if(gfg.keyCode === a_key){
scene.camera.point.x-=1;
render();
};
if(gfg.keyCode === d_key)
{
scene.camera.point.x+=1;
render();
};
if(gfg.keyCode === w_key){
scene.camera.point.y-=1;
render();
};
if(gfg.keyCode === s_key)
{
scene.camera.point.y+=1;
render();
};
if(gfg.keyCode === left_arrow){
scene.lights[0].point.z+=1;
render();
};
if(gfg.keyCode === right_arrow)
{
scene.lights[0].point.z-=1;
render();
};
if(gfg.keyCode === up_arrow){
scene.lights[0].point.x-=1;
render();
};
if(gfg.keyCode === down_arrow)
{
scene.lights[0].point.x+=1;
render();
};
};
}
function render(){
var start = performance.now();
var camera = scene.camera;
var eVec = unitVector(subtract(camera.vector, camera.point));
var eVecRight = unitVector(crossProduct(eVec, {x:0, y:1, z:0}));
var eVecUp = unitVector(crossProduct(eVecRight, eVec));
var halfFOV = (Math.PI * (camera.fieldOfView / 2)) / 180;
var halfW = Math.tan(halfFOV);
var halfH = (h/w)*halfW;
var W = halfW*2;
var H = halfH*2;
var Wpx = W/(w-1);
var Hpx = H/(h-1);
var index;
var spx = 50;
var ray = {
point: camera.point,
};
for (var i=0; i<scene.objects.length; i++){
var object = scene.objects[i];
if (object.type == "plane"){
object.vector = unitVector(object.vector);
}else if (object.type == "triangle"){
object.vector = unitVector(crossProduct(subtract(object.b, object.a),subtract(object.c,object.a)));
}
}
for (var x =0; x<w; x++){
for(var y =0; y<h; y++){
var color = {x:0,y:0,z:0};
for (var i=0; i<spx; i++){
var xcomp = scaleVector(eVecRight, (x+Math.random()) * Wpx - halfW);
var ycomp = scaleVector(eVecUp, (y+Math.random()) * Hpx - halfH);
ray.vector = unitVector(add3Vectors(eVec, xcomp, ycomp));
color = addVectors(gamma(scaleVector(trace(ray, scene, 2), 1/spx)),color);
}
// console.log(color);
index = (y*w+x)*4;
data.data[index]=clamp(color.x, 0, 255);
data.data[index+1]=clamp(color.y, 0, 255);
data.data[index+2]=clamp(color.z, 0, 255);
data.data[index+3]=255;
}
}
ctx.putImageData(data, 0,0);
var end = performance.now();
console.log(end-start);
}
function trace(ray, scene, iter){
// if(iter>3) return;
var distObj = SceneIntersect(ray, scene);
// if (distObj[0] === Infinity){
// return scene.skybox.color;
// }else{
// pointOfIntersect = addVectors(scene.camera.point, scaleVector(ray.vector, distObj[0]));
// return surface(ray, scene, distObj[1], pointOfIntersect, iter);
// }
if (distObj[0] === Infinity){
var pointOfIntersect = null;
}else{
var pointOfIntersect = addVectors(scene.camera.point, scaleVector(ray.vector, distObj[0]));
}
// console.log(distObj);
return surface(ray, scene, distObj[1], pointOfIntersect, iter);
// return {x:distObj[0]*10, y:0, z:0};
}
function SceneIntersect(ray, scene){
var closest = [Infinity, null];
for (var i=0; i<scene.objects.length; i++){
var object = scene.objects[i];
if (object.type == "sphere"){
var dist = raySphereIntersect(object, ray);
}else if(object.type == "plane"){
var dist = rayPlaneIntersect(object, ray);
}else if(object.type == "triangle"){
var dist = rayTriangleIntersect(object,ray);
}
if (dist !== undefined && dist < closest[0]){
closest = [dist, object];
}
}
return closest;
}
function isLightVisible(pt, scene, light) {
var distObject = SceneIntersect(
{
point: pt,
vector: unitVector(subtract(pt, light)),
},
scene
);
return distObject[0] > -0.005;
}
function gamma(a){
return {
x:Math.sqrt(a.x),
y:Math.sqrt(a.y),
z:Math.sqrt(a.z),
};
}
function dotProduct(a,b){
return a.x*b.x+a.y*b.y+a.z*b.z;
}
function unitVector(a){
m = Math.sqrt(dotProduct(a,a));
return {
x: a.x/m,
y: a.y/m,
z: a.z/m
};
}
function scaleVector(a, m){
return {
x:a.x*m,
y:a.y*m,
z:a.z*m
}
}
function subtract(a,b){
return {
x: a.x- b.x,
y: a.y- b.y,
z: a.z - b.z
};
}
function crossProduct(a,b){
return {
x: a.y*b.z-a.z*b.y,
y: a.z*b.x-a.x*b.z,
z: a.x*b.y-a.y*b.x
};
}
function add3Vectors(a,b,c){
return {
x:a.x+b.x+c.x,
y:a.y+b.y+c.y,
z:a.z+b.z+c.z
}
}
function addVectors(a,b){
return{
x:a.x+b.x,
y:a.y+b.y,
z:a.z+b.z
}
}
function reflect(a,n){
return subtract(scaleVector(n, 2*dotProduct(n,a)),a);
}
function clamp(x, min, max){
if(x<min) return min;
if (x>max) return max;
return x;
}
// function raySphereIntersect(sphere, ray){
// var ec = subtract(ray.point, sphere.point);
// // var v = dotProduct(ray.vector, ec);
// // var ecDot = dotProduct(ec, ec);
// // var s2 = sphere.radius * sphere.radius - ecDot + v*v;
// var a = dotProduct(ray.vector, ray.vector);
// var b = dotProduct(ec, ray.vector);
// var c = dotProduct(ec, ec) - sphere.radius*sphere.radius;
// var d = b*b -a*c;
// if (d <0){
// return;
// }else{
// // var dist = v - Math.sqrt(s2);
// var dist = (-b-Math.sqrt(d))/a;
// // return v - Math.sqrt(s2);
// // return (-b-Math.sqrt(d))/a;
// // console.log(dist);
// return dist;
// }
// }
// function raySphereIntersect(sphere, ray){
// var oc = subtract(ray.point, sphere.point);
// var p = dotProduct(ray.vector, oc);
// var q = dotProduct(oc, oc) - sphere.radius*sphere.radius;
// var d = (p*p)-q;
// if (d<0){
// return;
// }else{
// var droot = Math.sqrt(d);
// d1 = -p-droot;
// // d2 = -p+droot;
// return d1;
// }
// }
function raySphereIntersect(sphere, ray){
var ec = subtract(sphere.point, ray.point);
var v = dotProduct(ray.vector, ec);
var ecDot = dotProduct(ec, ec);
var s2 = sphere.radius * sphere.radius - ecDot + v*v;
// console.log(s2);
if (s2 <0){
return;
}else{
return v - Math.sqrt(s2);
}
}
function rayPlaneIntersect(plane, ray){
// var npNormal = unitVector(scaleVector(subtract(plane.vector, plane.point),-1));
// var ep = subtract(plane.point, ray.point);
// var a = dotProduct(npNormal,ep);
// var u = scaleVector(npNormal, a);
// var v = dotProduct(ray.vector, u);
// var d = (a*a)/v;
var n = unitVector(plane.vector);
var d = dotProduct(n,subtract(plane.point,ray.point))/dotProduct(ray.vector,n);
if (d<0){
return;
}else{
return d;
}
}
function rayTriangleIntersect(triangle, ray){
var a = triangle.a,
b = triangle.b,
c = triangle.c;
var n = triangle.vector;
var d = dotProduct(n,subtract(a,ray.point))/dotProduct(ray.vector,n);
var p = addVectors(ray.point,scaleVector(ray.vector,d));
if (dotProduct(n,crossProduct(subtract(b,a),subtract(p,a)))>=0 &&
dotProduct(n,crossProduct(subtract(c,b),subtract(p,b)))>=0 &&
dotProduct(n,crossProduct(subtract(a,c),subtract(p,c)))>=0){
return d;
}else{
return;
}
}
function surface(ray, scene, object, p, iter){
var tLambert=0;
var tSpecular = {x:0,y:0,z:0};
var tDiffuse = {x:0,y:0,z:0};
// var c = object.color;
// if(object.lambert>0){
// for (var i = 0; i<scene.lights.length; i++){
// var lightPoint = scene.lights[i].point;
// if (!isLightVisible(p, scene, lightPoint))continue;
// var r2 = dotProduct(subtract(lightPoint,p), subtract(lightPoint, p));
// var toLight = unitVector(subtract(lightPoint,p));
// // var intensity = dotProduct(toLight,unitVector(addVectors(normal,scaleVector(subtract(randSphere(p, normal), p),0.5))));
// var intensity = (scene.lights[i].j/r2)*dotProduct(toLight, normal);
// if (intensity>0) tLambert+=intensity;
// }
// }
// if(object.diffuse >0){
// var target = randSphere(p, normal);
// var rColor = trace({point:p, vector: unitVector(subtract(target, p))}, scene, ++iter);
// console.log(rColor);
// if(rColor){
// tDiffuse = addVectors(tDiffuse, scaleVector(rColor, object.diffuse));
// }else{
// tDiffuse = addVectors(scaleVector(scene.skybox.color,0.5),tDiffuse);
// }
// // console.log(tDiffuse);
// }
// if(object.specular>0){
// var rRay = {point: p, vector: reflect(ray.vector, normal)}
// // var rRay = {point:p, vector: unitVector(subtract(randSphere(p, normal), p))};
// var rColor = trace(rRay, scene, ++iter);
// if(rColor){
// tSpecular = addVectors(tSpecular, scaleVector(rColor,object.specular));
// }
// }
// return add3Vectors(
// scaleVector(c, tLambert*object.lambert),
// tSpecular,
// scaleVector(c, object.ambient)
// );
if (iter <= -1) {
// console.log("yo")
// return {x:255,y:0,z:0};
return scene.skybox.color;
}
if (p == null){
return scene.skybox.color;
}else{
if (object.type == "sphere"){
// console.log(object);
var normal = sphereNormal(object, p, ray);
var target = add3Vectors(p, normal, randSphere());
// return scaleVector(scaleVector(addVectors(normal, {x:1,y:1,z:1}),0.5),255);
// console.log(Math.sqrt(dotProduct(subtract(p,object.point),subtract(p,object.point))));
// return {x:subtract(p,object.point), y:0, z:0};
return scaleVector(trace({point:p, vector: unitVector(subtract(target, p))}, scene, --iter), 0.5);
// console.log(normal);
}else{
var normal = object.vector;
var target = add3Vectors(p, normal, randSphere());
// return {x:255*normal.x,y:255*normal.y, z:255*normal.z};
return scaleVector(trace({point:subtract(p,{x:0.005, y:0.005, z:0.005}), vector: unitVector(subtract(target, p))}, scene, --iter), 0.5);
}
}
}
function sphereNormal(sphere, p, ray){
var normal = scaleVector(subtract(p, sphere.point), 1/sphere.radius);
var front = dotProduct(ray.vector, normal) < 0;
// console.log(front);
normal = front ? normal : scaleVector(normal, -1);
return normal;
}
function randSphere(){
var a = Math.random()*2*Math.PI;
var b = Math.random()*2*Math.PI;
var x = Math.sin(a)*Math.cos(b);
var y = Math.sin(a)*Math.sin(b);
var z = Math.cos(a);
return {x:x,y:y,z:z};
}
// function test2() {
// var start = performance.now();
// for (var i=0; i<100000; i++){
// do {
// var d, x, y, z;
// x = Math.random() * 2.0 - 1.0;
// y = Math.random() * 2.0 - 1.0;
// z = Math.random() * 2.0 - 1.0;
// d = x*x + y*y + z*z;
// } while(d > 1.0);
// }
// var end = performance.now();
// return end-start;
// }
<!DOCTYPE html>
<html>
<head>
<title>raytracing</title>
<canvas id='viewport' width = '256' height = '256'></canvas>
<script type="text/javascript" src="main.js"></script>
</head>
<body>
</body>
</html>