Table of Contents

1. Intro

This document aims at giving a gentle introduction to sacr3d.

You can run the examples with the command

make examples/<the-example>

The examples presented here build on top of each other. There is generally just a few elements added between these. You can use

cd examples
diff <example-1> <example-2>

to see what was added.

2. Rendering with points

2.1. Point: Drawing a point

Let's start with the most basic thing you can want to draw: a simple point.

To see it in action go to point

Have a look at how it is done in point.scm

To build it yourself use

make examples/point

and open your browser at https://localhost:8088

2.2. Points: Drawing many points

OK now that we can draw a point, let's draw many of them!

To see the results go to points. Yey many points!

Have a look at how it is done in points.scm

2.3. Line: Connect the dots

How do we actually draw a line now between these dots ?

To watch the result go to line

Have a look at how it is done here line.scm

2.4. Window ratio: Drawing a circle

Drawing an ellipse is nice but what if we want a proper circle ? The coordinates of the screen take values between -1 and 1, regardless of the ratio of the window.

Let's adjust for that: ratio.scm

To see it running: ratio

2.5. Adding interaction: Lissajous curves

Now what happens if we change the values we enter in the sine and cosine functions? We could multiply our input x for by some values. We can even choose a different factor for each. Now what if we added even more spice and chose to let these factors depend on our mouse position?

Let's do it: interaction.scm

And tada: interaction

In no time we can play around with beautiful Lissajous curves! Can you spot where to place your mouse to get a circle again ?

2.6. Animation: Letting it play

We can now control parameters with out mouse position. How can we make a parameter to change by itself over time ? Let's make use of our time global variable!

animation.scm

animation

2.7. TODO Adding colors

Show how to change the color of the drawn vertices

2.8. 3D: Entering the next dimension

Let's add some depth to our render by adding a third dimension to our Lissajous curve. 3D rendering is all about giving the illusion of 3D: screens are flat anyways. Before we dive into more realistic looking things, we can use a simple trick to make our render appear 3D: make objects further away look smaller.

3d

3d.scm

2.9. Camera: Project to the screen (Ongoing)

OK now let's do proper 3D and simulate the principle of a real-world camera.

OpenGL already has some sort of default camera that we made use of until now: it is called the clip space: it's a cube of length 2 with a diagonal from (0,0,0) to (1,1,1).

Anything you put in there gets rendered to the screen, except that things closer will hide things further away. You can think of it as squishing everything in the 3D clip cube to the 2D screen. The 2D screen is by default the square at -1 in the z direction. The way the squishing is done is something called an orthogonal projection; basically it projects everything in the cube along lines orthogonal to the target screen. We will play with the idea of projection lines latter when we do ray-tracing.

But for now let's see if we can make use the builtin orthogonal projection OpenGL offers from it's clip cube to the screen. The trick is as follows: since things that end up in the clip cube get rendered, anything we put in there will also get rendered. So why not just put what we expect a camera to see in there ? What a camera sees is some sort of quadrilateral cone.

But if we cut a portion this cone we get something like a deformed cube. This is called the view frustum. Indeed its a frustrated (or limited) version of the infinite cone of vision of an ideal camera. We then just need to map this deformed frustum cube to the clip cube.

Now what is the 3D transformation that does that? The type of transformation we will need is called a projective transformation, a class of transformations characterized by the property of mapping lines to lines. Indeed we want to map the edges of the frustum cube to the corresponding edges of the clip cube.

To simplify things, let's assume our camera's focal point (the apex of the view cone) is at the origin, that the camera faces the z direction and that the near frustum square matches the square with diagonal (0, 0, 1) (1, 1, 1). Now how would the orthogonal camera's cone of vision look like ? Well it is not a quadrilateral cone, but a quadrilateral cylinder. Cylinders and cones are projectively the same things: a cylinder in just a cone with the apex "at infinity". So to map our cone to the cylinder, we need to map the apex of the cone to infinity.

camera

camera.scm

2.10. TODO Triangles

Most computer graphics tutorials start with this. "Hello, Triangle" is the "Hello, World" of CG. We deliberately introduced lower dimensional primitives first like points and line, because they are easier to understand and you can still do fun stuff with them!

We already know how to place vertices in clip space. Now we just need to introduce a draw command to connect the dots and render them as triangles.

2.11. TODO Color interpolation

Set color directly on the vertices and pass it to the vertex shader

2.12. TODO Parameterized surfaces

3. Rendering with pixels

3.1. Fragment pass: Assign a color to each pixel

Now that we know how to render triangles. We can also render a square as two triangles. We also saw how to interpolate between color values between vertices.

We are not limited to interpolating between color values though. We can interpolate between any values of our liking.

Now here comes the really cool part: If we render a square that fill the screen, we can assign coordinates to our vertices and they will get interpolated across the whole screen. Now we in the fragment shader we have access to the coordinates on screen.

This is what is called a fragment pass: your vertex shader is limited to filling the screen a square and your fragment shader draws thing using screen coordinates.

This opens up a whole new paradigm of rendering where the starting point is not vertices anymore but pixels.

fragment

fragment.scm

3.2. Algebraic curves: Rendering math using screen coordinates

An algebraic equation in the plane is something like

The algebraic object associated with this equation is the unit circle

So now that we have access to the two screen coordinates we can plot an algebraic object in the plane by checking which coordinates satisfy an equation.

algebraic

algebraic.scm

3.3. 2D Grid: Showing the coordinate system

Showing algebraic curves is fun but it is difficult to study them in details when they just appear in the void.

Let's add a grid of coordinate lines, to better orient ourselves!

grid

grid.scm

3.4. Blending: Layering renders on top of each other

Our grid is nice but if you look closely it looks like it is over the curve we render. What if we want to draw the curve over the grid ?

blend

blend.scm

3.5. Thin algebraic curves: Making things pretty

We are able to visualize an algebraic curve on the grid, but the thickness of the curve varies because the values of polynomial function close to zero do not behave uniformally with respect to the input.

One way to solve this is to use a clever trick involving the magnitude of the partial derivates of the polynomial. There is a GLSL function that computes just that : `fwdith`.

partial

partial.scm

4. Multi-pass rendering

4.1. Post-processing: sampling from previous passes

4.2. TODO Feedback Pass: sampling from the previous output of the same pass

5. TODO Ray-Tracing

6. TODO Color Theory

7. TODO Complex Numbers