Fork me on GitHub

PBR-Book Ch5 Color and Radiometry

PBR-Book Ch5 Color and Radiometry

Color and Radiometry (pbr-book.org)

In orderto precisely describe how light is represented and sampled to compute images, we must first establish some background in radiometry-- the study of the propagation of eletromagnetic radiation between approximately 380 nm and 780 nm, which account for light visible to humans. The lower wavelengths (\(\lambda \approx 400 \ \mathrm{nm}\)) are the bluish colors, the middle wavelengths (\(\lambda \approx 550 \ \mathrm {nm}\)) are the greens, and the upper wavelengths (\(\lambda \approx 650 \ \mathrm {nm}\)) are the reds.

In this chapter, we will introduce four key quantities that describe electromagnetic radiation: flux, intensity, irradiance, and radiance. These radiometric quantities are each described by their spectral power distribution (SPD)—a distribution function of wavelength that describes the amount of light at each wavelength. The Spectrum class, which is defined in Section 5.1, is used to represent SPDs in pbrt.

  • spectral power distribution 光谱功率分布
    • 描述光源在各个波长上的能量分布情况,通常用于分析光源的颜色特性

PBR-Book Ch5.1 Spectral Representation

1. Spectrum 类

文档 Spectral Representation (pbr-book.org)

代码 pbrt-v3/src/core/spectrum.h at master · mmp/pbrt-v3 (github.com)

#if defined(_MSC_VER)
#define NOMINMAX
#pragma once
#endif

#ifndef PBRT_CORE_SPECTRUM_H
#define PBRT_CORE_SPECTRUM_H

// core/spectrum.h*
#include "pbrt.h"
#include "stringprint.h"

namespace pbrt {

// Spectrum Utility Declarations
static const int sampledLambdaStart = 400;
static const int sampledLambdaEnd = 700;
static const int nSpectralSamples = 60;
extern bool SpectrumSamplesSorted(const Float *lambda, const Float *vals,
                                  int n);
extern void SortSpectrumSamples(Float *lambda, Float *vals, int n);
extern Float AverageSpectrumSamples(const Float *lambda, const Float *vals,
                                    int n, Float lambdaStart, Float lambdaEnd);
inline void XYZToRGB(const Float xyz[3], Float rgb[3]) {
    rgb[0] = 3.240479f * xyz[0] - 1.537150f * xyz[1] - 0.498535f * xyz[2];
    rgb[1] = -0.969256f * xyz[0] + 1.875991f * xyz[1] + 0.041556f * xyz[2];
    rgb[2] = 0.055648f * xyz[0] - 0.204043f * xyz[1] + 1.057311f * xyz[2];
}

inline void RGBToXYZ(const Float rgb[3], Float xyz[3]) {
    xyz[0] = 0.412453f * rgb[0] + 0.357580f * rgb[1] + 0.180423f * rgb[2];
    xyz[1] = 0.212671f * rgb[0] + 0.715160f * rgb[1] + 0.072169f * rgb[2];
    xyz[2] = 0.019334f * rgb[0] + 0.119193f * rgb[1] + 0.950227f * rgb[2];
}

enum class SpectrumType { Reflectance, Illuminant };
extern Float InterpolateSpectrumSamples(const Float *lambda, const Float *vals,
                                        int n, Float l);
extern void Blackbody(const Float *lambda, int n, Float T, Float *Le);
extern void BlackbodyNormalized(const Float *lambda, int n, Float T,
                                Float *vals);

// Spectral Data Declarations
static const int nCIESamples = 471;
extern const Float CIE_X[nCIESamples];
extern const Float CIE_Y[nCIESamples];
extern const Float CIE_Z[nCIESamples];
extern const Float CIE_lambda[nCIESamples];
static const Float CIE_Y_integral = 106.856895;
static const int nRGB2SpectSamples = 32;
extern const Float RGB2SpectLambda[nRGB2SpectSamples];
extern const Float RGBRefl2SpectWhite[nRGB2SpectSamples];
extern const Float RGBRefl2SpectCyan[nRGB2SpectSamples];
extern const Float RGBRefl2SpectMagenta[nRGB2SpectSamples];
extern const Float RGBRefl2SpectYellow[nRGB2SpectSamples];
extern const Float RGBRefl2SpectRed[nRGB2SpectSamples];
extern const Float RGBRefl2SpectGreen[nRGB2SpectSamples];
extern const Float RGBRefl2SpectBlue[nRGB2SpectSamples];
extern const Float RGBIllum2SpectWhite[nRGB2SpectSamples];
extern const Float RGBIllum2SpectCyan[nRGB2SpectSamples];
extern const Float RGBIllum2SpectMagenta[nRGB2SpectSamples];
extern const Float RGBIllum2SpectYellow[nRGB2SpectSamples];
extern const Float RGBIllum2SpectRed[nRGB2SpectSamples];
extern const Float RGBIllum2SpectGreen[nRGB2SpectSamples];
extern const Float RGBIllum2SpectBlue[nRGB2SpectSamples];

// Spectrum Declarations
template <int nSpectrumSamples>
class CoefficientSpectrum {
  public:
    // CoefficientSpectrum Public Methods
    CoefficientSpectrum(Float v = 0.f) {
        for (int i = 0; i < nSpectrumSamples; ++i) c[i] = v;
        DCHECK(!HasNaNs());
    }
#ifdef DEBUG
    CoefficientSpectrum(const CoefficientSpectrum &s) {
        DCHECK(!s.HasNaNs());
        for (int i = 0; i < nSpectrumSamples; ++i) c[i] = s.c[i];
    }
    CoefficientSpectrum &operator=(const CoefficientSpectrum &s) {
        DCHECK(!s.HasNaNs());
        for (int i = 0; i < nSpectrumSamples; ++i) c[i] = s.c[i];
        return *this;
    }
#endif  // DEBUG
    void Print(FILE *f) const {
        fprintf(f, "[ ");
        for (int i = 0; i < nSpectrumSamples; ++i) {
            fprintf(f, "%f", c[i]);
            if (i != nSpectrumSamples - 1) fprintf(f, ", ");
        }
        fprintf(f, "]");
    }
    CoefficientSpectrum &operator+=(const CoefficientSpectrum &s2) {
        DCHECK(!s2.HasNaNs());
        for (int i = 0; i < nSpectrumSamples; ++i) c[i] += s2.c[i];
        return *this;
    }
    CoefficientSpectrum operator+(const CoefficientSpectrum &s2) const {
        DCHECK(!s2.HasNaNs());
        CoefficientSpectrum ret = *this;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] += s2.c[i];
        return ret;
    }
    CoefficientSpectrum operator-(const CoefficientSpectrum &s2) const {
        DCHECK(!s2.HasNaNs());
        CoefficientSpectrum ret = *this;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] -= s2.c[i];
        return ret;
    }
    CoefficientSpectrum operator/(const CoefficientSpectrum &s2) const {
        DCHECK(!s2.HasNaNs());
        CoefficientSpectrum ret = *this;
        for (int i = 0; i < nSpectrumSamples; ++i) {
          CHECK_NE(s2.c[i], 0);
          ret.c[i] /= s2.c[i];
        }
        return ret;
    }
    CoefficientSpectrum operator*(const CoefficientSpectrum &sp) const {
        DCHECK(!sp.HasNaNs());
        CoefficientSpectrum ret = *this;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] *= sp.c[i];
        return ret;
    }
    CoefficientSpectrum &operator*=(const CoefficientSpectrum &sp) {
        DCHECK(!sp.HasNaNs());
        for (int i = 0; i < nSpectrumSamples; ++i) c[i] *= sp.c[i];
        return *this;
    }
    CoefficientSpectrum operator*(Float a) const {
        CoefficientSpectrum ret = *this;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] *= a;
        DCHECK(!ret.HasNaNs());
        return ret;
    }
    CoefficientSpectrum &operator*=(Float a) {
        for (int i = 0; i < nSpectrumSamples; ++i) c[i] *= a;
        DCHECK(!HasNaNs());
        return *this;
    }
    friend inline CoefficientSpectrum operator*(Float a,
                                                const CoefficientSpectrum &s) {
        DCHECK(!std::isnan(a) && !s.HasNaNs());
        return s * a;
    }
    CoefficientSpectrum operator/(Float a) const {
        CHECK_NE(a, 0);
        DCHECK(!std::isnan(a));
        CoefficientSpectrum ret = *this;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] /= a;
        DCHECK(!ret.HasNaNs());
        return ret;
    }
    CoefficientSpectrum &operator/=(Float a) {
        CHECK_NE(a, 0);
        DCHECK(!std::isnan(a));
        for (int i = 0; i < nSpectrumSamples; ++i) c[i] /= a;
        return *this;
    }
    bool operator==(const CoefficientSpectrum &sp) const {
        for (int i = 0; i < nSpectrumSamples; ++i)
            if (c[i] != sp.c[i]) return false;
        return true;
    }
    bool operator!=(const CoefficientSpectrum &sp) const {
        return !(*this == sp);
    }
    bool IsBlack() const {
        for (int i = 0; i < nSpectrumSamples; ++i)
            if (c[i] != 0.) return false;
        return true;
    }
    friend CoefficientSpectrum Sqrt(const CoefficientSpectrum &s) {
        CoefficientSpectrum ret;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] = std::sqrt(s.c[i]);
        DCHECK(!ret.HasNaNs());
        return ret;
    }
    template <int n>
    friend inline CoefficientSpectrum<n> Pow(const CoefficientSpectrum<n> &s,
                                             Float e);
    CoefficientSpectrum operator-() const {
        CoefficientSpectrum ret;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] = -c[i];
        return ret;
    }
    friend CoefficientSpectrum Exp(const CoefficientSpectrum &s) {
        CoefficientSpectrum ret;
        for (int i = 0; i < nSpectrumSamples; ++i) ret.c[i] = std::exp(s.c[i]);
        DCHECK(!ret.HasNaNs());
        return ret;
    }
    friend std::ostream &operator<<(std::ostream &os,
                                    const CoefficientSpectrum &s) {
        return os << s.ToString();
    }
    std::string ToString() const {
        std::string str = "[ ";
        for (int i = 0; i < nSpectrumSamples; ++i) {
            str += StringPrintf("%f", c[i]);
            if (i + 1 < nSpectrumSamples) str += ", ";
        }
        str += " ]";
        return str;
    }
    CoefficientSpectrum Clamp(Float low = 0, Float high = Infinity) const {
        CoefficientSpectrum ret;
        for (int i = 0; i < nSpectrumSamples; ++i)
            ret.c[i] = pbrt::Clamp(c[i], low, high);
        DCHECK(!ret.HasNaNs());
        return ret;
    }
    Float MaxComponentValue() const {
        Float m = c[0];
        for (int i = 1; i < nSpectrumSamples; ++i)
            m = std::max(m, c[i]);
        return m;
    }
    bool HasNaNs() const {
        for (int i = 0; i < nSpectrumSamples; ++i)
            if (std::isnan(c[i])) return true;
        return false;
    }
    bool Write(FILE *f) const {
        for (int i = 0; i < nSpectrumSamples; ++i)
            if (fprintf(f, "%f ", c[i]) < 0) return false;
        return true;
    }
    bool Read(FILE *f) {
        for (int i = 0; i < nSpectrumSamples; ++i) {
            double v;
            if (fscanf(f, "%lf ", &v) != 1) return false;
            c[i] = v;
        }
        return true;
    }
    Float &operator[](int i) {
        DCHECK(i >= 0 && i < nSpectrumSamples);
        return c[i];
    }
    Float operator[](int i) const {
        DCHECK(i >= 0 && i < nSpectrumSamples);
        return c[i];
    }
    // CoefficientSpectrum Public Data
    static const int nSamples = nSpectrumSamples;

  protected:
    // CoefficientSpectrum Protected Data
    Float c[nSpectrumSamples];
};
    
    ...
// 代码就 copy 到这里,后面的去 github 上看就可以

以下是 ChatGPT 对代码的解读

CoefficientSpectrum 类是光谱表示的一种通用形式,用于处理光谱数据在渲染或计算中的表现。它可以被用来表示不同波长范围内的光谱信息,例如在渲染中表示 反射率 Reflectance光照强度 Illuminant

1.1 数据成员变量

只有一个变量,就是 protected: Float c[nSpectrumSamples]

这个数组存储了光谱的实际采样值,数组的大小由模板参数 nSpectrumSamples 决定

CoefficientSpectrum 类中的值是浮点数 (Float 类型),这些值通常表示光谱在不同波长上的采样值。具体的数值范围可以是 0 到 1,也可以超出这个范围,具体取决于光谱的物理意义和使用场景。

常见的数值范围和用途:

  1. 0 到 1
    • 这种范围常用于表示归一化的光谱数据。例如,颜色的反射率通常在 0 到 1 之间,表示光在特定波长上的反射比例。
  2. 0 到更大的数值
    • 在表示亮度或光强度时,光谱值可能超出 1。例如,亮度可以使用任意正数来表示,特别是在高动态范围(HDR)渲染中。
  3. 负值
    • 在大多数情况下,光谱值不应该为负数,因为负数没有物理意义(例如,负的反射率)。不过,类中确实有对负值的支持(如重载的 operator-),这在某些计算过程中可能会用到,但通常会在后续步骤中进行修正或裁剪。

