Link Search Menu Expand Document

3.4: Rendering an Empty Scene


Download code
03-tracer.zip
Live example
examples/03-tracer/index.html

So, we have vectors, colors, rays, a scene, a camera… now we’re going to create the actual ray-tracer, hook that up to our HTML canvas, and render something.

We don’t have any shapes yet, so all we’ll be able to render is empty space, but we can control the background color of our scene so we should at least be able to check that the empty sky in our empty world is the right color.

Creating the renderer

Create a new file called modules/renderer.js, that looks like this:

// modules/renderer.js

import { Color } from './color.js';
import { Vector } from './vector.js';
import { Camera } from './camera.js';
import { Scene } from './scene.js';

class Renderer {
    #canvasWidth;
    #canvasHeight;

    constructor(canvasWidth, canvasHeight) {
        this.#canvasWidth = canvasWidth;
        this.#canvasHeight = canvasHeight;
    }
        
    render(scene, callback) {
        var started = new Date().valueOf();
        for (let pixelY = 0; pixelY < this.#canvasHeight; pixelY++) {
            for (let pixelX = 0; pixelX < this.#canvasWidth; pixelX++) {
                let sceneX = (pixelX / this.#canvasWidth) - 0.5;
                let sceneY = (pixelY / this.#canvasHeight) - 0.5;
                let pixelColor = scene.trace(sceneX, sceneY);
                callback(pixelX, pixelY, pixelColor);
            }
        }
        var duration = (new Date().valueOf() - started);
        console.log(`Render completed in ${duration / 1000} seconds`);
    }
}

export { Renderer, Scene, Camera, Color, Vector };

The main purpose of the Renderer class is to translate HTML canvas pixel coordinates into floating-point world coordinates, and then call scene.trace once for each pixel on our canvas.

The renderer includes a variable called step; this controls how many pixels we’ll render in each loop. The default value step=1 will render every pixel in the canvas, but this can take a long time - and if we’re trying to use console.log to debug our code, it’ll slow things to a crawl. Setting step to 10 or even 100 will render solid 10x10 or 100x100 blocks of pixels, which speeds things up considerably.

Notice that the renderer doesn’t actually update the canvas directly; instead, for each pixel that’s rendered, it’ll call a callback function and pass the x, y, color and step values.

Connecting it all together

Update the main.js file in your project with this code:

// main.js

import { Renderer, Camera, Scene, Vector, Color } from './modules/renderer.js';

let canvas = document.getElementById('my-canvas');
let ctx = canvas.getContext('2d');
let renderer = new Renderer(canvas.width, canvas.height);

function paintPixel(x, y, color) {  
  ctx.fillStyle = color.html;
  ctx.fillRect(x, y, 1, 1);
}

let camera = new Camera(new Vector(-4, 1, -5), Vector.O);
let background = Color.Blue;
let scene = new Scene(camera, background);
renderer.render(scene, paintPixel);

In this file, we create a scene containing a camera and a background color, then create a Renderer, and trace that scene, using a callback function that renders each pixel (or pixel block) to our HTML canvas element.

Checklist:

You should have a project structure now that looks like this:

Download code: examples/03.renderer.zip

Run this code: examples/03-tracer/index.html

If everything works, you’ll get a solid block of bright blue sky:

image-20220319152045451

Try changing the background color on line 4 – for example, to make our sky midnight dark blue, try:

let background = new Color(0, 20, 55);

image-20220319152156710

Review & Recap

  • The Renderer class is the connection between the browser’s HTML and canvas elements, which work in 2D screen coordinates based on pixels, and the ray-tracing engine, which works in 3D world coordinates based on vectors.
  • Any ray in our scene that doesn’t hit a shape will end up as scene.background - and because we don’t have any shapes yet, all we can do is draw empty skies.

Download code
03-tracer.zip
Live example
examples/03-tracer/index.html