博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PBRT笔记(4)——颜色和辐射度
阅读量:6331 次
发布时间:2019-06-22

本文共 5086 字,大约阅读时间需要 16 分钟。

SPD 光谱功率分布

CoefficientSpectrum

根据给定采样数表示光谱,为RGBSpectrum、SampledSpectrum的父类。

重载大量的基础代码,比较简单不做赘述。其中为了方便访问对应区域的SPD,而重载了[]操作符。(TabulatedBSSRDF等会用到)

该类只要以各种基础运算函数、重载各种操作符为主,以及一个Float c[nSpectrumSamples];用于保存SPD信息。

SampledSpectrum

SampledSpectrum则将光谱表达为波长范围上的采样点集合。(通过采样求得光谱系数)

人眼对400~700纳米波长的光最敏感,通常采样量为30即可准确地表示渲染时的SPD

static SampledSpectrum FromSampled(const Float *lambda, const Float *v,int n) {        //如果处于无序状态,则在排序后在返回结果         spectrum        if (!SpectrumSamplesSorted(lambda, v, n)) {            std::vector
slambda(&lambda[0], &lambda[n]); std::vector
sv(&v[0], &v[n]); //SortSpectrumSamples:讲sLambda与sv放入一个map中,使用sort进行排序后在放回对应的Vector //这里出现了一个骚操作,因为Vector存储的数据是紧挨着存储的和数组一样,所以可以直接用float*取地址进行操作 SortSpectrumSamples(&slambda[0], &sv[0], n); return FromSampled(&slambda[0], &sv[0], n); } SampledSpectrum r; for (int i = 0; i < nSpectralSamples; ++i) { //计算该采样区域的平均值 Float lambda0 = Lerp(Float(i) / Float(nSpectralSamples), sampledLambdaStart, sampledLambdaEnd); Float lambda1 = Lerp(Float(i + 1) / Float(nSpectralSamples), sampledLambdaStart, sampledLambdaEnd); r.c[i] = AverageSpectrumSamples(lambda, v, n, lambda0, lambda1); } return r; }
Float AverageSpectrumSamples(const Float *lambda, const Float *vals, int n,                             Float lambdaStart, Float lambdaEnd) {    for (int i = 0; i < n - 1; ++i) CHECK_GT(lambda[i + 1], lambda[i]);    CHECK_LT(lambdaStart, lambdaEnd);    //处理越界以及单一采样的情况    if (lambdaEnd <= lambda[0]) return vals[0];    if (lambdaStart >= lambda[n - 1]) return vals[n - 1];    if (n == 1) return vals[0];    Float sum = 0;    //如果头尾采样都在区间内,则将其加入结果中    if (lambdaStart < lambda[0]) sum += vals[0] * (lambda[0] - lambdaStart);    if (lambdaEnd > lambda[n - 1])        sum += vals[n - 1] * (lambdaEnd - lambda[n - 1]);    //移动到对应的区间    int i = 0;    while (lambdaStart > lambda[i + 1]) ++i;    CHECK_LT(i + 1, n);    //遍历各个区间,通过插值计算平均值,最终加到结果中    auto interp = [lambda, vals](Float w, int i) {        return Lerp((w - lambda[i]) / (lambda[i + 1] - lambda[i]), vals[i],                    vals[i + 1]);    };    for (; i + 1 < n && lambdaEnd >= lambda[i]; ++i) {        Float segLambdaStart = std::max(lambdaStart, lambda[i]);        Float segLambdaEnd = std::min(lambdaEnd, lambda[i + 1]);        sum += 0.5 * (interp(segLambdaStart, i) + interp(segLambdaEnd, i)) *               (segLambdaEnd - segLambdaStart);    }    return sum / (lambdaEnd - lambdaStart);}

针对任意SPD转化为$ X_\lambda Y_\lambda Z _\lambda$的计算,PRBT通过三条曲线进行适配。

所有的Spectrum都必须提供这个方法。在渲染图片的一个像素时,一束携带光谱信息的光线射入摄像机中的胶片,第一步胶片会将SPD转化为xyz系数,再经过一系列处理,最终将其转化为可以显示的RGB值。

RGB系数根据基于SPD相应曲线积分计算获得。对于既定曲线,乘积积分可通过预计算实现,并将全转换表示为一个矩阵。

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];}void ToRGB(Float rgb[3]) const {    Float xyz[3];    ToXYZ(xyz);    XYZToRGB(xyz, rgb);}

