如何实现朗伯着色

How to implement Lambertian shading

我正在使用 C++ 和 OpenGL (glDrawPixels) 开发光线追踪器,目前进展顺利。但是,我的阴影似乎有点偏离。我希望我的球体看起来更平滑一些,而强度之间没有明显的界限。目前,我只是在尝试实现 Lambertian 着色,即 Intensity = Id * pd * (N dot L)。我知道我需要如何通过归一化法向量和光向量来计算(N 点 L),但是 Id 和 pd(强度漫射和比例因子)呢?我只是使用 2.5 的常量值来实现以下目标:

(Here is a picture of what my shading looks like)

显然,2.5 是完全任意的,我之所以使用它只是因为我的其他尝试甚至不像正确的阴影。上面的等式似乎表明我应该将颜色乘以强度。这是我的方法:

Color simpleIlluminate(Vector3D normal, Vector3D light, Color color) {
    float intensity = dotProduct(normal, light)*2.5f;
    color = Color((unsigned int)intensity*color.r, (unsigned int)intensity*color.g, (unsigned int)intensity*color.b);
    return color;
}

假设法向量和光向量是正确的。颜色只是我创建的一个颜色结构,每个 RGB 值都有一个无符号整数。参数 Color color 是我要着色的球体的颜色,例如红色 = 颜色 (255,0,0)。要修改颜色的强度,我只需将每个值乘以我计算的强度,但当我 运行 它看起来不正确。

谁能解释一下我遗漏了什么,并告诉我在给定矢量和球体颜色的情况下正确的函数应该是什么样子?我到处都看过,但找不到好的解决方案。我也可能将 float 转换为 unsigned int 错误,但我不确定。感谢任何帮助。

I might also be doing my float to unsigned int conversion wrong

正确。 (unsigned int)intensity*color.r 等同于 ((unsigned int)intensity)*color.r。所以你的强度在乘以颜色之前呈现出离散的水平,从而产生这些线条。请尝试 (unsigned int)(intensity*color.r)

我会删除 2.5 因子,因为它可能会影响结果,并且这很可能会产生条带,因为您正在将因子放大到 0 和 1 范围之外。

朗伯反射率又称为余弦反射率,简单易懂。考虑一个平面,如果光线垂直于该平面,则所有光线都会落在该表面上(100%,或系数 1.0)。如果光线平行于该平面指向,则 0 光线将落在表面上(所有光线都是平行的,因此永远不会与平面相交,因此系数或 0)。

Lambertian 反射率只是光线方向与表面法线之间夹角的余弦值,范围从 0(没有光线照射到平面上)到 1.0(所有光线照射到平面上)。注意 Cosine(90) = 1。Cosine(0) = 0,因此称为余弦定律(Lambertian 反射率)。