start .
me .
HTML Document File : raycalc.html
<!DOCTYPE html>
<html>
<head>
<title>3D Sphere Renderer</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<canvas id="calculated" > </canvas>
<script>
// Canvas setup
const canvas = document.querySelector('canvas#calculated');
canvas.width = 800;
canvas.height = 600;
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(canvas.width, canvas.height);
const data = imageData.data;
// 3D Math utilities
class Vec3 {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
normalize() {
const len = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
return new Vec3(this.x / len, this.y / len, this.z / len);
}
dot(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
add(v) {
return new Vec3(this.x + v.x, this.y + v.y, this.z + v.z);
}
sub(v) {
return new Vec3(this.x - v.x, this.y - v.y, this.z - v.z);
}
mul(s) {
return new Vec3(this.x * s, this.y * s, this.z * s);
}
reflect(normal) {
const dot = this.dot(normal);
return this.sub(normal.mul(2 * dot));
}
}
// Scene configuration
const sphere = {
center: new Vec3(0, 1, 0),
radius: 1,
color: new Vec3(0.8, 0.3, 0.3),
specular: 0.7,
reflectivity: 0.5,
transparency: 0.2
};
const light = {
position: new Vec3(5, 5, -5),
color: new Vec3(1, 1, 1),
intensity: 1.0
};
const plane = {
normal: new Vec3(0, 1, 0),
point: new Vec3(0, 0, 0),
textureScale: 0.5
};
const camera = {
position: new Vec3(0, 2, -5),
target: new Vec3(0, 0, 0),
up: new Vec3(0, 1, 0),
fov: 60,
aspectRatio: canvas.width / canvas.height
};
// Texture generation
function createCheckerboardTexture(size) {
const texture = new Array(size * size);
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
const isEven = ((x + y) % 2) === 0;
texture[y * size + x] = isEven ? new Vec3(0.9, 0.9, 0.9) : new Vec3(0.2, 0.2, 0.2);
}
}
return texture;
}
const planeTexture = createCheckerboardTexture(256);
// Intersection functions
function intersectSphere(origin, direction) {
const oc = origin.sub(sphere.center);
const a = direction.dot(direction);
const b = 2 * oc.dot(direction);
const c = oc.dot(oc) - sphere.radius * sphere.radius;
const discriminant = b * b - 4 * a * c;
if (discriminant < 0) return null;
const t = (-b - Math.sqrt(discriminant)) / (2 * a);
if (t < 0) return null;
const point = origin.add(direction.mul(t));
const normal = point.sub(sphere.center).normalize();
return { point, normal, t };
}
function intersectPlane(origin, direction) {
const denom = direction.dot(plane.normal);
if (Math.abs(denom) < 1e-6) return null;
const t = plane.point.sub(origin).dot(plane.normal) / denom;
if (t < 0) return null;
const point = origin.add(direction.mul(t));
return { point, normal: plane.normal, t };
}
// Shading and lighting
function calculateLighting(point, normal, viewDir) {
const lightDir = light.position.sub(point).normalize();
const shadowRay = { origin: point.add(normal.mul(0.001)), direction: lightDir };
// Check for shadows
const shadowHit = intersectSphere(shadowRay.origin, shadowRay.direction);
if (shadowHit && shadowHit.t < light.position.sub(point).dot(lightDir)) {
return new Vec3(0.1, 0.1, 0.1); // Ambient light only
}
const diffuse = Math.max(0, normal.dot(lightDir));
const reflectDir = lightDir.reflect(normal);
const specular = Math.pow(Math.max(0, reflectDir.dot(viewDir)), 32);
return new Vec3(
light.color.x * (diffuse + specular * sphere.specular),
light.color.y * (diffuse + specular * sphere.specular),
light.color.z * (diffuse + specular * sphere.specular)
);
}
// Sample plane texture
function samplePlaneTexture(point) {
const u = Math.floor(point.x * plane.textureScale) & 255;
const v = Math.floor(point.z * plane.textureScale) & 255;
return planeTexture[v * 256 + u];
}
// Main rendering function with antialiasing
function render() {
const samples = 4; // Anti-aliasing samples per pixel
const maxDepth = 3; // Maximum reflection depth
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
let color = new Vec3(0, 0, 0);
// Super-sampling anti-aliasing
for (let sy = 0; sy < samples; sy++) {
for (let sx = 0; sx < samples; sx++) {
const u = (x + (sx + 0.5) / samples) / canvas.width * 2 - 1;
const v = 1 - (y + (sy + 0.5) / samples) / canvas.height * 2;
const direction = new Vec3(
u * Math.tan(camera.fov * Math.PI / 360) * camera.aspectRatio,
v * Math.tan(camera.fov * Math.PI / 360),
1
).normalize();
color = color.add(traceRay(camera.position, direction, maxDepth));
}
}
// Average samples and apply gamma correction
color = color.mul(1 / (samples * samples));
color = new Vec3(
Math.pow(color.x, 1/2.2),
Math.pow(color.y, 1/2.2),
Math.pow(color.z, 1/2.2)
);
const index = (y * canvas.width + x) * 4;
data[index] = Math.min(255, Math.max(0, Math.floor(color.x * 255)));
data[index + 1] = Math.min(255, Math.max(0, Math.floor(color.y * 255)));
data[index + 2] = Math.min(255, Math.max(0, Math.floor(color.z * 255)));
data[index + 3] = 255;
}
}
ctx.putImageData(imageData, 0, 0);
}
// Recursive ray tracing function
function traceRay(origin, direction, depth) {
if (depth <= 0) return new Vec3(0, 0, 0);
const sphereHit = intersectSphere(origin, direction);
const planeHit = intersectPlane(origin, direction);
let hit = null;
let isSphere = false;
// Determine closest intersection
if (sphereHit && planeHit) {
hit = sphereHit.t < planeHit.t ? sphereHit : planeHit;
isSphere = sphereHit.t < planeHit.t;
} else {
hit = sphereHit || planeHit;
isSphere = !!sphereHit;
}
if (!hit) return new Vec3(0.1, 0.1, 0.1); // Background color
let color;
if (isSphere) {
// Sphere shading
const lighting = calculateLighting(hit.point, hit.normal, direction.mul(-1));
color = sphere.color.mul(lighting.x);
// Reflections
if (sphere.reflectivity > 0) {
const reflectDir = direction.reflect(hit.normal);
const reflectOrigin = hit.point.add(hit.normal.mul(0.001));
const reflectColor = traceRay(reflectOrigin, reflectDir, depth - 1);
color = color.mul(1 - sphere.reflectivity).add(reflectColor.mul(sphere.reflectivity));
}
// Transparency
if (sphere.transparency > 0) {
const refractDir = direction; // Simplified refraction
const refractOrigin = hit.point.sub(hit.normal.mul(0.001));
const refractColor = traceRay(refractOrigin, refractDir, depth - 1);
color = color.mul(1 - sphere.transparency).add(refractColor.mul(sphere.transparency));
}
} else {
// Plane shading with texture
const textureColor = samplePlaneTexture(hit.point);
const lighting = calculateLighting(hit.point, hit.normal, direction.mul(-1));
color = textureColor.mul(lighting.x);
// Mirror-like reflections on plane
const reflectDir = direction.reflect(hit.normal);
const reflectOrigin = hit.point.add(hit.normal.mul(0.001));
const reflectColor = traceRay(reflectOrigin, reflectDir, depth - 1);
color = color.mul(0.8).add(reflectColor.mul(0.2));
}
return color;
}
// Start rendering asynchronously
setTimeout(() => {
render();
// document.body.appendChild(canvas);
}, 100);
</script>
<script src="https://arkenidar.com/web/show-source.js" > </script>
</body>
</html>