具体的数值范围如何确定?

  • 反射率 (Reflectance) 或透射率 (Transmittance)
    • 通常在 0 到 1 之间。
  • 光强度 (Illuminance)
    • 可能是 0 到正无穷(例如,HDR 场景中非常亮的光源)。
  • 其他场景
    • 根据具体的应用场景,光谱数据的范围可能需要适应不同的需求。

1.2 类的主要特点和功能

  1. 模板参数 nSpectrumSamples

    • 这是一个模板类,其中 nSpectrumSamples 决定了 光谱的采样点数量。例如,在某些渲染系统中,我们可能需要对光谱进行多个点的采样,这个类允许我们指定这些点的数量。
  2. 构造函数

    • CoefficientSpectrum(Float v = 0.f)
      • 通过这个构造函数可以初始化光谱中的所有采样点为同一个值 v,默认为 0
  3. 重载操作符

    • 这个类重载了各种数学操作符,如 +, -, *, / 等,用于进行光谱间的加减乘除操作
    • 像标量一样计算方便
  4. 光谱操作

    • operator+=, operator-=, operator*= 等允许对当前光谱进行原地修改
    • operator*= 允许标量与光谱相乘,结果是每个光谱采样点都乘以这个标量。
  5. 判断和检验

    • HasNaNs()
      • 检查这个光谱中是否有非数字 Nan 的值
    • IsBlack()
      • 判断光谱是否为全黑即所有采样点都为 0
    • MaxComponentValue()
      • 返回光谱中最大采样点的值
  6. 其它功能

    • Clamp(Float low = 0, Float high = Ifinity)
      • 用于将光谱中所有的值限制在 lowhigh 之间
    • ToString()
      • 将光谱转换为字符串表示
    • WriteRead 函数用于将光谱写入文件或从文件读取
  7. 输入输出

    • 通过 Print(FILE *f) 将光谱的数据以特定格式打印到文件

    • operator<< 重载用于将光谱对象流输出到标准输出或其它输出流

未完待续...

posted @ 2024-09-02 20:08  icewalnut  阅读(7)  评论(0编辑  收藏  举报