首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从SPD计算sRGB颜色

从SPD计算sRGB颜色
EN

Computer Graphics用户
提问于 2019-01-21 13:20:04
回答 1查看 359关注 0票数 2

我正在尝试编写我自己的光谱路径跟踪器,并将计算出来的SPD转换为LDR值。我现在所做的是使用CIE 1931颜色匹配函数将采样SPD(在W/m^-1中)转换为XYZ:

代码语言:javascript
复制
Vec3 xyz(0, 0, 0);
float norm = 0;

for (int i = 0; i < SPD_SAMPLE_COUNT; ++i) {
    const float wavelength =
        lerp(MIN_WAVELENGTH, MAX_WAVELENGTH, i / SPD_SAMPLE_COUNT);
    const Vec3 cieMatchingFunctionValue = cie1931(wavelength);
    xyz += cieMatchingFunctionValue * spd[i];
    norm += cieMatchingFunctionValue.y;
}

xyz /= norm;

在这里,cie1931的实现是来自怀曼等人的任意近似。

然后XYZ到xyY (即色度和亮度),将亮度归一化为整个图像的范围(现在,通过除以我的最大亮度来保持简单):

代码语言:javascript
复制
float maxLuminance = 0;

for (auto &xyz : image) {
    maxLuminance = std::max(maxLuminance, xyz.y);
}

for (auto &xyz : image) {
    Vec2 chromaticity = Vec2(xyz.x, xyz.y) / (xyz.x + xyz.y + xyz.z);
    float luminance = xyz.y / maxLuminance;

    // then to "scaled" XYZ and RGB, see below
}

此外,我还尝试了Reinhard音调映射操作符:

代码语言:javascript
复制
float logSum = 0;

for (auto &xyz : image) {
    logSum += std::log(xyz.y);
}

float logAvgLuminance = std::exp(logSum / image.size());

for (auto &xyz : image) {
    Vec2 chromaticity = Vec2(xyz.x, xyz.y) / (xyz.x + xyz.y + xyz.z);
    const float a = 0.18; // constant from Reinhard's paper.
    float luminance = a * xyz.y / logAvgLuminance;
    luminance /= luminance + 1;

    // then to "scaled" XYZ and RGB, see below
}

然后回到XYZ,而不是线性RGB (wia

代码语言:javascript
复制
for (auto &xyz : image) {
    // XYZ to chromaticity and "scaled luminance", see above

    Vec3 scaledXyz(
        chromaticity.x * luminance / chromaticity.y,
        luminance,
        (1 - chromaticity.x - chromaticity.y) * luminance / chromaticity.y
    );

    float r = dot(Vec3( 3.2406, -1.5372, -0.4986), scaledXyz);
    float g = dot(Vec3(-0.9689,  1.8758,  0.0415), scaledXyz);
    float b = dot(Vec3( 0.0557, -0.2040,  1.0570), scaledXyz);

    // Output RGB, i.e. std::cout << Vec3(r, g, b);
}

之后,我仍然得到RGB值。我做错了什么?似乎“最大”亮度应该取决于颜色的色度。有什么算法可以解释这一点吗?

EN

回答 1

Computer Graphics用户

回答已采纳

发布于 2019-01-27 00:06:35

当使用光谱渲染时,获得外部的sRGB值是预期的和正常的。

sRGB色域仅覆盖CIE色度空间中间的一个三角形:

(图表取自维基百科)

大的“马蹄铁”形状是所有物理上可能的色度(CIE,xy坐标)从任何可能的光谱集合。因此,您的xy坐标应该被期望在horseshoe...but中的某个位置,只有在三角形内部的坐标才能表示为sRGB!其他的对于sRGB来说“太饱和”了,并且将不得不以某种方式被夹紧或重新映射。(色调映射本身并不能解决这个问题,因为它关注的是亮度,而不是色度。)

有多种方法可以根据你想要的复杂程度来处理色域外的颜色:

  • 当然,您可以简单地夹紧sRGB值,但是这可能会改变进程中颜色的色调和/或亮度。
  • 为了同时保持色调和亮度,您可以在计算scaledXyz之前编辑xy坐标。如果初始xy坐标位于sRGB三角形之外,请查找从xy坐标到白点的线段(例如D65);然后查找该直线段与sRGB三角形的交集。这是最近的sRGB点,其亮度和色调与原始点相同,但饱和度较低。( sRGB三角形的顶点可以通过将RGB(1,0,0),RGB(0,1,0)和RGB(0,0,1)转换为CIE空间得到。
  • 或者,您也可以尝试对图像中的饱和度值进行线性或非线性的重新映射,而不是夹紧(在sRGB色域之外抛出饱和度中的任何细节),类似于亮度。这里的“饱和”是指距离白点的距离。这样做的目的是将整个图像压向白点,从而把所有的东西都带到色域内。但这可能会影响色域内颜色在视觉上不受欢迎的表面饱和度。
票数 2
EN
页面原文内容由Computer Graphics提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://computergraphics.stackexchange.com/questions/8505

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档