RGBSpectrum

讲光谱系数转化为RGB值(先转成x、y、z,再转成RGB值)

static RGBSpectrum FromSampled(const Float *lambda, const Float *v, int n) {    if (!SpectrumSamplesSorted(lambda, v, n)) {        std::vector
slambda(&lambda[0], &lambda[n]); std::vector
sv(&v[0], &v[n]); SortSpectrumSamples(&slambda[0], &sv[0], n); return FromSampled(&slambda[0], &sv[0], n); } Float xyz[3] = {0, 0, 0}; for (int i = 0; i < nCIESamples; ++i) { Float val = InterpolateSpectrumSamples(lambda, v, n, CIE_lambda[i]); xyz[0] += val * CIE_X[i]; xyz[1] += val * CIE_Y[i]; xyz[2] += val * CIE_Z[i]; } Float scale = Float(CIE_lambda[nCIESamples - 1] - CIE_lambda[0]) / Float(CIE_Y_integral * nCIESamples); xyz[0] *= scale; xyz[1] *= scale; xyz[2] *= scale; return FromXYZ(xyz);}Float InterpolateSpectrumSamples(const Float *lambda, const Float *vals, int n, Float l) { for (int i = 0; i < n - 1; ++i) CHECK_GT(lambda[i + 1], lambda[i]); if (l <= lambda[0]) return vals[0]; if (l >= lambda[n - 1]) return vals[n - 1]; int offset = FindInterval(n, [&](int index) { return lambda[index] <= l; }); CHECK(l >= lambda[offset] && l <= lambda[offset + 1]); Float t = (l - lambda[offset]) / (lambda[offset + 1] - lambda[offset]); return Lerp(t, vals[offset], vals[offset + 1]);}

因为这里的代码都要后面几章才会用到,看得不太明白,待看到后面几章后再补充。

剩下的辐射度部分比较简单(稍微介绍了一下brdf、btdf、bsdf、bssrdf,而且和第二版是一样的),而且知乎上已经有一些比较好的解释了,不做赘述。不过我依然建议去看原文。

转载于:https://www.cnblogs.com/blueroses/p/9961009.html

你可能感兴趣的文章
阿里云容器Kubernetes监控(二) - 使用Grafana展现Pod监控数据
查看>>
区块链应用 | 不知道什么时候起,满世界都在谈区块链的事情
查看>>
小程序爆红 专家:对简单APP是巨大打击
查看>>
FarBox--另类有趣的网站服务【转】
查看>>
在非纯色背景上,叠加背景透明的BUTTON和STATIC_TEXT控件
查看>>
Distributed2:Linked Server Login 添加和删除
查看>>
海量数据处理相关面试问题
查看>>
Python-time
查看>>
Java中取两位小数
查看>>
RTX发送消息提醒实现以及注意事项
查看>>
使用 ftrace 调试 Linux 内核【转】
查看>>
唯一聚集索引上的唯一和非唯一非聚集索引
查看>>
Spark新愿景:让深度学习变得更加易于使用——见https://github.com/yahoo/TensorFlowOnSpark...
查看>>
linux磁盘配额
查看>>
NFS文件共享服务器的搭建
查看>>
%r 和 %s 该用哪个?
查看>>
小公司职场不是“切糕”
查看>>
play工程部署到云服务器
查看>>
ListView 取消点击效果
查看>>
降级论
查看>>