Thoughts on making Cycles into a spectral renderer

As you can see here, blue light is far from a single wavelength. This is the spectrum used currently in spectral Cycles:

This sort of calibration is like a ‘rule of thumb’ where it might get you close enough to the correct value for some cases (but even that seems unlikely with this method), but is by no means accurate or useful in the context of spectral rendering.

1 Like

If you dont mind,i would like to see some examples.

Where does this come from?From a other spectralengine?

I do not know what color upsampling technique most spectral renderers are actually using.
Mitsuba 2 is using the paper that @smilebags eventually intends to implement (as I understand)
I don’t know what that paper was called again but it’s burried within this thread a few times by now.
Neatly, he did just give you what the take in Spectral Cycles currently looks like.

Meanwhile, I expanded the Additive / Multiplicative Color chart real quick:

Top Row: using RGB
Bottom Row: using reconstructed Spectra)
Left two are additive (they work like light sources)
Right two are multiplicative (they work like reflected light)

To be clear, both rows are rendered in the same scene at the same time in the Spectral branch. The difference is, that on top I only used color inputs (yellow sockets) whereas on the bottom it’s spectral (red) sockets all the way through.

The center of the multiplicative Spectral RGB triangle is actually brown, not neutral grey. Which is a direct result of this spectral overlap. But additively you get grey, just as you’d expect.

You can also see, that the additive behavior of RGB Colors and Spectra is identical, but multiplicative there are big differences. (The RGB version becomes much darker as the colors are treated as if they were fully saturated when, actually, they are not)

Note: I opted to normalize these such that the “amount of color” you use for each adds up to 1, so in the additive case, in the center, it should amount to RGB 1/3 1/3 1/3, not 1 1 1 - or for the CMY one, it’d be 2/3 2/3 2/3 which is why that one is brighter here.
In the multiplicative case, it’s not so straight forward to predict which colors would appear where since, unlike the additive case, multiplying spectra is gonna crucially and fundamentally depend on what those spectra are. But effectively, the center of the multiplicative RGB triangle would be what happens if you multiply the spectra of R, G, B, and then take the third root: (R*G*B)^(1/3)
This is equivalent to using watercolors or inks where you dilute each color such that it absorbs three times less than normally, and then add the three diluted colors together. If you poured them together undiluted, it’d result in something darker.
If you ever worked with watercolors and just randomly mixed just about every color in there together, you’d find that the mixture tends towards a weird off brown. So this seems pretty accurate to me.

If you want to play around with it, here’s the blend file:

The top row should work whether you render spectrally or not. So it should be possible to compare that row between regular and spectral Cycles. The bottom row is gonna cause issues without spectral rendering.

I noticed there was some bug with the color sockets though. I’m not sure why, but sometimes one of the colors would go through wrong when using Separate RGB or Separate XYZ (and the other one would work) - it’s not specific to one of those separate nodes. One time one worked, another time the other. Definitely something weird going on there. @pembem22 @smilebags

2 Likes

This comes from some work that @Scott_Burns kindly did for us a few years ago.

2 Likes

this is specifically the chart that has a 10nm resolution, right? The actual curve you use now, iirc, goes down to nm resolution iirc? (Via the python-ported scripts that are also burried in here somewhere. Dang, sometimes I feel like we need a second thread that’s literally just resources like that, no other talk allowed, to keep track of it all haha)

1 Like

Yep, this was just showing that the blue used for sRGB 0,0,1 isn’t a narrow spectrum, the exact data we’re using is marginally different

The development of those curves can be found here: http://scottburns.us/fast-rgb-to-spectrum-conversion-for-reflectances/

and the Python routines for reflectance reconstruction are here: http://scottburns.us/matlab-octave-and-python-source-code-for-refl-recon-chrom-adapt/

(I prefer “reflectance reconstruction” over “upsampling” because that’s not really what we’re doing!)

5 Likes

It’s worth highlighting that there is a long history of massaging spectral curves from RGB! Think of this approach as a hack to bridge the gap between RGB work and spectral; without it, absolutely everything would need to be authored spectrally, which isn’t viable of course.

More recently, Hanika and Jakob, Otsu, and many others. The lowest gradient fit has been around for a while I believe, somewhat going all the way back to the sadly deceased Brian Smits work at Pixar around 1999!

So again, it isn’t like the reconstructed (thanks Burnsie!) spectra have any real basis in reality, but are engineered to be “Sorta kinda close ish and Good Enough” that fit the needs of bridging between RGB and spectra.

2 Likes

The “smoothest” or “lowest gradient” curve idea goes back to van Trigt in two papers published in 1990.

3 Likes

Just did a test regarding bounce light.
Here is the sun lamp settings (UI might be a little different because I have Photographer 3 addon):
image
Here is how I temporarily fix the pinkish tint:


Here is the actual test:

It seems Spectral version is darker? Why is that?

Would be great if the original OCIO configuration can be restored to properly address the achromatic.

Okay here is the result of the previous build that does not have filmic:


A new build is available on GraphicAll. Changes:

  • Added more operations to the “Spectrum Math” node.
  • Added new “Map Range Spectrum” node.
    image
  • Added “Ray Wavelength” output to the “Light Path” node. It outputs wavelengths of the current ray in nanometers.
  • Fixed incorrect OCIO config that resulted in a red tint.
  • Fixed spectral Nishita sky texture not outputting anything.
  • OSL rendering is now supported in RGB mode.
9 Likes

I suspect Ray Wavelength as a Light Path output is precisely what Brecht wanted to not give a user access to?
It’ll be fun to play with but I suspect it’ll break a bunch of assumptions…

(That said, this was kind of already possible with spectral curves so maybe this isn’t a big deal?)

Either way, thanks, looking forward to giving it a go!

I don’t know whether this by itself will cause any problems without also having a wavelength to float conversion available since it still only allows you to operate on the entire spectrum as a whole. Let’s see, if it causes a bunch of bugs, it’ll be clear we need to avoid it.

1 Like

As far as I can tell, the lightpath wavelength input is a convenient shortcut for this construction

Whether I use Light Path or this multiply idea, I end up with the same outcomes. So if this light path thing is going to be problematic, so will all this spectrum math. So here’s hoping it’s not…

image

(This is from the thin film shader, still not finished or reworked with the new capabilities, but now the previously scalar cosine is a spectral one instead)

Here’s the same but with the output squared so I don’t get negative spectra: image

And the momentarily reworked node tree (still gotta integrate all the Complex IOR stuff)

2 Likes

That’s exactly my reasoning, a more compact version of the already possible setup. As @smilebags already said, all the spectral values are isolated and can only be plugged in into inputs that explicitly accept those values. Node link becomes red if a shader output is connected to non-shader input:
image
The same can be done for the spectral outputs to visually indicate that. So, in theory, this should be fine. If it causes problems then there are issues in the overall concept of manipulating spectral data.

1 Like

Here is the result with latest GraphicAll build:


Interesting that your spectral outcome is so much darker

Is it supposed to be darker?