You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I found a problem when trying to verify my own solution to this that (at least) affects materials that uses spectral data to describe reflectance/transmittance properties. The problem is the following function:
static RGBSpectrum FromSampled(const Float *lambda, const Float *v, int 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;
returnFromXYZ(xyz);
}
Which corresponds to the Riemann sum of:
X = ∫(S(λ)·x(λ)·dλ) / ∫(y(λ)·dλ)
Y = ∫(S(λ)·y(λ)·dλ) / ∫(y(λ)·dλ)
Z = ∫(S(λ)·z(λ)·dλ) / ∫(y(λ)·dλ)
The problem for the reflectance case is that the reflectance S(λ) has to attenuate some illuminant I(λ) for these values to make sense, and the normalization factor should also take this into account:
X = ∫(S(λ)·I(λ)·x(λ)·dλ) / ∫(y(λ)·I(λ)·dλ)
Y = ∫(S(λ)·I(λ)·y(λ)·dλ) / ∫(y(λ)·I(λ)·dλ)
Z = ∫(S(λ)·I(λ)·z(λ)·dλ) / ∫(y(λ)·I(λ)·dλ)
The code implicitly uses a constant equal energy illuminant I(λ)=C that is eliminated, which is fine except that the function used to then transform the resulting XYZ tristimulus values to sRGB assumes XYZD65:
This shifts the RGB values to more reddish hues in places where SPD's are used to describe reflectance, for example when specifying eta and k for conductive materials. The following scene uses spectral data to specify eta for usual soda-lime glass:
(FrConductor generalizes to dielectrics for k=0, so this is supposed to represent the specular reflectance part of glass)
This can be fixed by either using the D65 illuminant as I(λ) when applying the CIE CMF's in the reflectance case, or by simply multiplying the XYZ values with the D65 white point before applying the transformation to sRGB:
static RGBSpectrum FromSampled(const Float *lambda, const Float *v, int n,
SpectrumType type = SpectrumType::Illuminant) {
...
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;
if (type == SpectrumType::Reflectance) {
xyz[0] *= 0.95047f;
xyz[2] *= 1.08883f;
}
returnFromXYZ(xyz);
}
Note that this requires an additional SpectrumType argument that differentiates between reflectance and illuminant spectrums.
The text was updated successfully, but these errors were encountered:
linusmossberg
changed the title
Incorrect illuminant when sampling spectral reflectance data to sRGB
Incorrect illuminant when integrating spectral reflectance data to sRGB
Jul 8, 2020
I found a problem when trying to verify my own solution to this that (at least) affects materials that uses spectral data to describe reflectance/transmittance properties. The problem is the following function:
Which corresponds to the Riemann sum of:
The problem for the reflectance case is that the reflectance
S(λ)
has to attenuate some illuminantI(λ)
for these values to make sense, and the normalization factor should also take this into account:The code implicitly uses a constant equal energy illuminant
I(λ)=C
that is eliminated, which is fine except that the function used to then transform the resulting XYZ tristimulus values to sRGB assumes XYZD65:This shifts the RGB values to more reddish hues in places where SPD's are used to describe reflectance, for example when specifying
eta
andk
for conductive materials. The following scene uses spectral data to specifyeta
for usual soda-lime glass:(
FrConductor
generalizes to dielectrics fork=0
, so this is supposed to represent the specular reflectance part of glass)This can be fixed by either using the D65 illuminant as
I(λ)
when applying the CIE CMF's in the reflectance case, or by simply multiplying the XYZ values with the D65 white point before applying the transformation to sRGB:Note that this requires an additional
SpectrumType
argument that differentiates between reflectance and illuminant spectrums.The text was updated successfully, but these errors were encountered: