Skip to content

Commit

Permalink
add Ch1 Ep4 Diffuse Lighting and demo!
Browse files Browse the repository at this point in the history
erichlof committed Feb 12, 2024
1 parent cdf24f9 commit a22b5bb
Showing 11 changed files with 367 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions Chapter_1/Episode_4/css/rayTracingStyles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
html, body
{
background-color: gray;
margin: 0%;
overflow: hidden;
}

#myCanvas
{
position: fixed;
background-color: black;
width: 100%;
height: 100%;
}

#userInfo
{
position: fixed;
color: white;
bottom: 2%;
left: 1%;
font-family: Arial, Helvetica, sans-serif;
font-size: x-large;
}
15 changes: 15 additions & 0 deletions Chapter_1/Episode_4/diffuseLighting.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title> Diffuse Lighting </title>
<link href="css/rayTracingStyles.css" rel="stylesheet">
</head>

<body>
<canvas id="myCanvas"> </canvas>
<p id="userInfo"> user text info </p>

<script src="js/mathUtils.js"> </script>
<script src="js/diffuseLighting.js"> </script>
</body>
</html>
146 changes: 146 additions & 0 deletions Chapter_1/Episode_4/js/diffuseLighting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
let canvas = document.getElementById("myCanvas");
let context = canvas.getContext("2d");

let aspectRatio = 0;

let userInfo = document.getElementById("userInfo");


window.addEventListener("resize", handleWindowResize);
function handleWindowResize()
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

aspectRatio = canvas.width / canvas.height;

userInfo.innerHTML = "canvasW: " + canvas.width + " canvasH: " + canvas.height + "<br>" +
"total pixel count: " + (canvas.width * canvas.height);

// redraw the screen
draw();
}

let canvasX = 0;
let canvasY = 0;

let rayOrigin = new Vector3(0, 0, 0);
let rayDirection = new Vector3();
let pixelColor = new Vector3(); // x = r, y = g, z = b

let skyColor = new Vector3(0.2, 0.6, 1.0);
let groundColor = new Vector3(0.4, 0.4, 0.4);
let fogColor = new Vector3(0.7, 0.7, 0.7);
let gradientSkyColor = new Vector3();

let planeOrigin = new Vector3(0, -3, 0);
let planeNormal = new Vector3(0, 1, 0);
planeNormal.normalize();

let sphereRadius = 3;
let spherePosition = new Vector3(0, planeOrigin.y + sphereRadius, -15);
let sphereColor = new Vector3(0.8, 0.01, 0.01);

let t = 0;
let nearestT = 0;
let intersectionColor = new Vector3();
let intersectionPoint = new Vector3();
let intersectionNormal = new Vector3();


let directionToLight = new Vector3(1, 1, 1);
directionToLight.normalize();
let sunLightColor = new Vector3(1, 1, 1);

let ambientColor = new Vector3();
let ambientIntensity = 0.4;
let diffuseColor = new Vector3();
let diffuseIntensity = 0;

