<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ray Tracing Sphere</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000; /* Fond noir pour un meilleur rendu */
}
canvas {
display: block;
}
#fpsCounter {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 16px;
}
#zoomCounter {
position: absolute;
top: 30px;
left: 10px;
color: white;
font-size: 16px;
}
</style>
</head>
<body onmousemove="moveLightSource(event)" onwheel="handleZoom(event)">
<canvas id="myCanvas"></canvas>
<div id="fpsCounter"></div>
<div id="zoomCounter"></div>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const fpsCounter = document.getElementById('fpsCounter');
const zoomCounter = document.getElementById('zoomCounter');
// Configuration
const width = canvas.width = window.innerWidth*0.5;
const height = canvas.height = window.innerHeight*0.5;
const sphereRadius = 1; // Sphere radius
// Camera settings
let cameraZ = 3; // Distance de la caméra
const minZoom = 1.5; // Distance minimale
const maxZoom = 10; // Distance maximale
// Light Source
let lightSource = [0, 0, 3]; // Initial light source position
// Function to create sphere intersection
function createSphere(radius, center) {
return {
radius: radius,
center: center
}
}
let sphere = createSphere(sphereRadius, [0,0,0]);
// Render Variables
let lastTime = 0;
let fps = 0;
// Function to calculate the distance from the ray origin to the sphere center
function distance(rayOrigin, sphereCenter) {
return Math.sqrt(Math.pow(rayOrigin[0] - sphereCenter[0], 2) +
Math.pow(rayOrigin[1] - sphereCenter[1], 2) +
Math.pow(rayOrigin[2] - sphereCenter[2], 2));
}
// Function to move the light source based on mouse position
function moveLightSource(event) {
const mouseX = event.clientX;
const mouseY = event.clientY;
// Convert mouse coordinates to 3D coordinates. Adjust factors as needed.
lightSource[0] = (mouseX / width) * 2 - 1;
lightSource[1] = 1 - (mouseY / height) * 2;
lightSource[2] = 3; // Fixed depth for the light source
render(); // Redraw the scene with the new light position
}
// Function to calculate the color of a point on the sphere
function calculateColor(point) {
const normal = normalize([
point[0] - sphere.center[0],
point[1] - sphere.center[1],
point[2] - sphere.center[2]
]);
const lightDirection = normalize([
lightSource[0] - point[0],
lightSource[1] - point[1],
lightSource[2] - point[2]
]);
let intensity = Math.max(dot(normal, lightDirection), 0); // removed .1
return [intensity, intensity, intensity]; // Basic grayscale color
}
// Function to calculate the dot product
function dot(v1, v2) {
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
// Function to normalize a vector
function normalize(vector) {
let length = Math.sqrt(dot(vector, vector));
if (length === 0) {
return [0, 0, 0];
}
return [vector[0] / length, vector[1] / length, vector[2] / length];
}
// Function to handle zoom with mouse wheel
function handleZoom(event) {
event.preventDefault();
// Adjust camera distance based on wheel direction
const zoomSpeed = 0.1;
if (event.deltaY < 0) {
// Zoom in
cameraZ = Math.max(minZoom, cameraZ - zoomSpeed);
} else {
// Zoom out
cameraZ = Math.min(maxZoom, cameraZ + zoomSpeed);
}
zoomCounter.textContent = `Zoom: ${cameraZ.toFixed(2)}`;
render(performance.now());
}
// Render function
function render(timestamp) {
ctx.clearRect(0, 0, width, height);
let now = timestamp;
let deltaTime = now - lastTime;
lastTime = now;
fps = Math.round(1000 / deltaTime);
fpsCounter.textContent = `FPS: ${fps}`;
zoomCounter.textContent = `Zoom: ${cameraZ.toFixed(2)}`;
// Raytracing loop
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// Ray origin and direction
let rayOrigin = [0, 0, -cameraZ]; // Camera position with zoom
let rayDirection = [
(x / width) * 2 - 1, // Normalize x to -1 to 1
1 - (y / height) * 2, // Normalize y to -1 to 1
1 // Point into the screen
];
rayDirection = normalize(rayDirection);
// Calculate intersection with sphere
let oc = [
rayOrigin[0] - sphere.center[0],
rayOrigin[1] - sphere.center[1],
rayOrigin[2] - sphere.center[2]
];
let a = dot(rayDirection, rayDirection);
let b = 2 * dot(oc, rayDirection);
let c = dot(oc, oc) - sphere.radius * sphere.radius;
let discriminant = b * b - 4 * a * c;
if (discriminant >= 0) {
let t1 = (-b - Math.sqrt(discriminant)) / (2 * a);
let t2 = (-b + Math.sqrt(discriminant)) / (2 * a);
//let t = Math.max(t1, t2); // Use the closer intersection
let t = t1; //choose the first value
let intersectionPoint = [
rayOrigin[0] + t * rayDirection[0],
rayOrigin[1] + t * rayDirection[1],
rayOrigin[2] + t * rayDirection[2]
];
let color = calculateColor(intersectionPoint);
ctx.fillStyle = `rgb(${color[0] * 255}, ${color[1] * 255}, ${color[2] * 255})`;
ctx.fillRect(x, y, 1, 1);
} else {
// No intersection - draw background color
ctx.fillStyle = 'rgb(0, 0, 0)'; // Black background
ctx.fillRect(x, y, 1, 1);
}
}
}
}
// Initial render
let timestamp;
function init() {
timestamp = performance.now();
render(timestamp);
}
init();
</script>
</body>
</html>