How to normalize light when calculating it in a ray tracer

Hello, I’m building a Ray Tracer and I’ve the following problem:
I’m at the point where I want to calculate direct illumination on a surface, without shadows, without some complicated BRDF methods. The method that I’m using for calculating the light gives me 2 different results for 2 different models.
This is the method:

ofColor RayCaster::L_scatteredDirect(const shared_ptr<Surfel>& surfelX,const glm::vec3 wo) const{
    glm::vec3 Light = surfelX->emittedRadiance(wo);
    for (int i = 0; i<lights.size(); i++) {
        glm::vec3 lightPos = lights[i].getGlobalPosition();

        if (visible(surfelX->getPosition(), lightPos)) {
            glm::vec3 offset = lightPos - surfelX->getPosition();
            const float distanceToLight = glm::length(offset);
            glm::vec3 wi = glm::normalize(offset);
            glm::vec3 color = surfelX->getColor();
            // light power is not implemented in ofLight,
            // I use a getDiffuseColor().getBrightness() for this
            float lightPower = lights[i].getDiffuseColor().getBrightness() * 100;
            float biradiance = lightPower / (4 * PI * sqrt(distanceToLight));

            //lambertian light
            float dProd = abs(glm::dot(wi, surfelX->getGeometricNormal()));
            glm::vec3 finiteScatteringDensity = surfelX->finiteScatteringDensity(wi, wo);
            Light +=
                biradiance * // comment out this when debugging
                finiteScatteringDensity *
                glm::vec3( dProd ) *
                color;
        }

    }
    return ofColor(Light.x*255, Light.y*255, Light.z*255);
};

This is the Surfel class, in case you are wondering how the finiteScatteringDensity is doing, well, nothing for now.

#include "Surfel.h"

Surfel::Surfel(const glm::vec3& _faceNormal, const glm::vec3& _rayDirection, const glm::vec3 _position){
    geometricNormal = _faceNormal;
    shadingNormal = _faceNormal; // TODO, this should be calculated, for example from a bump map
    position = _position;
//  We'll allow all of our triangles to be “two sided” for now, so that we see consistent results from the front and back of the triangle. When you detect a hit on the triangle, if the dot product of the ray direction and the counter-clockwise normal is positive, then the ray actually struck the triangle from behind.
    if(glm::dot(_faceNormal, _rayDirection) >= 0){
        backface = true;
    }
    color = glm::vec3(1.f,1.f,1.f); //for now, all the surfaces are white. TODO, pass the color when initializing the Surfel
}

glm::vec3 Surfel::emittedRadiance(glm::vec3 wo) const {
    return glm::vec3(0.f,0.f,0.f); //material is not emissive
};

glm::vec3 Surfel::finiteScatteringDensity(const glm::vec3& w_i, const glm::vec3& w_o) const {
    //easy for now. TODO, implement different reflections
    return lambertianReflectivity();
}

glm::vec3 Surfel::getColor() const{
    return glm::vec3(1.0,1.0,1.0);
}

// From http://graphicscodex.com/projects/rays/
// "The simplest material parameter is to set the G3D::UniversalSurfel::lambertianReflectivity to the color that you want the object to appear."
glm::vec3 Surfel::lambertianReflectivity() const {
    return color;
}

This is the image that I obtain running the ray tracer on a teapot :

with a 640x400 resolution.

And this is the image that I see when I run the same code with a cornell box model.

As the cornell box model is pretty small, I’ve to adjust the light position in order to have the poinLight on the ceil of the Box. This let me think that the problem was probably that the light sometime goes out of range.
In fact, when I change this:

            Light +=
                biradiance * // comment out this when debugging
                finiteScatteringDensity *
                glm::vec3( dProd ) *
                color;

to this:

            Light +=
                finiteScatteringDensity *
                glm::vec3( dProd ) *
                color;

I’ve a correct image.

So, my question is, how can i change L_scatteredDirect in order to have the values of the glm::vec3 Light variable inside that method, ranging from glm::vec3(0,0,0) to glm::vec3(1.f,1.f,1.f) ?

I would just use ofFloatColor instead of ofColor, then you don’t need to normalize, and you can use the ranges higher than 1 to do some hdr postproduction effects if you need

Many thanks Arturo! it worked. Now I’ve another problem, related to the fact that I’m obtaining the lightPower value out of the brightness of the light color and then I multiply it for an arbitrary scalar, that changes a lot the image. this is with value 100

float lightPower = lights[i].getDiffuseColor().getBrightness() * 100;

And this is with value 10

I think it make sense to have the lightPower value as a parameter that can be adjusted in the GUI. But yes, problem solved :wink:

since float colors don’t stop at 1 you can also replace that value with colors for the light diffuse that go beyond 1, so 10 would be ofFloatColor(10,10,10) and 100, ofFloatColor(100,100,100)…

Mmm, I’m trying to remain as close as possible to the way in which usually light is calculated in a ray tracer. In almost all the examples that I’ve seen, lightPower is a scalar, and it is used to calculate the “biradiance” in this way.
biradiance = lightPower / (4 * PI * sqrt(distanceToLight));
If I use a ofFloatColor instead of a scalar the equation does not hold anymore.