Reflectance spectrum editor with a Mixture of Gaussians

(The web app is available HERE)

(Edited on 2022/08/24 to reflect the addition of automatic retrieval of data from

(Edited on 2022/09/12 to add code example of how to use the exported C structure)

(Edited on 2022/11/02 to reflect the addition of visualisation of imported data)

(Edited on 2023/08/09 to reflect the addition of the export to POV-Ray)

As those following me on Twitter already know, I'm currently working on a physically based rendering engine for LibCapy. In this project, I'm using reflectance spectrum to calculate the hue of materials. In this article I introduce the web app I made to manipulate these spectrum more easily.

The reflectance spectrum of a material represents for each wavelength how much of the light is reflected when photons interact with atoms. For a given material, if all reflection levels are equal, and given a white illuminant, the material will look white. If wavelength in the blue hues are more absorbed, the material will look green+red=yellow. The measurement of reflectance spectrum is called absorption spectroscopy (absorption is just the opposite of reflectance). The measured spectrum can be described as tabulated data, which can be found of the Internet, for example here: or

However these data are difficult to obtain and manipulate. When the user needs to simply define one himself for artistic purposes, and to have a convenient way to implement a spectrum in a software, a different approach is desirable. A mixture of Gaussians is a convenient and accurate way to model the CIE standard observer as shown in this paper by Wyman, Sloan and Shirley. Following the same idea, and thinking of Gaussian as an approximation for absorption rays in spectroscopy, I decided to approximate and implement a reflectance spectrum \(S(\lambda)\) as the product of piece-wise Gaussians with 5 parameters (\(b, a, \mu, \sigma_1, \sigma_2\)): \(S(\lambda)=1-\Pi_i(1-b_i-a_iG(\lambda,\mu_i,\sigma_{i,1},\sigma_{i,2})))\) where: $$ G(\lambda,\mu,\sigma_1,\sigma_2)=\left\lbrace{ \begin{array}{l} exp(-(\lambda-\mu)*(\lambda-\mu)/(2\sigma_1^2)),\forall \lambda\le\mu\\ exp(-(\lambda-\mu)*(\lambda-\mu)/(2\sigma_2^2)),\forall \lambda\gt\mu\\ \end{array}}\right. $$

Then, I needed a tool to convert tabulated data, visualise and edit reflectance spectrum as mixture of Gaussians. Looking for such a tool, I could only find a Java applet archived on the WayBack Machine, now inaccessible, and which fits only partially my needs. So I've made my own as a web app. Why a web app ? I'm not found of web programming, but I can't object that's an easy way to make it accessible to others, which may be useful in this case, and it's good to challenge oneself sometime. No fancy framework however, all that matter to me is that it gets the job done, which it does perfectly as a single html page and vanilla javascript.

The web app looks like this:

At the top, a visualisation of wavelength from 380nm to 700nm is displayed. For each wavelength, the response of the CIE standard observer is calculated for an impulse of intensity 1 at this wavelengh and 0 everywhere else, and converted to sRGB. Superimposed is the mixture of Gaussians. In black each individual Gaussians, in white the composition of all Gaussians, and in gray the currently selected material if its display is requested. 1.0 (no absorption, or total reflectance) is at the top, 0.0 (total absorption, or no reflectance) is at the bottom.

The value of the 5 parameters for each Gaussian is displayed on the bottom left of the screen. One can edit these values by moving the sliders. A Gaussian can be removed from the mixture by clicking on the "Remove" button. A new Gaussian can be added to the mixture by clicking on the button "Add Gaussian" at the bottom of the column.

At the top of the middle column, the list of materials from the database of is automatically retrieved. Selecting one of them update the text area under the button "Fit to" with that material reflectance as CSV data. CSV data can also be edited manually in the text area. Clicking on "Fit to" convert these data to a mixture of Gaussians. Fitting may take a few seconds before completion. Upon completion the current mixture is deleted and replaced with the newly calculated one. The "Display data" check box under the text box allows to display/hide the selected material along the mixture of Gaussians.

The format of the tabulated data is: one line per wavelength; each line as "w,r" where w is the wavelength in nanometer and r the reflectance in [0,1]. The fitting algorithm uses differential evolution, with a fit function equals to \(0.0025n+(\sum_w|P(w)-D(w)|)/N\) where \(n\) is the number of Gaussian in the mixture, \(P(w)\) and \(D(w)\) are resp. the prediction and the input data for the wavelength \(w\), and \(N\) is the number of line in the data. Test on 4 materials from gives the following RME: gold 0.01, copper 0.01, silver 0.01, McBeth blue 0.05. As the fitting algorithm starts with a random seed each time differents, it will return slightly different mixtures of Gaussians at each call on the same input data.

By clicking the "Export to C" button, one can get the current mixture of Gaussians as a C structure to be used as follow.

By clicking the "Export to POV-Ray" button, one can get the current mixture of Gaussians as a POV-Ray array to be used with the macros introduced in this article. The Makefile below will install all that's needed and render an example of a sphere on a table using the "blue mattress" SPD exported with my tool.

This should produce the image SpectralRender/SpectralComposerTestSPD.png below:

To use it yourself, refer to my example script SpectralRender/testSpd.pov, the Makefile's test rule, and Ive's instructions in his article.

At the bottom right of the web app, a visualisation of the color corresponding to the current mixture of Gaussian is displayed. It is calculated by applying the mixture as described above to an illuminant of type E (constant intensity). The intensity of the illuminant is controlled by the slider at the top of the column. If the result color is out of the sRGB gamut, black is displayed instead. The sRGB values are displayed above the color.

You can run the web app on your local computer without Internet access by copying the html page and opening it locally with a web browser. However the data from won't be retrieved automatically if you have no Internet access and you will have to edit the spectrum manually yourself. The web app is distributed under the GNU General Public License. If you have any question or comment email me.

Many thanks to Pr J. Alstan Jakubiec for linking back this web app on !

in All, Computer graphics, Web app,
Copyright 2021-2024 Baillehache Pascal