Home » Programming » C++ Raytracer » Ray Tracer Part Four – Shading and illumination

Ray Tracer Part Four – Shading and illumination

gridcoin728x90_40b-99980e

Shading and Illumination

So far we can intersect a ray with a sphere, and calculate the normal at the intersect point, and in this article we will extend the raytracer to shade the sphere.  This will help give the illusion of depth to the sphere.

First it is necessary to check whether an intersectPoint is illuminated by a lightsource, or whether it is in shadow.  This is achieved by casting a ray from the intersect point towards the lightsource, and testing whether it intercepts any objects.  As soon as any object is intercepted, we know the intersectionPoint is in shade, and can stop testing. (note, although neither option is realistic, I have assumed a transparent object will not cast a shadow)

rayToLight = lightSourceCentre - intersectpoint;
lightSourceDistance = rayToLight.length();
rayToLight.normalise();

for(all non transparent objects in scene)
{
   if(currentObject.intersect(intersectPoint, rayToLight, lightSourceDistance, normal))
   {
      // ray hit an object CLOSER THAN LIGHTSOURCE
      // so point is in shadow
   }
}

This can be put in a method that returns true if a point is in shadow.  Be sure to ensure that the ray intersects at a distance less than the light source distance.

Diffuse Illumination

First we will calculate the diffuse lighting component.  This assumes that any light that hits the surface is scattered evenly in every direction.  So regardless of the viewing angle, the point will appear to be the same color.  The intensity of the light does however depend on the angle between the surface normal vector, and the vector towards the lightsource.  If the surface is perpendicular to the light source vector, the surface will receive maximum illumination (and if parallel, the surface will receive no illumination).  This diffuse lighting component can be calculated as follows:

intersectToLight = lightSourcePoint - intersectpoint;
intersectToLight.Normalise();
lDotNormal = surfaceNormal.Dot(intersectToLight);
lDotNormal = lDotNormal < 0 ? 0.0f : lDotNormal;
diffuseComponent = kd * surfaceColor * lightSourceColor * lDotNormal

‘kd’ is the diffuse coefficient, a value between 0 and 1.  Both the surfaceNormal vector and vector towards the lightSource must be normalised, and lDotNormal must be checked to ensure the value is greater than 0.  We know of course that if the dot product is negative then the surface is facing away from the lightsource, and is thus in shadow.

Phong lighting

The next component to calculate is the specular component, in this case Phong.  This gives an object the appearance of being shiny, such as plastic or metal. Again, the specular component is only calculated if the lDotNormal value is positive.

Phong

To begin with we know the vector L, N, and V (vector towards the eyePoint), and we want to find the vector R (R is the same as the reflection vector, which will be needed later). First, ensure L, N and V are all normalised.  Referring to the image above, the vector R can be calculated as (2*lDotN)*N – L.  Next take the dot product of R and V. this RdotV value is then raised to the power ‘n’.  This n value determines how concentrated the specular highlight will be. A large n value will lead to a smaller spot, a smaller n value will result in a broader spot.  We also have another value  to adjust, the specular constant ks. This should take a value between 0 and 1, and can be used to scale the intensity of the highlight.

Phong Component = ks*lightSourceColor*(RDotV^n)

This will give the specular highlight the same color as the light source color.  If the objects material is metallic, such as gold, the specular component (and reflections) tend to take on the color of the material.  Thus for metallic surfaces the Phong component becomes:

Phong Component = ks*lightSourceColor*surfaceColor*(RDotV^n)

Ambient Lighting

Ambient lighting is the addition of some lighting to every intersection point, even if in shadow.  Without this surfaces in shadow would appear completely black, which gives very unrealistic images.  Adding ambient lighting like this is far from realistic, but is simple to apply.  The way in which i have applied it is to calculate it for every light source in a scene, dependent on the lightsources colors (ka is an ambient constant, I use 0.1).

Ambient Component = ka * kd * lightSourceColor * surfaceColor

The total illumination for a point is the sum of all these components.

Total Illumination = Diffuse + Specular + Ambient

Modifications

An alternative to Phong illumination is Blinn-Phong.  Blinn-Phong has the advantage that it is faster to compute than Phong, with almost the same effect.

In the diffuse illumination formula, distance from the light source does not effect the intensity of illumination.  An object close to the light source, and one very far away will receive the same illumination (provided the angle between their normals and the lightSource ray are the same).  If you would like to account for this, simply divide the total illumination by (distanceToLightSource^2).

Using point light sources casts very hard shadows, by using soft shadows the realism of images can be greatly improved.  In order to cast soft shadows we instead use area light sources, and cast many shadow test rays from each intersection point towards random points on the surface of the area light source.  The ratio of obstructed to unobstructed rays then determines how much illumination is applied to the surface point.


Leave a comment

Contact

Email: sjh148@uclive.ac.nz