PBR读书笔记三:Color & Radiometry
众所周知,自然、物理界的光照不是靠RGB
三个离散分量描述的,而是由不同强度的不同波长\(\lambda\)的光组成的,因此如果要基于物理地渲染,对于自然光谱的模拟是必不可少的,在pbrt
和mistuba
两款渲染器中,都采用了Spectrum
类来承载光谱数据,对于不同波长的数据,我们描述为SPD,aka spectral power distribution,光谱能量分布。
Spectrum
自然界光谱不是离散描述的,于是我们可以像傅立叶变换一样,用一组带系数的基底函数\(c_iB_i(\lambda)\)来逼近实际的波形。
pbrt中,定义了两种实际的Spectrum
类,并且使用一个typedef
来在实际的使用中切换两者的类型:
1 | typedef RGBSpectrum Spectrum; |
mitsuba
中也用了相同的操作来切换单精度和双精度的浮点数。
小插曲
c++里可以使用typedef
来定义新类型,特别是在c里面,为了避免struct
关键字冗余,往往会使用其定义结构体,那么c++推出的using
关键字与其相有什么区别呢?
——答案是,没有,在标准中被如此描述:
A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id.
在c++中,我们可以放心大胆地使用using
替换define
等一系列关键字!同时,我也比较推荐常量使用constexpr
定义,而不是使用#define
,让我们享受类型系统带来的便利。
定义了一个基类
1 | template <int nSpectrumSamples> class CoefficientSpectrum{ |
这里,pbrt假定我们使用了系数线性地放缩基函数并且求和。
SampledSpectrum
正如其名,SampledSpectrum
对光谱的一个区间进行采样,并且等宽地对波长进行系数记录。
XYZ色彩
人类视觉的一个重要属性在于:任何颜色都可以通过三个颜色分量表示,这使得对于任意的SPD,我们都可以构建一套三维分量\(x_\lambda\),\(y_\lambda\),\(z_\lambda\)。
分量的计算如下:
\[x_\lambda = \int_\lambda S(\lambda)X(\lambda)\text{d}x\]
在SampledSpectrum
中则是对离散的分量求和:
\[x_\lambda \approx \frac{\lambda_{\text{end}} - \lambda_{\text{start}}}{N}\sum^{N-1}_{i=0}{X_ic_i}\]
这样我们就可以得到SampleSpectrum
到RGBSpectrum
的转换。
RGBSpectrum
其实RGB说白了也是三个特殊的XYZ分量,其在LCD和LED上的波长分布还有所不同:
我们可以通过上述的公式将任意的XYZ色彩SampledSpectrum
转化为RGB色彩空间中的色彩RGBSpectrum
;但是反过来,一个RGB色彩可能对应于无数个XYZ色彩,于是pbrt遵循如下转换规则:
- 如果所有的RGB系数相同,则结果SPD为常量。
- 我们希望计算得到的SPD色彩的函数图像尽量的平滑。
这里pbrt参考了An RGB-to-spectrum conversion for reflectances这篇论文来实现。
Radiometry
略(因为已经通过CMU 15-462学过了)
PBR读书笔记三:Color & Radiometry
https://hyiker.github.io/2022/01/05/PBR读书笔记三:Color-Radiometry/