Introduction
In this tutorial we will be creating a basic Lambert LightModel. From this tutorial you'll learn how you can create your own lighting models to plug them in into AllegRT.
Creating the class
The first thing to do is create the header. It's a good idea to make sure the header is only included once, using C macros. Now we will create a class Lambert derived from LightModel and we need to include LightModel's header file. There's also a public default constructor and a public default destructor.

#ifndef LAMBERT
#define LAMBERT

#include "lightmodel.h"

class Lambert:
public LightModel
{
private:
public:
Lambert();
~Lambert();
};

#endif
The lonely function
There's only one function on our class (and on class LightModel :), calcLight. It returns the color after adding the Point's light intensity. The function takes as parameters: a pointer to the Light, a reference to the point's Material, a pointer to the ray and a pointer to the Scene.

~Lambert();

void calcLight(Light *light, Material &m, Point *ray, Scene *scene);
};
Prototype of the source
The header is done. Now we must implement the class. We start by including the Lambert header. Then we create the empty bodies for the constructor, destructor and the calcLight function.

#include "lambert.h"

Lambert::Lambert()
{
}

Lambert::~Lambert()
{
}

void Lambert::calcLight(Light *light, Material &m, Point *ray, Scene *scene)
{
}
The lighting function
Inside the calcLight function we must code our lighting equation. The diffuse lighting equation is simply: light color * surface color * surface diffuse constant * angle between normal and light * attenuation. The angle is defined by the dot product of the normal and the light. The equation requires all values to be between 0 and 1, so we have to work with floats. To transform components (r,g,b) of colors into 0-1 values we simply transform them into floats and divide by 255. So we declare variables that represent the transformed values of each component of each color. To simplify the code we retrieve the light's material onto a Material object.

void Lambert::calcLight(Light *light, Material &m, Point *ray, Scene *scene)
{
float mr = (float)m.getR() / 255.f;
float mg = (float)m.getG() / 255.f;
float mb = (float)m.getB() / 255.f;

Material lm = light->getMaterial();

float lr = (float)lm.getR() / 255.f;
float lg = (float)lm.getG() / 255.f;
float lb = (float)lm.getB() / 255.f;
}
N dot L
To implement the equation, it's necessary to calculate the angle. To calculate the angle we require the normal and the light ray. The light ray is given by the light position minus the intersection position. The light position retrieved by the light's Primitive's getRandomPoint. We save the angle in variable ndotl.

void Lambert::calcLight(Light *light, Material &m, Point *ray, Scene *scene)
{
float mr = (float)m.getR() / 255.f;
float mg = (float)m.getG() / 255.f;
float mb = (float)m.getB() / 255.f;

Material lm = light->getMaterial();

float lr = (float)lm.getR() / 255.f;
float lg = (float)lm.getG() / 255.f;
float lb = (float)lm.getB() / 255.f;

Point normal = m.getNormal();

Point lightPos = light->getPrimitive()->getRandomPoint();

Point lightRay = lightPos - m.getPos();

float ndotl = normal.dotProduct(lightRay);

}
Attenuation
If we want the light to fade over distance, we must multiply the light intensity by an attenuation value. We will use a simple approach. It defines attenuation as the square of the distance from the light divided by the light radius of reach (we will use the light's intensity value for the radius). So on code this is:

float ndotl = normal.dotProduct(lightRay);

float attenuation = m.getPos().calcDist(lightPos) / light->getIntensity();
attenuation = attenuation * attenuation;
}
The final color
Now to define the equation for each component of the resulting color. The equation is: ndotl * surface diffuse constant * surface component color * light component color * attenuation. However this gives the value in form of 0 - 1 so we must multiply it by 255 and transform into int to get the final value.

attenuation = attenuation * attenuation;

m.setR((int)(ndotl * m.getDiffuse() * mr * lr * attenuation * 255.f));
m.setG((int)(ndotl * m.getDiffuse() * mg * lg * attenuation * 255.f));
m.setB((int)(ndotl * m.getDiffuse() * mb * lb * attenuation * 255.f));
}
A small optimization
As multiplication is faster than division on most CPUs, instead of dividing each color by 255, we transform 255 into a 0-1 value and multiply each component by that value.

void Lambert::calcLight(Light *light, Material &m, Point *ray, Scene *scene)
{
float oneBy255 = 1.f / 255.f;

float mr = (float)m.getR() * oneBy255;
float mg = (float)m.getG() * oneBy255;
float mb = (float)m.getB() * oneBy255;

Material lm = light->getMaterial();

float lr = (float)lm.getR() * oneBy255;
float lg = (float)lm.getG() * oneBy255;
float lb = (float)lm.getB() * oneBy255;

Point normal = m.getNormal();
Result: lambert.h

#ifndef LAMBERT
#define LAMBERT

#include "lightmodel.h"

class Lambert:
public LightModel
{
private:
public:
Lambert();
~Lambert();

void calcLight(Light *light, Material &m, Point *ray, Scene *scene);
};

#endif
Result: lambert.cpp

#include "lambert.h"

Lambert::Lambert()
{
}

Lambert::~Lambert()
{
}

void Lambert::calcLight(Light *light, Material &m, Point *ray, Scene *scene)
{
float oneBy255 = 1.f / 255.f;

float mr = (float)m.getR() * oneBy255;
float mg = (float)m.getG() * oneBy255;
float mb = (float)m.getB() * oneBy255;

Material lm = light->getMaterial();

float lr = (float)lm.getR() * oneBy255;
float lg = (float)lm.getG() * oneBy255;
float lb = (float)lm.getB() * oneBy255;

Point normal = m.getNormal();

Point lightPos = light->getPrimitive()->getRandomPoint();

Point lightRay = lightPos - m.getPos();

float ndotl = normal.dotProduct(lightRay);

float attenuation = m.getPos().calcDist(lightPos) / light->getIntensity();
attenuation = attenuation * attenuation;

m.setR((int)(ndotl * m.getDiffuse() * mr * lr * attenuation * 255.f));
m.setG((int)(ndotl * m.getDiffuse() * mg * lg * attenuation * 255.f));
m.setB((int)(ndotl * m.getDiffuse() * mb * lb * attenuation * 255.f));
}