如何对 RGB 颜色应用平面着色?

How to apply flat shading to RGB colors?

我正在创建一个小型 3d 渲染应用程序。我决定对我的三角形使用简单的平面着色 - 只需计算面法线和光源之间角度的余弦值,并用它来缩放光强度。

但我不确定应该如何将阴影系数应用到我的 RGB 颜色。

例如,假设某个表面与光源成 60 度角。 cos(60度) = 0.5,所以我应该只保留发射光的一半能量。

我可以通过该系数简单地缩放 RGB 值,如以下伪代码所示:

double shade = cos(angle(normal, lightDir))
Color out = new Color(in.r * shade, in.g * shade, in.b * shade)

但是即使在较小的角度下,生成的颜色也会变得太暗。经过一番思考,这似乎是合乎逻辑的——我们的眼睛感知到光能的对数(这就是为什么我们在明亮的白天和夜晚都能看到)。并且 RGB 值已经代表了对数刻度。

我的下一次尝试是利用 linear/logarithmic 洞察力。理论上:

output energy = lg(exp(input energy) * shade)

可以简化为:

output energy = lg(exp(input energy)) + lg(shade)
output energy = input energy + lg(shade)

所以这样的阴影只是将阴影系数(负数)的对数添加到 RGB 值:

double shade = lg(cos(angle(normal, lightDir)))
Color out = new Color(in.r + shade, in.g + shade, in.b + shade)

这似乎可行,但是否正确?在真实的渲染管线中是如何完成的?

  1. 颜色RGB向量乘以色度系数

    您最初假设的余弦值。对数缩放由目标成像设备和人眼完成

  2. 如果您的颜色太深,可能的原因是:

    • 余弦值或角度值被截断为整数
    • 或者您的管道没有线性比例输出(一些伽玛校正可以做到这一点)
    • 或者你在某个地方遇到了错误
    • 或者您的角度和余弦使用不同的度量标准 (radians/degrees)
    • 你忘了把环境光系数加到色度值上
    • 你的向量是相反的或错误的(目视检查它们,看看第一个 link 如何)
    • 你的向量不在同一个坐标系中(光通常在GCS中,法线向量在模型LCS中,所以你需要至少将其中一个转换到另一个的坐标系中)
  3. cos(angle)本身通常不是用余弦计算的

    当您将所有数据作为向量获取时,只需使用点积

    double shade = dot(normal, lightDir)/(|normal|.|lightDir|)
    

    如果向量是单位大小,那么你可以放弃按大小除法...这就是法向量和光向量被归一化的原因...

  4. 一些相关问题和笔记

    • 这可能会启发一两件事(对于初学者)
    • Normal/Bump mapping 查看片段着色器并搜索 dot
    • 查看稍微复杂的照明方案

    GCS/LCS意思是global/local坐标系