C++ 具有phong着色和光线跟踪的瑕疵

C++ 具有phong着色和光线跟踪的瑕疵,c++,graphics,raytracing,phong,C++,Graphics,Raytracing,Phong,在光线跟踪器上实现phong着色时,我目前正在试验不同的人工制品 第一种情况发生在我以我认为正确的方式实施镜面反射照明计算时:将光源的贡献累加为: specular_color += light_intensity * std::pow (std::max(0.f,reflected*camera_dir),mat.ns); 但是,如果我不累积贡献,通过 specular_color = light_intensity * std::pow (std::max(0.f,reflected*

在光线跟踪器上实现phong着色时,我目前正在试验不同的人工制品

第一种情况发生在我以我认为正确的方式实施镜面反射照明计算时:将光源的贡献累加为:

specular_color += light_intensity * std::pow (std::max(0.f,reflected*camera_dir),mat.ns); 


但是,如果我不累积贡献,通过

specular_color = light_intensity * std::pow (std::max(0.f,reflected*camera_dir),mat.ns);
我明白了:



看起来更接近了,但仍然有一些人工制品

打印specular_color变量(即3浮点对象)假定的一些值时,我收到的值高达

镜面反射颜色(200)后:1534.73 1534.73 1534.73

对于x和y=200且添加+符号的像素

没有它,我会得到:

镜面反射颜色(200)后:0

所有这些浮点值都是用钳制的

a [ctr] = min (final_color.blue*255.0f,255.0f);
     a [ctr+1] = min (final_color.green*255.0f,255.0f);
     a [ctr+2] = min (final_color.red*255.0f,255.0f); 
用于文件写入

最终的_值只不过是:

final_color = diffuse_color * mat.ks + specular_color * mat.kd;
镜面反射颜色(光照强度、反射、相机方向)的组件似乎是正确的,因为在其他地方使用时没有问题


因此,如果您能提供任何关于错误位置和修复方法的建议,我将不胜感激。

第一条建议:不要使用0.0-255.0表示光源的强度。使用0.0到1.0。缩放和积累将更好地发挥作用。显示时,将最终强度映射到0-255范围内的像素值

如果将“最亮”的灯光表示为255,并且场景中有一个这样的灯光,则可以忽略它。但是如果你再加上第二盏灯,任何给定的像素都可能被两盏灯照亮,最终亮度是你能代表的最亮物体的两倍,这基本上就是你第一个例子中的情况——你的大多数像素太亮了,无法显示

要对灯光进行规格化,必须通过
灯光数量*255进行额外的除法和乘法。那会变得一团糟。强度会线性缩放和累积,但像素值不会。因此,使用强度并在最后转换为像素值

要进行映射,您有几个选项

  • 找到图像中的最低和最高强度,并使用线性映射将其转换为0-255之间的像素值

    例如,如果最低强度为0.1,最高强度为12.6,则可以按如下方式计算每个像素值:

    pixel_value = (intensity - 0.1) / (12.6 - 0.1) * 255;
    
    这不是很真实(物理上),但无论场景中有多少(或很少)灯光,都足以获得良好的效果。你实际上是在做一个粗糙的“自动曝光”。不幸的是,你的“黑暗”场景看起来太亮了,而你的明亮场景可能看起来太暗了

  • 眼睛和胶片对光强的实际响应曲线不是线性的。它通常是一条s形曲线,通常可以近似为:

    response = 1 - exp(intensity / exposure);
    pixel_value = 255 * response;  // clamp to 0 - 255
    
    这是一个很好的解释。基本上,添加第二个相同强度的光不应该使像素亮度增加两倍,因为这不是我们(或胶片)真正感知亮度的方式

    响应曲线可能更加复杂。老式感光胶片有着奇怪的特性。例如,与使用快速快门拍摄的“等效”曝光相比,使用胶片进行长时间曝光可能会产生不同的图像

  • 当你想获得超准确度时,你还需要观察观察系统的伽马射线,它不仅试图解释感知中的非线性,还试图解释显示器和传感器中的非线性。如果我理解正确的话,HDR最终是关于仔细地将测量的强度映射到显示值,以便在广泛的强度范围内保持对比度

    最后,虽然它与您显示的问题没有直接关系,但看起来您在包含的代码片段中反转了漫反射和镜面反射材质属性:

    final_color = diffuse_color * mat.ks + specular_color * mat.kd;
    

    我假设您打算使用
    mat.ks
    进行镜面反射,使用
    mat.kd
    进行漫反射。当您试图调整这些值时,这可能会使事情变得混乱。

    如果您查看Phong反射模型的一般定义,它通常是所有光源的总和,形式如下:

    kd*(L.N)*i+ks*(R.V)^a*i

    在这种情况下,
    kd
    ks
    是漫反射和镜面反射常数,
    L
    N
    R
    是相关向量,
    a
    是反光度,
    i
    是当前入射光的强度。您的版本稍有不同,因为您可以用拆分求和并移出常数的方式重写,但这不是一种常见的方式:

    kd*∑((L.N)*i)+ks*∑((R.V)^a*i)

    之所以没有做到这一点,是因为一般渲染方程的工作原理,它通常以积分的形式存在于半球的某一点上:

    Lo(wo)=Le(wo)+∫ f(wi,wo)*(wi.n)*Li(wi)dwi

    在这种情况下,
    Lo
    是沿
    wo
    方向的出射辐射,
    Le
    是沿某一方向的发射贡献,
    f
    是BRDF,
    n
    是表面法线,
    Li
    是入射方向
    wi
    (这是正在整合的内容)。在实践中,这是作为总和实现的,再次表明在一个方向上的点上的照明贡献是一个方向上每个单独照明计算的加权总和。对于具有点光源的简单渲染器(如您的点光源),这只是每个光源贡献的总和,因为它假定为l光线只能来自光源,而不是环境本身。这不是一个真正的问题,但是如果你计划实现一个更复杂的照明模型,那么你必须重写你的程序结构