blender/intern/cycles/app/cie_d65_luminance_fit.py
Weizhen Huang f15daf81bf Cycles: Add dispersion to Principled BSDF
Dispersion is defined by two parameters:
Abbe Number and Dispersion Scale.

This corresponds to OpenPBR v1.1.1

Pull Request: https://projects.blender.org/blender/blender/pulls/162041

Note: dispersion is temporarily disabled when using MNEE (shadow caustics) on oneAPI, due to a compiler bug. Waiting for a fix from the oneAPI side.

![prism.png](/attachments/219c3966-b39d-4846-991b-976f84f83081)
Co-authored-by: Sebastian Herholz <sebastian.herholz@gmail.com>
2026-08-18 11:03:49 +02:00

205 lines
13 KiB
Python

# SPDX-FileCopyrightText: 2026 Blender Foundation
#
# SPDX-License-Identifier: Apache-2.0
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
# Spectral power distribution of D65, wavelengths from 380 to 780 nm, 1 nm wavelength steps.
# https://cie.co.at/datatable/cie-standard-illuminant-d65
cie_d65_spd = [
49.9755, 50.4428, 50.91, 51.3773, 51.8446, 52.3118, 52.7791, 53.2464, 53.7137, 54.1809,
54.6482, 57.4589, 60.2695, 63.0802, 65.8909, 68.7015, 71.5122, 74.3229, 77.1336, 79.9442,
82.7549, 83.628, 84.5011, 85.3742, 86.2473, 87.1204, 87.9936, 88.8667, 89.7398, 90.6129,
91.486, 91.6806, 91.8752, 92.0697, 92.2643, 92.4589, 92.6535, 92.8481, 93.0426, 93.2372,
93.4318, 92.7568, 92.0819, 91.4069, 90.732, 90.057, 89.3821, 88.7071, 88.0322, 87.3572,
86.6823, 88.5006, 90.3188, 92.1371, 93.9554, 95.7736, 97.5919, 99.4102, 101.228, 103.047,
104.865, 106.079, 107.294, 108.508, 109.722, 110.936, 112.151, 113.365, 114.579, 115.794,
117.008, 117.088, 117.169, 117.249, 117.33, 117.41, 117.49, 117.571, 117.651, 117.732,
117.812, 117.517, 117.222, 116.927, 116.632, 116.336, 116.041, 115.746, 115.451, 115.156,
114.861, 114.967, 115.073, 115.18, 115.286, 115.392, 115.498, 115.604, 115.711, 115.817,
115.923, 115.212, 114.501, 113.789, 113.078, 112.367, 111.656, 110.945, 110.233, 109.522,
108.811, 108.865, 108.92, 108.974, 109.028, 109.082, 109.137, 109.191, 109.245, 109.3,
109.354, 109.199, 109.044, 108.888, 108.733, 108.578, 108.423, 108.268, 108.112, 107.957,
107.802, 107.501, 107.2, 106.898, 106.597, 106.296, 105.995, 105.694, 105.392, 105.091,
104.79, 105.08, 105.37, 105.66, 105.95, 106.239, 106.529, 106.819, 107.109, 107.399,
107.689, 107.361, 107.032, 106.704, 106.375, 106.047, 105.719, 105.39, 105.062, 104.733,
104.405, 104.369, 104.333, 104.297, 104.261, 104.225, 104.19, 104.154, 104.118, 104.082,
104.046, 103.641, 103.237, 102.832, 102.428, 102.023, 101.618, 101.214, 100.809, 100.405,
100, 99.6334, 99.2668, 98.9003, 98.5337, 98.1671, 97.8005, 97.4339, 97.0674, 96.7008,
96.3342, 96.2796, 96.225, 96.1703, 96.1157, 96.0611, 96.0065, 95.9519, 95.8972, 95.8426,
95.788, 95.0778, 94.3675, 93.6573, 92.947, 92.2368, 91.5266, 90.8163, 90.1061, 89.3958,
88.6856, 88.8177, 88.9497, 89.0818, 89.2138, 89.3459, 89.478, 89.61, 89.7421, 89.8741,
90.0062, 89.9655, 89.9248, 89.8841, 89.8434, 89.8026, 89.7619, 89.7212, 89.6805, 89.6398,
89.5991, 89.4091, 89.219, 89.029, 88.8389, 88.6489, 88.4589, 88.2688, 88.0788, 87.8887,
87.6987, 87.2577, 86.8167, 86.3757, 85.9347, 85.4936, 85.0526, 84.6116, 84.1706, 83.7296,
83.2886, 83.3297, 83.3707, 83.4118, 83.4528, 83.4939, 83.535, 83.576, 83.6171, 83.6581,
83.6992, 83.332, 82.9647, 82.5975, 82.2302, 81.863, 81.4958, 81.1285, 80.7613, 80.394,
80.0268, 80.0456, 80.0644, 80.0831, 80.1019, 80.1207, 80.1395, 80.1583, 80.177, 80.1958,
80.2146, 80.4209, 80.6272, 80.8336, 81.0399, 81.2462, 81.4525, 81.6588, 81.8652, 82.0715,
82.2778, 81.8784, 81.4791, 81.0797, 80.6804, 80.281, 79.8816, 79.4823, 79.0829, 78.6836,
78.2842, 77.4279, 76.5716, 75.7153, 74.859, 74.0027, 73.1465, 72.2902, 71.4339, 70.5776,
69.7213, 69.9101, 70.0989, 70.2876, 70.4764, 70.6652, 70.854, 71.0428, 71.2315, 71.4203,
71.6091, 71.8831, 72.1571, 72.4311, 72.7051, 72.979, 73.253, 73.527, 73.801, 74.075,
74.349, 73.0745, 71.8, 70.5255, 69.251, 67.9765, 66.702, 65.4275, 64.153, 62.8785,
61.604, 62.4322, 63.2603, 64.0885, 64.9166, 65.7448, 66.573, 67.4011, 68.2293, 69.0574,
69.8856, 70.4057, 70.9259, 71.446, 71.9662, 72.4863, 73.0064, 73.5266, 74.0467, 74.5669,
75.087, 73.9376, 72.7881, 71.6387, 70.4893, 69.3398, 68.1904, 67.041, 65.8916, 64.7421,
63.5927, 61.8752, 60.1578, 58.4403, 56.7229, 55.0054, 53.288, 51.5705, 49.8531, 48.1356,
46.4182, 48.4569, 50.4956, 52.5344, 54.5731, 56.6118, 58.6505, 60.6892, 62.728, 64.7667,
66.8054, 66.4631, 66.1209, 65.7786, 65.4364, 65.0941, 64.7518, 64.4096, 64.0673, 63.7251,
63.3828
]
# CIE 1931 colour-matching functions, 2 degree observer
# y for wavelengths from 380 to 780 nm, 1 nm wavelength steps.
# https://cie.co.at/datatable/cie-1931-colour-matching-functions-2-degree-observer
cie_y = [
0.0000390000000, 0.0000428264000, 0.0000469146000, 0.0000515896000, 0.0000571764000,
0.0000640000000, 0.0000723442100, 0.0000822122400, 0.0000935081600, 0.0001061361000,
0.0001200000000, 0.0001349840000, 0.0001514920000, 0.0001702080000, 0.0001918160000,
0.0002170000000, 0.0002469067000, 0.0002812400000, 0.0003185200000, 0.0003572667000,
0.0003960000000, 0.0004337147000, 0.0004730240000, 0.0005178760000, 0.0005722187000,
0.0006400000000, 0.0007245600000, 0.0008255000000, 0.0009411600000, 0.0010698800000,
0.0012100000000, 0.0013620910000, 0.0015307520000, 0.0017203680000, 0.0019353230000,
0.0021800000000, 0.0024548000000, 0.0027640000000, 0.0031178000000, 0.0035264000000,
0.0040000000000, 0.0045462400000, 0.0051593200000, 0.0058292800000, 0.0065461600000,
0.0073000000000, 0.0080865070000, 0.0089087200000, 0.0097676800000, 0.0106644300000,
0.0116000000000, 0.0125731700000, 0.0135827200000, 0.0146296800000, 0.0157150900000,
0.0168400000000, 0.0180073600000, 0.0192144800000, 0.0204539200000, 0.0217182400000,
0.0230000000000, 0.0242946100000, 0.0256102400000, 0.0269585700000, 0.0283512500000,
0.0298000000000, 0.0313108300000, 0.0328836800000, 0.0345211200000, 0.0362257100000,
0.0380000000000, 0.0398466700000, 0.0417680000000, 0.0437660000000, 0.0458426700000,
0.0480000000000, 0.0502436800000, 0.0525730400000, 0.0549805600000, 0.0574587200000,
0.0600000000000, 0.0626019700000, 0.0652775200000, 0.0680420800000, 0.0709110900000,
0.0739000000000, 0.0770160000000, 0.0802664000000, 0.0836668000000, 0.0872328000000,
0.0909800000000, 0.0949175500000, 0.0990458400000, 0.1033674000000, 0.1078846000000,
0.1126000000000, 0.1175320000000, 0.1226744000000, 0.1279928000000, 0.1334528000000,
0.1390200000000, 0.1446764000000, 0.1504693000000, 0.1564619000000, 0.1627177000000,
0.1693000000000, 0.1762431000000, 0.1835581000000, 0.1912735000000, 0.1994180000000,
0.2080200000000, 0.2171199000000, 0.2267345000000, 0.2368571000000, 0.2474812000000,
0.2586000000000, 0.2701849000000, 0.2822939000000, 0.2950505000000, 0.3085780000000,
0.3230000000000, 0.3384021000000, 0.3546858000000, 0.3716986000000, 0.3892875000000,
0.4073000000000, 0.4256299000000, 0.4443096000000, 0.4633944000000, 0.4829395000000,
0.5030000000000, 0.5235693000000, 0.5445120000000, 0.5656900000000, 0.5869653000000,
0.6082000000000, 0.6293456000000, 0.6503068000000, 0.6708752000000, 0.6908424000000,
0.7100000000000, 0.7281852000000, 0.7454636000000, 0.7619694000000, 0.7778368000000,
0.7932000000000, 0.8081104000000, 0.8224962000000, 0.8363068000000, 0.8494916000000,
0.8620000000000, 0.8738108000000, 0.8849624000000, 0.8954936000000, 0.9054432000000,
0.9148501000000, 0.9237348000000, 0.9320924000000, 0.9399226000000, 0.9472252000000,
0.9540000000000, 0.9602561000000, 0.9660074000000, 0.9712606000000, 0.9760225000000,
0.9803000000000, 0.9840924000000, 0.9874182000000, 0.9903128000000, 0.9928116000000,
0.9949501000000, 0.9967108000000, 0.9980983000000, 0.9991120000000, 0.9997482000000,
1.0000000000000, 0.9998567000000, 0.9993046000000, 0.9983255000000, 0.9968987000000,
0.9950000000000, 0.9926005000000, 0.9897426000000, 0.9864444000000, 0.9827241000000,
0.9786000000000, 0.9740837000000, 0.9691712000000, 0.9638568000000, 0.9581349000000,
0.9520000000000, 0.9454504000000, 0.9384992000000, 0.9311628000000, 0.9234576000000,
0.9154000000000, 0.9070064000000, 0.8982772000000, 0.8892048000000, 0.8797816000000,
0.8700000000000, 0.8598613000000, 0.8493920000000, 0.8386220000000, 0.8275813000000,
0.8163000000000, 0.8047947000000, 0.7930820000000, 0.7811920000000, 0.7691547000000,
0.7570000000000, 0.7447541000000, 0.7324224000000, 0.7200036000000, 0.7074965000000,
0.6949000000000, 0.6822192000000, 0.6694716000000, 0.6566744000000, 0.6438448000000,
0.6310000000000, 0.6181555000000, 0.6053144000000, 0.5924756000000, 0.5796379000000,
0.5668000000000, 0.5539611000000, 0.5411372000000, 0.5283528000000, 0.5156323000000,
0.5030000000000, 0.4904688000000, 0.4780304000000, 0.4656776000000, 0.4534032000000,
0.4412000000000, 0.4290800000000, 0.4170360000000, 0.4050320000000, 0.3930320000000,
0.3810000000000, 0.3689184000000, 0.3568272000000, 0.3447768000000, 0.3328176000000,
0.3210000000000, 0.3093381000000, 0.2978504000000, 0.2865936000000, 0.2756245000000,
0.2650000000000, 0.2547632000000, 0.2448896000000, 0.2353344000000, 0.2260528000000,
0.2170000000000, 0.2081616000000, 0.1995488000000, 0.1911552000000, 0.1829744000000,
0.1750000000000, 0.1672235000000, 0.1596464000000, 0.1522776000000, 0.1451259000000,
0.1382000000000, 0.1315003000000, 0.1250248000000, 0.1187792000000, 0.1127691000000,
0.1070000000000, 0.1014762000000, 0.0961886400000, 0.0911229600000, 0.0862648500000,
0.0816000000000, 0.0771206400000, 0.0728255200000, 0.0687100800000, 0.0647697600000,
0.0610000000000, 0.0573962100000, 0.0539550400000, 0.0506737600000, 0.0475496500000,
0.0445800000000, 0.0417587200000, 0.0390849600000, 0.0365638400000, 0.0342004800000,
0.0320000000000, 0.0299626100000, 0.0280766400000, 0.0263293600000, 0.0247080500000,
0.0232000000000, 0.0218007700000, 0.0205011200000, 0.0192810800000, 0.0181206900000,
0.0170000000000, 0.0159037900000, 0.0148371800000, 0.0138106800000, 0.0128347800000,
0.0119200000000, 0.0110683100000, 0.0102733900000, 0.0095333110000, 0.0088461570000,
0.0082100000000, 0.0076237810000, 0.0070854240000, 0.0065914760000, 0.0061384850000,
0.0057230000000, 0.0053430590000, 0.0049957960000, 0.0046764040000, 0.0043800750000,
0.0041020000000, 0.0038384530000, 0.0035890990000, 0.0033542190000, 0.0031340930000,
0.0029290000000, 0.0027381390000, 0.0025598760000, 0.0023932440000, 0.0022372750000,
0.0020910000000, 0.0019535870000, 0.0018245800000, 0.0017035800000, 0.0015901870000,
0.0014840000000, 0.0013844960000, 0.0012912680000, 0.0012040920000, 0.0011227440000,
0.0010470000000, 0.0009765896000, 0.0009111088000, 0.0008501332000, 0.0007932384000,
0.0007400000000, 0.0006900827000, 0.0006433100000, 0.0005994960000, 0.0005584547000,
0.0005200000000, 0.0004839136000, 0.0004500528000, 0.0004183452000, 0.0003887184000,
0.0003611000000, 0.0003353835000, 0.0003114404000, 0.0002891656000, 0.0002684539000,
0.0002492000000, 0.0002313019000, 0.0002146856000, 0.0001992884000, 0.0001850475000,
0.0001719000000, 0.0001597781000, 0.0001486044000, 0.0001383016000, 0.0001287925000,
0.0001200000000, 0.0001118595000, 0.0001043224000, 0.0000973356000, 0.0000908458700,
0.0000848000000, 0.0000791466700, 0.0000738580000, 0.0000689160000, 0.0000643026700,
0.0000600000000, 0.0000559818700, 0.0000522256000, 0.0000487184000, 0.0000454474700,
0.0000424000000, 0.0000395610400, 0.0000369151200, 0.0000344486800, 0.0000321481600,
0.0000300000000, 0.0000279912500, 0.0000261135600, 0.0000243602400, 0.0000227246100,
0.0000212000000, 0.0000197785500, 0.0000184528500, 0.0000172168700, 0.0000160645900,
0.0000149900000
]
# Wavelengths in um
x = [x * 0.001 for x in range(380, 781)]
def sigmoid(x, a, x0):
return 1 / (1 + np.exp(-a * (x - x0)))
def normalized_sigmoid(x, a, x0):
y = sigmoid(x, a, x0)
# Normalization factor so that the function evaluates to 0 at 380nm and 1 at 780nm
y0 = sigmoid(0.38, a, x0)
N = sigmoid(0.78, a, x0) - y0
return (y - y0) / N
# Compute luminance
sum = 0.0
luminance = list(range(380, 781))
for i in range(0, 401):
# Sampling purely based on luminance would give high weights to green wavelengths,
# which leads to color noise. We want a bit of all the wavelengths, so add some constant.
# The choice of 0.25 is kind of random
luminance[i] = (cie_y[i] + 0.25) * cie_d65_spd[i]
sum += luminance[i]
# Compute cdf
partial_sum = 0.0
luminance_cdf = list(range(380, 781))
for i in range(0, 401):
partial_sum += luminance[i]
luminance_cdf[i] = partial_sum / sum
# Initial guess
p0 = [40, 0.58]
# Fit cdf to a sigmoid
popt, pcov = curve_fit(normalized_sigmoid, x, luminance_cdf, p0, method='lm')
# Print fitted result
a = popt[0]
x0 = popt[1]
y0 = sigmoid(0.38, a, x0)
N = sigmoid(0.78, a, x0) - y0
print(a, x0, y0, N)
# Plot original function and fitted function
plt.figure(figsize=(10, 6))
label_tex = rf"$F(x) = \frac{{\frac{{1}}{{1 + e^{{-{a:.5f}(x - {x0:.5f})}}}} - {y0:.5f}}}{{{N:.5f}}}$"
plt.plot(x, normalized_sigmoid(x, *popt), label=label_tex, linestyle='--')
plt.plot(x, luminance_cdf, label='Luminance cdf')
# Labels and legend
plt.xlabel('Wavelength/um')
plt.title('Fit luminance cdf to a normalized sigmoid')
plt.legend(fontsize=20)
plt.grid(True)
plt.tight_layout()
plt.show()