Search code examples
graphicsraytracingshading

How to implement Lambertian shading


I'm working on a ray tracer using C++ and OpenGL (glDrawPixels), and it is going well so far. However, my shading seems to be a bit off. I want my spheres to look a little smoother without having the blatant lines between intensities. At the moment, I am just trying to implement Lambertian shading, which is Intensity = Id * pd * (N dot L). I know how I need to calculate the (N dot L) by normalizing the normal vector and light vector, but what about the Id and pd (intensity diffuse and factor of proportionality)? I am just using a constant value of 2.5 to achieve the following:

(Here is a picture of what my shading looks like)

Obviously, 2.5 is completely arbitrary, and I'm only using it because my other attempts did not even resemble proper shading. The equation above seems to suggest that I should multiply the color by the intensity. This is my method:

Color simpleIlluminate(Vector3D normal, Vector3D light, Color color) {
    float intensity = dotProduct(normal, light)*2.5f;
    color = Color((unsigned int)intensity*color.r, (unsigned int)intensity*color.g, (unsigned int)intensity*color.b);
    return color;
}

Assume that the normal vector and the light vector are correct. Color is just a color struct I made that has an unsigned int for each of the RGB values. The parameter Color color is the color of the sphere I want to shade, e.g. red = Color(255,0,0). To modify the intensity of the color, I simply multiply each value by the intensity I calculated, but it doesn't look right when I run it.

Can someone explain what I am missing and show me what the correct function should look like given the vectors and sphere color? I've looked everywhere, but I can't find a good solution. I might also be doing my float to unsigned int conversion wrong, but I'm not sure. Any help is appreciated.


Solution

  • I would remove that 2.5 factor as it is probably screwing with the results and this could very well produce the banding since you are scaling up the factor to outside the range 0 and 1.

    Lambertian reflectance is also known as Cosine reflectance and is really simply to understand. Consider a plane, if a light was perpendicular to the plane, then all light would fall on the surface (100%, or factor 1.0). If a light was pointing parallel to that plane then 0 light would fall on the surface (all light rays are parallel and thus would never intersect the plane, so a factor or 0).

    The Lambertian reflectance is simply the Cosine of the angle between light direction and surface normal and ranges from 0 (no light hitting the plane) to 1.0 (all light hitting the plane). Note Cosine(90) = 1. Cosine(0) = 0, hence called Cosine law (Lambertian reflectance).