function draw()
{
// loop over all pixels on screen
for (let row = 0; row < canvas.height; row++)
{
for (let column = 0; column < canvas.width; column++)
{
canvasX = column / (canvas.width - 1);
canvasY = row / (canvas.height - 1);
// flip Y coordinates so 0 is bottom of screen and 1 is top of screen
canvasY = 1 - canvasY;
// canvasX is now in the range of 0.0 to 1.0 (left to right)
// canvasY is now in the range of 0.0 to 1.0 (bottom to top)

canvasX *= 2; // canvasX now goes from 0.0 to 2.0 (left to right)
canvasY *= 2; // canvasY now goes from 0.0 to 2.0 (bottom to top)
canvasX -= 1; // canvasX now goes from -1.0 to +1.0 (left to right)
canvasY -= 1; // canvasY now goes from -1.0 to +1.0 (bottom to top)

canvasX *= aspectRatio;

rayDirection.set(canvasX, canvasY, -1.5);
rayDirection.normalize();

gradientSkyColor.mix(fogColor, skyColor, rayDirection.y * 1.5);

// Important: must reset nearestT for each pixel!
nearestT = Infinity;
// Important: must also reset pixelColor to black (no color) for each pixel!
pixelColor.set(0, 0, 0);

t = intersectSphere(spherePosition, sphereRadius, rayOrigin, rayDirection);
if (t < nearestT)
{
nearestT = t;
intersectionPoint.getPointAlongRay(rayOrigin, rayDirection, t);
intersectionNormal.copy(intersectionPoint);
intersectionNormal.subtract(spherePosition);
intersectionColor.copy(sphereColor);
}

t = intersectPlane(planeOrigin, planeNormal, rayOrigin, rayDirection);
if (t < nearestT)
{
nearestT = t;
intersectionNormal.copy(planeNormal);
intersectionColor.copy(groundColor);
}

// if the ray hit anything at all, apply lighting to the intersection (using the recorded intersection data)
if (nearestT < Infinity)
{
intersectionNormal.normalize();

ambientColor.copy(intersectionColor);
ambientColor.multiplyScalar(ambientIntensity);

diffuseColor.copy(intersectionColor);
diffuseColor.multiplyColor(sunLightColor);
diffuseIntensity = intersectionNormal.dot(directionToLight);
diffuseIntensity = Math.max(0, diffuseIntensity);
diffuseColor.multiplyScalar(diffuseIntensity);

pixelColor.add(ambientColor);
pixelColor.add(diffuseColor);

pixelColor.mix(pixelColor, gradientSkyColor, nearestT * 0.004);
}

// if the ray missed everything, set the pixel color to the background color
if (nearestT == Infinity)
{
pixelColor.copy(gradientSkyColor);
}

pixelColor.multiplyScalar(255);
pixelColor.floor();

//"rgb(r,g,b)"
context.fillStyle = "rgb(" + pixelColor.x + "," + pixelColor.y + "," + pixelColor.z + ")";
context.fillRect(column, row, 1, 1);
}
}
} // end function draw()

// jumpstart the drawing of the screen
handleWindowResize();
181 changes: 181 additions & 0 deletions Chapter_1/Episode_4/js/mathUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
function Vector3(x = 0, y = 0, z = 0)
{
this.x = x;
this.y = y;
this.z = z;
return this;
}

Vector3.prototype.set = function(x, y, z)
{
this.x = x;
this.y = y;
this.z = z;
return this;
};

Vector3.prototype.copy = function(otherVector)
{
this.x = otherVector.x;
this.y = otherVector.y;
this.z = otherVector.z;
return this;
};

Vector3.prototype.add = function(otherVector)
{
this.x += otherVector.x;
this.y += otherVector.y;
this.z += otherVector.z;
return this;
};

Vector3.prototype.subtract = function(otherVector)
{
this.x -= otherVector.x;
this.y -= otherVector.y;
this.z -= otherVector.z;
return this;
};

Vector3.prototype.multiplyScalar = function(scalarNumber)
{
this.x *= scalarNumber;
this.y *= scalarNumber;
this.z *= scalarNumber;
return this;
};

Vector3.prototype.multiplyColor = function(otherColorVector)
{
this.x *= otherColorVector.x;
this.y *= otherColorVector.y;
this.z *= otherColorVector.z;
return this;
};

Vector3.prototype.floor = function()
{
this.x = Math.floor(this.x);
this.y = Math.floor(this.y);
this.z = Math.floor(this.z);
return this;
};

Vector3.prototype.absolute = function()
{
this.x = Math.abs(this.x);
this.y = Math.abs(this.y);
this.z = Math.abs(this.z);
return this;
};

Vector3.prototype.vectorLength = function()
{
return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
};

