6

我使用以 (0,0,0) 为中心并直接查看球体基元的相机实现了 Phong 照明方案。以下是场景文件的相关内容,用于使用OpenGL查看场景以及使用我自己的实现来渲染场景:

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   

这里

由 OpenGL 生成的结果图像。

这里

我的渲染应用程序生成的图像。

如您所见,两者之间存在各种差异:

  1. 我图像上的高光比 OpenGL 中的要小。
  2. 漫反射表面似乎没有以正确的方式漫反射,导致我的图像中的黄色区域不必要地大,而在 OpenGL 中,靠近球体底部有一个漂亮的深绿色区域
  3. OpenGL 产生的颜色比我图像中的颜色深得多。

这是我看到的最突出的三个差异。以下是我对 Phong 照明的实现:

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;
}

以下是相关的 LightIntensity(...) 和 LightDirection(...) 函数:

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;
}

对于任何明显的实施错误,我将不胜感激。我想知道是否可能仅仅因为 OpenGL 用于显示的伽马值和我的显示器的默认伽马值而发生差异。我也知道 OpenGL(或至少我提供的部分)不能在对象上投射阴影。并不是说这与所讨论的问题相关,但它只是让我想知道它是否只是 OpenGL 与我正在尝试做的事情之间的显示和功能差异。

谢谢您的帮助。

4

2 回答 2

3

作为第一步,我将检查您的相交表面法线是否已归一化,这在计算漫反射和镜面反射项点积时尤其重要。

出于调试目的,您可以逐一检查照明项的输出(例如场景环境输出、光环境漫反射镜面反射输出、光衰减因子等),将等式中的其他项设为 0。一些简单的术语可能会产生相同的输出,并且您可以使用这种方法将搜索范围缩小到更少的代码行。它甚至可能与您实现中的其他对象/方法相关。

另外,请记住,OpenGL 的 Phong 着色并不严格遵循 Phong 着色模型,因为法线是按顶点计算的,然后在三角形内插值,它们不是按表面上的点计算的。您的球体模型似乎已经足够细分,所以这不应该是一个实际问题。

据我所知,除非您使用 sRGB 颜色空间作为渲染目标,否则 OpenGL 不会执行 gamma 校正。我希望正确的软件实现能够产生与硬件 OpenGL 实现非常相似的结果。调试愉快:)

于 2010-03-28T23:14:02.277 回答
0

就我而言,我最初对伽玛值差异的猜测是正确的。调用我的渲染算法的主程序通过调用来校正图像的每个像素的 RGB 值来执行伽马校正image->TosRGB()。注释掉调用后,我得到了 OpenGL 生成的图像。

于 2010-04-01T01:23:44.237 回答