Raytracing with Shadertoy
Online raytracing tutorial using Shadertoy. - yduf
see also:
This tutorial is heavily based upon ssloy/tinyraytracer and lesser on Ray Tracing in One Weekend from Peter Shirley It’s goal is to have a working raytracer in a minimum of steps.
But rather than doing it offline with c++, I focus on doing it in a webbrowser, using Shadertoy. It has the benefit of making it very interactive, simpler (core type and function like vec3 and reflect are already there) and faster (realtime) because of the use of shader and GPU. It also has some drawbacks:
So in the end perhaps more compliated for complete beginer because of some shadertoy Magic. I let you judge (by refering yourself to the original tutorial).
Most of the text is not mine and has been copied from the other tutorials, it’s there to help understood the path taken and code modification.
Using shader allows to use direclty vec3 object that are part of the shading langage. The drawback is that there is no class and only struct and functions can be used.
This tutorial is primary made for myself and follow the same plan than the one I am referring to. When relevant I kept the same title as the chapter it maches on other tutorials.
1 - Generate an image
When creating a new shader on Shadertoy, we get some sample code which is close to the following one. Which is made to get the exact same rendering than in other tutorial.
mainImage is the equivalent of the render function. It’s job is to output for a given screen coordinate an color for this pixel. It uses normalzed coordinates (between 0 and 1). And produce the following output:
Notice: that the uv coordinates we get has the origine (0,0) at the bottom left, since the color is dark there (which is not necessary the same on the other tutorials).
2 - Rays, a simple camera, and background
At the core of a ray tracer is to send rays through pixels and compute what color is seen in the direction of those rays. This is of the form calculate which ray goes from the eye to a pixel, compute what that ray intersects, and compute a color for that intersection point. When first developing a ray tracer, I always do a simple camera for getting the code up and running. I also make a simple color(ray) function that returns the color of the background (a simple gradient).
Peter Shirley wrote “Note that I do not make the ray direction a unit length vector because I think not doing that makes for simpler and slightly faster code.”
At least the intersection code that I am using latter assume unit vector for ray direction, I spend sometime understanding this, and to avoid further issue I choose to normalize ray vector upfront in the camera code.
3 - ray tracing
Now for each pixel we will form a ray coming from the origin and passing through our pixel, and then check if this ray intersects with the sphere:
I want to define one sphere in my code and draw it without being obsessed with materials or lighting.
We already have the camera in place, so we just have to check if ray originating from the camera, intersect with the sphere. As camera coordinate and world coordinate are the same we just have to compute ray/sphere intersection (code taken from this blog). We just had that check to the cast_ray function:
4 - Lighting
The image is perfect in all aspects, except for the lack of light. Throughout the rest of the article we will talk about lighting. We will trick the eye by drawing completely non-physical, but visually plausible results. To start with: why is it cold in winter and hot in summer? Because the heating of the Earth’s surface depends on the angle of incidence of the Sun’s rays. The higher the sun rises above the horizon, the brighter the surface is. Conversely, the lower it is above the horizon, the dimmer it is. And after the sun sets over the horizon, photons don’t even reach us at all.
Back our spheres: we emit a ray from the camera (no relation to photons!) at it stops at a sphere. How do we know the intensity of the intersection point illumination? In fact, it suffices to check the angle between a normal vector in this point and the vector describing a direction of light. The smaller the angle, the better the surface is illuminated. Recall that the scalar product between two vectors a and b is equal to product of norms of vectors times the cosine of the angle between the vectors: a*b = | a | b | cos(alpha(a,b)). If we take vectors of unit length, the dot product will give us the intensity of surface illumination. |
Thus, in the cast_ray function, instead of a constant color we will return the color taking into account the light sources:
5 - Better materials
The dot product trick gives a good approximation of the illumination of matt surfaces, in the literature it is called diffuse illumination. What should we do if we want to draw shiny surfaces?
This trickery with illumination of matt and shiny surfaces is known as Phong reflection model. The wiki has a fairly detailed description of this lighting model. It can be nice to read it side-by-side with the source code. Here is the key picture to understanding the magic:
So lets introduce Phong Material to the code:
Beyond the spheres
To continue the tutorial by adding shadows and reflections, we need more than one objects. Rather than using more spheres, I wanted to add a proper floor now.
To be continued…