let inverseLength = 0;
Vector3.prototype.normalize = function()
{
inverseLength = 1 / this.vectorLength();
this.x *= inverseLength;
this.y *= inverseLength;
this.z *= inverseLength;
return this;
};

Vector3.prototype.dot = function(otherVector)
{
return (this.x * otherVector.x) + (this.y * otherVector.y) + (this.z * otherVector.z);
};

Vector3.prototype.mix = function(vectorA, vectorB, t)
{
t = Math.min(t, 1);
t = Math.max(t, 0);
this.x = vectorA.x + (vectorB.x - vectorA.x) * t;
this.y = vectorA.y + (vectorB.y - vectorA.y) * t;
this.z = vectorA.z + (vectorB.z - vectorA.z) * t;
return this;
};

Vector3.prototype.getPointAlongRay = function(rayOrigin, rayDirection, t)
{
this.x = rayOrigin.x + (t * rayDirection.x);
this.y = rayOrigin.y + (t * rayDirection.y);
this.z = rayOrigin.z + (t * rayDirection.z);
return this;
};


let planeO_rayO_vec = new Vector3();
let rayD_dot_planeN = 0;
let result = 0;

function intersectPlane(planeOrigin, planeNormal, rayOrigin, rayDirection)
{
rayD_dot_planeN = rayDirection.dot(planeNormal);
if (rayD_dot_planeN >= 0)
{
return Infinity;
}

planeO_rayO_vec.copy(planeOrigin);
planeO_rayO_vec.subtract(rayOrigin);
result = planeO_rayO_vec.dot(planeNormal) / rayD_dot_planeN;
if (result > 0)
{
return result;
}

return Infinity;
}

let t0 = 0;
let t1 = 0;
let discriminant = 0;
let oneOver_2a = 0;

function solveQuadratic(a, b, c)
{
discriminant = (b * b) - (4 * a * c);
if (discriminant < 0)
{
return false;
}
discriminant = Math.sqrt(discriminant);
oneOver_2a = 1 / (2 * a);
t0 = (-b - discriminant) * oneOver_2a;
t1 = (-b + discriminant) * oneOver_2a;
return true;
}

let L = new Vector3();
// note: in the video for a, b, and c below, I put them all on one line, such as
// let a, b, c = 0; However, this does not initialize all three to 0 as I had thought.
let a = 0;
let b = 0;
let c = 0;

function intersectSphere(spherePosition, sphereRadius, rayOrigin, rayDirection)
{
L.copy(rayOrigin);
L.subtract(spherePosition);
a = rayDirection.dot(rayDirection);
b = 2 * L.dot(rayDirection);
c = L.dot(L) - (sphereRadius * sphereRadius);
if (solveQuadratic(a, b, c) == false)
{
return Infinity;
}
if (t0 > 0)
{
return t0;
}
else if (t1 > 0)
{
return t1;
}
return Infinity;
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -52,3 +52,4 @@ So...
* Episode 1: [Visualize Ray Directions as rgb colors](https://erichlof.github.io/Joy-of-Ray-Tracing/Chapter_1/Episode_1/rayDirections.html) - [video](https://youtu.be/gJ7SMXnVVvY)
* Episode 2: [Ray Intersect Plane, Mix (blend) function](https://erichlof.github.io/Joy-of-Ray-Tracing/Chapter_1/Episode_2/intersectPlane.html) - [video](https://youtu.be/NTieRi0JD5g)
* Episode 3: [Ray Intersect Sphere](https://erichlof.github.io/Joy-of-Ray-Tracing/Chapter_1/Episode_3/intersectSphere.html) - [video](https://youtu.be/BLOnAegyFZE)
* Episode 4: [Diffuse Lighting, Directional Light Source](https://erichlof.github.io/Joy-of-Ray-Tracing/Chapter_1/Episode_4/diffuseLighting.html) - [video](https://youtu.be/mqYFZDO0Zbk)

0 comments on commit a22b5bb

Please sign in to comment.