草庐IT

c++ - OpenGL 渲染与自己的 Phong 照明实现

coder 2024-02-05 原文

我已经使用以 (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 用于显示的 Gamma 值和我的显示器的默认 Gamma 值而发生。我也知道 OpenGL(或者至少是我提供的那些部分)不能在对象上转换阴影。并不是说这与所讨论的问题相关,而是让我想知道它是否只是 OpenGL 与我正在尝试做的事情之间的显示和功能差异。

感谢您的帮助。

最佳答案

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

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

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

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

关于c++ - OpenGL 渲染与自己的 Phong 照明实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2534774/

有关c++ - OpenGL 渲染与自己的 Phong 照明实现的更多相关文章

  1. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  10. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

随机推荐