光线追踪 - 如何结合漫反射和镜面反射颜色?

11

我一直在阅读有关光线追踪和着色的文章,但我的光线追踪图像看起来并不太好。我指的是高光附近非常亮绿色的区域。结果绿色在这里已经达到了最大值。

如何调整颜色和/或着色计算以使其看起来正确?

(别在意傻瓜代码, 我只是想先确保原则正确)。

以下是它的样子:

enter image description here

这里只显示漫反射成分:

enter image description here

这里只显示镜面反射成分:

enter image description here

编辑:将漫反射改为Color diffuseColor = ColorMake(0.0f, 0.6f, 0.0f);然后图像看起来是这样的:

enter image description here

Point lightPosition = PointMake(-100.0f, 100.0f, -100.0f);
Color diffuseColor  = ColorMake(0.0f, 1.0f, 0.0f);
Color specularColor = ColorMake(1.0f, 1.0f, 1.0f);
Color pixelColor    = ColorMake(0.0f, 0.0f, 0.0f);

//  Trace...

            // Diffuse
            Point intersectionPosition = PointMake(x, y, z);
            Vector intersectionNormal = VectorMake((x - xs) / rs, (y - ys) / rs, (z - zs) / rs);
            Vector intersectionNormalN = VectorNormalize(intersectionNormal);
            Vector lightVector          = VectorSubtract(lightPosition, intersectionPosition);
            VectorlightVectorN         = VectorNormalize(lightVector);
            float      cosTheta        = VectorDotProduct(intersectionNormalN, lightVectorN);
            if (cosTheta < 0.0f)
            {
                cosTheta = 0.0f;
            }

            pixelColor = ColorMultScalar(diffuseColor, cosTheta);

            // Specular
            Vector incomVector    = VectorSubtract(intersectionPosition, lightPosition);
            Vector incomVectorN   = VectorNormalize(incomVector);

            float myDot = - VectorDotProduct(incomVectorN, intersectionNormalN);
            float myLen = 2.0f * myDot;

            Vector tempNormal     = VectorMultScalar(intersectionNormalN, myLen);
            Vector reflectVector  = VectorAdd(tempNormal, incomVectorN);
            Vector reflectVectorN = VectorNormalize(reflectVector);

            float mySpec = MAX(-VectorDotProduct(reflectVectorN, incomVectorN), 0);
            mySpec       = powf(mySpec, 5);

            specularColor = ColorMultScalar(specularColor, mySpec);
            pixelColor    = ColorAdd(pixelColor, specularColor);
            pixelColor    = ColorClamp(pixelColor);

            [self putPixelatX:i andY:j andR:pixelColor.r andG:pixelColor.g andB:pixelColor.b];
4个回答

7
问题在于,当你计算球体的漫反射颜色时,已经有一小部分像素点的绿色通道是1或非常接近1的。将“phong”组件(在所有通道中都接近1)加到其中会产生一片像素值大于等于1的区域。当你将颜色值限制在1以内时,这个大于等于1的区域就会显现出来。
你可以通过使用图片编辑程序并对两个图层进行“相加”叠加来测试此问题(phong图层在漫反射图层上方)。这将给出你看到的结果 - 这是预期的结果。
你可以通过多种措施避免这个问题:
  1. 您可以稍微降低光源的亮度,即通过用亮度(例如0.8或0.7)乘以从余弦计算出的漫反射强度来实现。
  2. 您可以限制球体的颜色饱和度(特别是绿色),使其不那么绿;)
  3. 使用色调映射运算符将像素的颜色值归一化为[0..1]范围-然而,这个主题非常广泛-Wikipedia可能会给出一个很好的介绍。对于非基于物理的渲染,您甚至不必完全使用这些较简单的色调映射运算符也可能足以产生令人愉悦的效果。

我的光线追踪实验已经有几年了,但您可以尝试这些方法。


更新1:

我注意到一件事,当你对输出图像进行伽马校正时,效果会减弱;)-好吧,那有点不完美。

最终的解决方案是采用物理正确或使用另一种着色模型:维基百科上的高光


更新2:

一个实际的解决方案是计算 Phong 光照模型对最终像素颜色的贡献(即你的变量 mySpec)。这个想法是只在镜面反射不为0时使用部分漫反射组件,也就是说,如果你有一些镜面反射成分,那么你实际上看不到太多(或者根本看不到)漫反射成分,因此可以进行调整:

float diffuseContrib = 1.f - mySpec;

这看起来很不错,但我不确定它是否正确 :).

请注意;这假设您的镜面和漫反射分量在[0..1]范围内。

我的结果是这样的:

diffuse contribution calculated using the specular contribution


6

“高光+漫反射+环境光”照明模型一直存在问题。它是一个hack,因此没有正确性的保证。

如果您非常想巩固自己的基础,请查看Matt Pharr和Greg Humphreys的优秀著作《基于物理的光线跟踪》。


这并不正确,这就是关键所在。不能有“正确性保证” ;) - 这只是用方便的数学函数(例如phong的pow和一些任意值的加法)来着色像素的一种方式。至少我是这么看的。最重要的是; PBRT FTW!!1 - Marcus Borkenhagen
虽然你的观点完全正确,但很多人跳进光线追踪的时候并没有意识到这一点——他们通常被漂亮的图片所吸引,却不知道需要进行大量的调整 :) - Rahul Banerjee

1
你应该了解Blinn/Phong模型。这是一些着色器片段代码示例。 基本上,您需要根据各自的角度缩放单个组件(环境光、漫反射、高光项)并将它们加起来。
varying vec3 N;
varying vec3 v;    
void main (void)  
{  
   vec3 L = normalize(gl_LightSource[0].position.xyz - v);   
   vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0)  
   vec3 R = normalize(-reflect(L,N));  

   //calculate Ambient Term:  
   vec4 Iamb = gl_FrontLightProduct[0].ambient;    

   //calculate Diffuse Term:  
   vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);
   Idiff = clamp(Idiff, 0.0, 1.0);     

   // calculate Specular Term:
   vec4 Ispec = gl_FrontLightProduct[0].specular 
                * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess);
   Ispec = clamp(Ispec, 0.0, 1.0); 
   // write Total Color:  
   gl_FragColor = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec;     
}

摘自:http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/lighting.php


1
为什么不做 (光 + 环境) * 漫反射 + 镜面反射?也就是在将光照和阴影与漫反射相乘之后再添加镜面反射成分?这样可以产生清晰的高光效果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接