Good Morning.
I'm doing a small ray tracing example with JS but I've a problem. When I'm trying to calculate the reflected color it always returns [255,255,255] and I don't know why. If object is specular I trace a ray and it return 255,255,255. (Line 114 - 124 js)
In addition the spheres makes the shadows of the lighy ok, but no the shadows of for example (sphere 1 to sphere 2). I think the problem is in the function isLightVisible(line 89-98 js)
Finally I think that they don't respect wich is in front of wich one.
HTML CODE:
<html>
<head>
<title>Ray Tracing Example</title>
</head>
<body>
<h1>Ray Tracing</h1>
<table>
<th>
<canvas id="glcanvas" style="border: solid;" width="400" height="400"></canvas>
<button onclick="start()"> Start </button>
</th>
</table>
<p style="text-indent:85px;">Camera Position: [0, 0, 0]</p>
<script type="text/javascript" src="raytracingV2.js"></script>
<script src="gl-matrix/common.js"></script>
<script type="text/javascript" src="gl-matrix/vec3.js"></script>
<script type="text/javascript" src="gl-matrix/vec2.js"></script>
<script type="text/javascript" src="gl-matrix/vec4.js"></script>
<script type="text/javascript" src="gl-matrix/vec4.js"></script>
<script type="text/javascript" src="gl-matrix/mat4.js"></script>
<script type="text/javascript" src="gl-matrix/mat3.js"></script>
<script type="text/javascript" src="gl-matrix/quat.js"></script>
<script type="text/javascript">
function start () {
// Inicialitzem el RayTracing
inicialitzar(Scene);
}
var Screen = {
width : 0,
height : 0,
canvas : null,
context : null,
buffer : null,
};
var Scene = {
Fons: [0, 0, 0],
Shapes: [
{
id : "esfera_blava",
tipus : "esfera",
radi : 0.25,
centre : [-0.3,0,1],
point : [-0.3,0,1],
color: [155, 200, 155],
specular: 0.2,
lambert: 0.7,
ambient: 0.1,
// Material:
// Rd, Rs, Ra (colors)
// alpha specular (float)
// Reflexion and/or Transmision (float)
},
{
id : "esfera_vermella",
tipus : "esfera",
radi : 0.25,
centre : [0.3,0,1.4],
point : [0.3,0,1.4],
color: [200, 155, 155],
specular: 0.2,
lambert: 0.7,
ambient: 0.1,
// Material:
// Rd, Rs, Ra (colors)
// alpha specular (float)
// Reflexion and/or Transmision (float)
},
],
Camera: {
position: [0,0,0], // posicio camera
up : [0,1,0], // vector amunt
centre : [0,0,-1], // centre escena
fov : 60, // field of view
X : vec3.create(),
Z : vec3.create(),
Y : vec3.create(),
},
Lights: [
{
position: vec3.create(), // S'emplena segons els valors entrats
color : vec3.create(), // S'emplena segons els valors entrats
},
{
position: vec3.create(), // S'emplena segons els valors entrats
color : vec3.create(), // S'emplena segons els valors entrats
},
{
position: vec3.create(), // S'emplena segons els valors entrats
color : vec3.create(), // S'emplena segons els valors entrats
},
]
};
</script>
</body>
</html>
RAY TRACING JS CODE
// RAY TRACING - example //
// Inicialitzem el RayTracing
function inicialitzar(Scene) {
// Parametres de les llums
// Put it interactively
Scene.Lights[0].position = [-1, 2, 4];
Scene.Lights[0].color = [0.4, 0.4, 0.4];
Scene.Lights[1].position = [1, 4, 4];
Scene.Lights[1].color = [1, 0, 0];
Scene.Lights[1].position = [-1, -4, -4];
Scene.Lights[1].color = [1, 0, 0];
Screen.canvas = document.getElementById("glcanvas");
if (Screen.canvas == null) {
alert("Invalid element: " + id);
return;
}
Screen.context = Screen.canvas.getContext("2d");
if(Screen.context == null){
alert("Could not get context");
return;
}
Screen.width = Screen.canvas.width;
Screen.height = Screen.canvas.height;
Screen.buffer = Screen.context.createImageData(Screen.width,Screen.height);
// Calculem els eixos de la camera
calcularEixos(Scene);
// Calculem els increments i P0 (GLOBALS)
incX = calcularIncrementX(Scene.Camera,Screen);
incY = calcularIncrementY(Scene.Camera,Screen);
P0 = calcularP0(incX,incY,Scene.Camera,Screen);
// Executem RayTracing
rayTracing(Scene, Screen);
Screen.context.putImageData(Screen.buffer, 0, 0);
};
function sphereIntersection(sphere, ray, scene){
var eye_to_center = vec3.subtract(sphere.point, scene.Camera.position);
var v = vec3.dot(eye_to_center, vec3.clone(ray));
var eoDot = vec3.dot(eye_to_center,eye_to_center);
var discriminant = sphere.radi * sphere.radi -eoDot + v * v;
if (discriminant<0){
return;
}else{
return v - Math.sqrt(discriminant);
}
}
// Calcular increment de X
function calcularIncrementX(Cam,Scr) {
var rati = (Scr.height/Scr.width);
var theta = (Cam.fov * Math.PI / 180);
var w = 2*Math.tan(theta/2); // Calculem w' = 2*tg(theta/2)
var h = w*rati; // Calculem h' = w'*rati
var aux = w/Scr.width; // w'/W
var incX = vec3.scale(Cam.X,aux); // Calculem increment de X (X * 2*tg(theta/2)/W)
return incX;
}
// Calcular increment de Y
function calcularIncrementY(Cam,Scr) {
var rati = (Scr.height/Scr.width);
var theta = (Cam.fov * Math.PI / 180);
var w = 2*Math.tan(theta/2); // Calculem w' = 2*tg(theta/2)
var h = w*rati; // Calculem h' = w'*rati
var aux = rati*w/Scr.height; // rati*w'/H
var incY = vec3.scale(Cam.Y,aux); // Calculem increment de Y (Y * 2*tg(theta/2)/W)
return incY;
}
function isLightVisible(pt, scene, light){
var distObject = intersectarScene(
{
point:pt,
vector: vec3.unitVector(vec3.sub(pt, light)),
},
scene
);
return distObject[0] > -0.005;
}
function surface(ray, scene, object, pointAtTime, normal, depth){
var b = object.color;
var c = [0,0,0];
var lambertAmount = 0;
if (object.lambert){
for (var i = 0; i<scene.Lights.length; i++){
var lightPoint = scene.Lights[i].position;
if (!isLightVisible(pointAtTime, scene, lightPoint)) continue;
var contribution = vec3.dot(vec3.unitVector(vec3.subtract(lightPoint, pointAtTime)), normal);
if (contribution > 0) lambertAmount += contribution;
}
}
if (object.specular){
var reflectedRay = {
point: pointAtTime,
vector: vec3.reflectThrough(vec3.clone(ray), normal),
};
var reflectedColor = trace(reflectedRay, scene, ++depth);
if(reflectedColor){
c = vec3.add(c, vec3.scale(reflectedColor, object.specular));
}
}
lambertAmount = Math.min(1,lambertAmount);
var a = vec3.scale(b, lambertAmount * object.lambert);
var d = vec3.scale(b, object.ambient);
var color = vec3.add3(a, d, c);
return color;
}
// Calcular P0
function calcularP0(incX,incY,Cam,Scr) {
var P = vec3.subtract(Cam.position,Cam.Z); // Calculem P (O - Z)
var aux = vec3.scale(incX,((Scr.width-1)/2)); // Increment de X * (W-1)/2
var aux2 = vec3.scale(incY,((Scr.height-1)/2)); // Increment de Y * (H-1)/2
var aux3 = vec3.subtract(P,aux); // P - Increment de X * (W-1)/2
var P0 = vec3.add(aux3,aux2); // Calculem P0 (P - Increment de X * (W-1)/2 + Increment de Y * (H-1)/2)
return P0;
}
// Calcular els eixos de la camera
function calcularEixos(Scene) {
Scene.Camera.Z = vec3.normalize(vec3.subtract(Scene.Camera.position, Scene.Camera.centre)); // |O - C|
Scene.Camera.X = vec3.normalize(vec3.cross(Scene.Camera.up, Scene.Camera.Z)); // |up x Z|
Scene.Camera.Y = vec3.cross(Scene.Camera.Z, Scene.Camera.X); // Z x X
}
function plot(x,y,color){
var index = (x+y*Screen.buffer.width)*4;
Screen.buffer.data[index+0] = color[0];
Screen.buffer.data[index+1] = color[1];
Screen.buffer.data[index+2] = color[2];
Screen.buffer.data[index+3] = 255;
return index;
}
function sphereNormal(sphere, pos) {
return vec3.unitVector(vec3.subtract(pos, sphere.point));
}
function intersectarScene(ray, scene) {
var closest = [Infinity, null];
for (var i = 0; i< scene.Shapes.length; i++){
var object = scene.Shapes[i];
var dist = sphereIntersection(object, ray,scene);
if (dist !== undefined && dist < closest[0]) {
closest = [dist, object];
}
}
return closest;
}
function trace(ray, scene, depth){
if (depth > 3) return;
var distObject = intersectarScene(ray,scene);
if (distObject[0] === Infinity) {
return [255,255,255];
}
var dist = distObject[0];
var object = distObject[1];
var pointAtTime = vec3.add(scene.Camera.position, vec3.scale(vec3.clone(ray), dist));
return surface(ray,scene,object,pointAtTime, sphereNormal(object, pointAtTime), depth);
}
// Pintar cada pixel
function rayTracing(Scene, Screen) {
console.log(incX);
for(var x = 0; x < Screen.width; x++){
for (y = 0; y < Screen.height; y++){
var rDirection = computeRay(incX,incY,P0,Scene.Camera,x,y);
var color = trace(rDirection, Scene, 0);
plot(x,y,color);
}
}
}
// Computar el raig
function computeRay(incX,incY,P0,Cam,x,y){
// Calculem la direccio per a cada pixel
var aux = vec3.scale(incX,x); // Increment de X * x
var aux2 = vec3.scale(incY,y); // Increment de Y * y