views:

294

answers:

2

Hello:

I have implemented a Phong Illumination Scheme using a camera that's centered at (0,0,0) and looking directly at the sphere primitive. The following are the relevant contents of the scene file that is used to view the scene using OpenGL as well as to render the scene using my own implementation:

ambient 0 1 0

dir_light  1 1 1       -3 -4 -5

# A red sphere with 0.5 green ambiance, centered at (0,0,0) with radius 1
material  0 0.5 0  1 0 0    1 0 0   0 0 0  0 0 0  10 1 0
sphere   0    0 0 0    1   

Here

The resulting image produced by OpenGL.

Here

The image that my rendering application produces.

As you can see, there are various differences between the two:

  1. The specular highlight on my image is smaller than the one in OpenGL.
  2. The diffuse surface seems to not diffuse in the correct way, resulting in the yellow region to be unneccessarily large in my image, whereas in OpenGL there's a nice dark green region closer to the bottom of the sphere
  3. The color produced by OpenGL is much darker than the one in my image.

Those are the most prominent three differences that I see. The following is my implementation of the Phong illumination:

R3Rgb Phong(R3Scene *scene, R3Ray *ray, R3Intersection *intersection)
{
  R3Rgb radiance;
  if(intersection->hit == 0)
  {
    radiance = scene->background;
    return radiance;
  }

  R3Vector normal = intersection->normal;
  R3Rgb Kd = intersection->node->material->kd;
  R3Rgb Ks = intersection->node->material->ks;

  // obtain ambient term
  R3Rgb intensity_ambient = intersection->node->material->ka*scene->ambient;

  // obtain emissive term
  R3Rgb intensity_emission = intersection->node->material->emission;

  // for each light in the scene, obtain calculate the diffuse and specular terms
  R3Rgb intensity_diffuse(0,0,0,1);
  R3Rgb intensity_specular(0,0,0,1);
  for(unsigned int i = 0; i < scene->lights.size(); i++)
  {
    R3Light *light = scene->Light(i);
    R3Rgb light_color = LightIntensity(scene->Light(i), intersection->position);
    R3Vector light_vector = -LightDirection(scene->Light(i), intersection->position);

    // calculate diffuse reflection
    intensity_diffuse += Kd*normal.Dot(light_vector)*light_color;

    // calculate specular reflection
    R3Vector reflection_vector = 2.*normal.Dot(light_vector)*normal-light_vector;
    reflection_vector.Normalize();
    R3Vector viewing_vector = ray->Start() - intersection->position;
    viewing_vector.Normalize();
    double n = intersection->node->material->shininess;
    intensity_specular += Ks*pow(max(0.,viewing_vector.Dot(reflection_vector)),n)*light_color;

  }

  radiance = intensity_emission+intensity_ambient+intensity_diffuse+intensity_specular;
  return radiance;
}

Here are the related LightIntensity(...) and LightDirection(...) functions:

R3Vector LightDirection(R3Light *light, R3Point position)
{
  R3Vector light_direction;
  switch(light->type)
  {
    case R3_DIRECTIONAL_LIGHT:
      light_direction = light->direction;
      break;

    case R3_POINT_LIGHT:
      light_direction = position-light->position;
      break;

    case R3_SPOT_LIGHT:
      light_direction = position-light->position;
      break;
  }
  light_direction.Normalize();
  return light_direction;
}

R3Rgb LightIntensity(R3Light *light, R3Point position)
{
  R3Rgb light_intensity; 
  double distance;
  double denominator;
  if(light->type != R3_DIRECTIONAL_LIGHT)
  {
    distance = (position-light->position).Length();
    denominator = light->constant_attenuation + 
                         light->linear_attenuation*distance + 
                         light->quadratic_attenuation*distance*distance;
  }   

  switch(light->type)
  {
    case R3_DIRECTIONAL_LIGHT:
      light_intensity = light->color;
      break;

    case R3_POINT_LIGHT:
      light_intensity = light->color/denominator;
      break;

    case R3_SPOT_LIGHT:
      R3Vector from_light_to_point = position - light->position;
      light_intensity = light->color*(
                        pow(light->direction.Dot(from_light_to_point),
                            light->angle_attenuation));
      break;
  }
  return light_intensity;
}

I would greatly appreciate any suggestions as to any implementation errors that are apparent. I am wondering if the differences could be occurring simply because of the gamma values used for display by OpenGL and the default gamma value for my display. I also know that OpenGL (or at least tha parts that I was provided) can't cast shadows on objects. Not that this is relevant for the point in question, but it just leads me to wonder if it's simply display and capability differences between OpenGL and what I am trying to do.

Thank you for your help.

+1  A: 

As a first step, I would check whether your intersection surface normal is normalized, especially important when calculating diffuse and specular term dot products.

For debugging purposes, you can check the outputs of your lighting terms (such as scene ambient output, light ambient-diffuse-specular output, light attenuation factors, etc) one by one, 0'ing the other terms in the equations. Some simple terms are likely to produce identical output, and you can narrow your search down to fewer lines of code with this approach. It may even turn out to be related to other objects / methods in your implementation.

Also, please keep in mind that OpenGL's Phong shading does not follow the Phong shading model strictly, because the normals are calculated per vertex and then interpolated inside the triangles, they are not calculated per point on the surface. Your sphere model seems to be tessellated enough, so this should not be a practical problem.

OpenGL does not performs gamma correction unless you use sRGB color space as a render target, as far as I know. I would expect a correct software implementation to produce very similar results of a hardware OpenGL implementation. Happy debugging :)

tersyon
A: 

In my case, my initial guess about the differences in gamma values was correct. The main program that called my rendering algorithm performed gamma correction by correcting each pixel's RGB value of my image by doing an image->TosRGB() call. After commenting out the call, I obtained the image produced by OpenGL.

Myx