If you set your Base Color (or Diffuse Color) directly in ACEScg, it is important to be aware that there should be some limits in terms of saturation and luminance. One may ask : what would be a proper limit for the Diffuse Color ?
Here is my personal answer : the Pointer’s gamut ! Before we go deep into its definition, let’s step back a bit and have a proper look at the albedo.
After reading Chapter 1 (about Color Management) and Chapter 1.5 (about ACES), you may ask yourself : where do I start ? How do I set correctly all these values ? Sometimes we struggle to balance correctly the look development of our assets between texturing, lighting or grading. For example, how strong should be our lights in a turntable ? Or how bright and saturated should our textures be ?
I personally consider the albedo color to be a “stable” reference. Since albedo comes from “real life“, it is only natural to consider it as a proper way to balance our assets.
From the Substance PBR guide : The visible color of a surface is due to the wavelengths emitted by the light source. These wavelengths are absorbed by the object and reflected both specularly and diffusely. The remaining reflected wavelengths are what we see as color.
From Jeremy Selan : In a real scene, if you measure luminance values with a tool such as a spectroradiometer, one can observe a very wide range of values in a single scene. […] Very dark materials (such as charcoal) reflect a small fraction of incoming light, often in the 3-5% range. As a single number, this overall reflectivity is called “albedo”.
Albedo and reflections
We just got two great definitions from Substance and Jeremy Selan. And I think it is really worth it to pause a bit and think about what the albedo really is. Because there are so many misconceptions about it.
From Wikipedia : Surface albedo is defined as the ratio of radiosity to the irradiance (flux per unit area) received by a surface. […] albedo is the directional integration of reflectance over all solar angles in a given period.Great article about this as well: Everything is shiny.
What do all these definitions tell us ? That you get some specular/glossiness information embedded in the albedo value. That is very important.
Let’s take charcoal as an example. Charcoal not being a pure lambertian surface, you will get some “specular” reflection at grazing angles. For artistic control in CG, we generally split diffuse and specular reflections but in real life they are just really the same.Great explanation by Thomas Mansencal.
Diffuse Color or Albedo AOV ?
In real life albedo includes both diffuse reflection and specular reflection. That’s the first thing we need to clear out. But many render engines, like Guerilla Render, have simplified this process : the albedo AOV is simply the Diffuse Color (also called Base Color or Diffuse Reflection). Arnold, on the other hand, seems to have done the “real” thing :
The fresnel in the diffuse_albedo is a result of the Specular IOR and how it affects the albedo of the diffuse to make it energy efficient. So with no Specular, there would be no fresnel on the diffuse_albedo, as all the energy would be in the diffuse.From the Arnold documentation.
I found this last part particularly interesting. The misconception about the albedo I was talking earlier may come from these different behaviors. This is particularly critical for our next paragraph.
Now it is important to keep in mind that “ground truth” is somehow vague : albedo in the first place is a measure bound to solar irradiance which encompasses both the diffuse and specular components of the reflection when it is measured. Textures are pretty much never shot in direct sunlight. Ground truth would be more about spectrally measure not only the BTF (Bidirectional Texture Function) of the surface but also measure its Polarimetric Reflectance : a Spatially Varying Polarimetric BRDF (Bidirectional Reflectance Distribution Function). This will get an almost perfect representation of the surface but by doing so you’ll also loose all your artistic controls over it. Otherwise you can check the vrscans (the VRScans are non-spectral BTF) or Quixel Megascans.Thomas Mansencal is very much aware of albedo-related challenges.
Which Albedo limit ?
If it was not clear enough, I clearly split the Color Selection in two categories :
- Lights and emissive surfaces -> Rec.2020 or ACEScg gamuts.
- Albedo and specular colors of non-emissive surfaces -> Pointer’s gamut.
To have a saturated color, the spectral distribution must be narrow-band. The laser, i.e. a line, being the most saturated in the world. It’s the opposite of surfaces that are pretty smooth. So they cannot be extremely saturated by the very nature of their spectral distribution of reflection.Another perfect explanation by Thomas Mansencal.
Hence the two categories :
- With a light source, the spectrum is very changing with spikes.
- With a natural or man made surface, the spectrum is very smooth.
The problem you may face, however, is when you light a surface with a light narrow band. You find yourself in a situation where even if indeed your surface is smooth it reflects something narrow-band. So you can end up with a super saturated surface (it often happens in concert for example). And sometimes (often) it doesn’t go as we would like, e.g. blue highlights fix.I just love Thomas’ concert example. So visual !
Finally I had to ask him about fluorescence :
Fluorescence is actually considered emission. So it is not limited like a non-emissive surface would be. Fluorescence is simply a re-emission at a different wavelength. We even talk about Optical Brightener in laundry.I love when all the dots start to connect like this.
Studios have provided different answers to this issue : how do we limit the albedo range to a PBR one ? Here is a couple of solutions I have seen :
- A technical check scanning albedo textures and stopping the publish if some out-of-range values are found in them.
- A soft clip limit directly in the shader to fit the range of any color input.
- A visual check of the Albedo AOV for out-of-range values (meaning the Albedo AOV is correctly set).
All these solutions are okay. Even if most of the time they only take in account the luminance of the maps. But what about saturation ? And this is where the Pointer’s Gamut comes handy !
Interestingly enough Thomas Mansencal made a plea for Colour Analysis tools… In 2014 !I am only six years late.
I have been wondering for a while if there was any study on the Diffuse Color/Reflection and saturation. Until I found out about the Pointer’s Gamut. Most of my data comes from this great article.
First of all, a very basic question that has bothered many people. Why is it called Pointer‘s ? It is actually very simple ! In 1980, a scientist named Michael Pointer took over 4000 references to study their colors and came with a Gamut named after him.
The Pointer’s gamut is (an approximation of) the gamut of real surface colors as can be seen by the human eye, based on the research by Michael R. Pointer (1980). […] What this means is that every color that can be reflected by the surface of an object of any material is inside the Pointer’s gamut.
This totally sounds like a legit solution. But to what should we compare the Pointer’s Gamut ? The answer is given to us in the same article (just read it) :
Pointer’s gamut is defined for diffuse reflection (matte surface). As opposed to diffuse reflection there is specular reflection, or mirror like reflection. By specular reflection objects can reflect colors that are outside the Pointer’s gamut.It could not be any clearer. Really.
The Pointer’s Gamut is a study done for Kodak originally and the list of the 4089 samples used is not available. However, you may find the dataset on the RIT website.
A technical check based on Pointer’s Gamut
We could definitely think about developing an application allowing us to compare our base color textures to the Pointer’s Gamut. A few studios have already developed some solutions internally and an open-source software would be more than welcome for the community.
A few recommendations about this Pointer’s Gamut check :
- What is important is to prevent the majority of cases.
- The Pointer Gamut is not exhaustive, it does not represent all the possible reflectances.
- Pointer did not measure all actual surfaces. He has a quite large representative sample.
- We have to apply all of this wisely and filter 98% of problematic cases.
- It shouldn’t become a brake on creativity, on the contrary it should help it.
- The system is not made to stop people from doing their job, but to help them do it better and faster.
MrLixm managed to develop a free app checking if your textures are within the Pointer’s Gamut or not.Congrats Liam !
Pointer’s Gamut example values
I’ll share here a few values here so you get a rough idea if they’re inside the Pointer’s Gamut or not. But let’s not be too dogmatic neither, since everything is “wrong” in digital RGB scene rendering…
|Colorspace||Outside Pointer’s Gamut||Inside Pointer’s Gamut||Comments|
|Linear – sRGB||(1, 0, 0)||(0.9, 0.03, 0.03) – (0.9042, 0.0278, 0.0098)||This reminds me of the values used in my coke can render. Cool !|
|Linear – sRGB||(0, 1, 0)||(0.03, 0.5, 0.03) – (0.4618, 0.8201, 0.4270)||The sRGB green primary is “corrected” to enter PG.|
|Linear – sRGB||(0, 0, 1)||(0, 0.06, 0.6) – (0.0641, 0.0225, 0.5637)||The sRGB blue primary is “corrected” to enter PG.|
|Linear – sRGB||(0, 0, 0.6)||(0.05, 0.05, 0.6)|
|Linear – sRGB||(0, 0, 0)||(0.0191, 0.0186, 0.0229)||It almost matches the charcoal reference (see chart below). Sweet !|
|Linear – sRGB||(1, 1, 1)||(0.7624, 0.7645, 0.7494)||It almost matches the fresh snow reference (see chart below). Awesome !|
|ACEScg||(1, 0, 0)||(0.6552, 0.1353, 0.0494)||Do not forget that ACEScg primaries are outside of the Spectral Locus !|
|ACEScg||(0, 1, 0)||(0.4985, 0.7630, 0.4471)||Do not forget that ACEScg primaries are outside of the Spectral Locus !|
|ACEScg||(0, 0, 1)||(0.0527, 0.0246, 0.4228)||Do not forget that ACEScg primaries are outside of the Spectral Locus !|
|ACEScg||(1, 1, 0)||(0.8473, 0.7838, 0.0741)|
|ACEScg||(0, 1, 1)||(0.5891, 0.7720, 0.8745)|
|ACEScg||(1,0,1)||(0.5827, 0.1991, 0.6153)|
Albedo charts and their limits
We now have a clear target for our Diffuse Reflection. We may translate this into a technical check or even an albedo chart.
There are some interesting albedo charts out there even from different render engines, like Unity and Unreal. Since these values have been obtained from real-world measured values, they are pretty good guidelines. It is also interesting to notice that charcoal and snow are generally the “extreme” examples in most albedo charts.
It seems to me that nobody has worked on them like Sebastien Lagarde from Unity. In 2013, Sebastien was already talking about their use and their limits.
I sometimes see shaders with the 0 value in albedo. I agree that the 0 value can be an optimization since there is no value nor bsdf to evaluate. But keep in mind that the 0 value would likely stop the light path in a RGB render engine.
CG Albedo charts
My process to generate these charts was pretty simple. I merged one chart posted on ACEScentral, one from Sebastien Lagarde and the macbeth color checker into one. Since most of these charts have sRGB values between 0 and 255, I used this converter to normalize them. I also had to dig a bit to find the macbeth values in sRGB.
This whole process, similar to the asset conversion shown here, can be simplified by importing the charts into Nuke and directly pick the values in the viewer.
There are now two charts : one with ACEScg values and one with Linear – sRGB values. The luminance values comes from Nuke’s viewer. I have ordered the albedo values by luminance as I thought it would be more convenient. The charts themselves have been written as png files with the following “colorspace” : “Utility – sRGB – Texture”.
You may want to use these values with “a pinch of salt”, since we don’t know exactly if they are strictly about diffuse reflectance or albedo. From the render tests I have been doing about charcoal, here are some more accurate values (which match the ones from Unreal Engine) :
|Diffuse Reflectance||Specular Reflectance||Total Albedo|
Same thing with the “Fresh snow”. Some charts indicate an albedo value of 0.9, some 0.81. I went for the lowest value since I was more aiming at a diffuse color value chart. Which leads us to our final question…
Macbeth Chart Values
Are the macbeth chart values diffuse albedo or total albedo ? A fairly long time ago a colleague took material reference photos of items using cross-polarisation and, of course, the macbeth chart has a specular component. […] But maybe we should treat the online macbeth chart (from colour-science) as diffuse and anyone referencing a photo with a chart they would need to compensate for the specular component ?
I cannot answer any better than Thomas Mansencal, so I’ll just quote him.
From a measurement and unit standpoint albedo is meant to represent diffuse reflection (of solar irradiance). The problem is as much as you can separate them when you model a BRDF, it is really hard to do so in real life when taking measurements. In a way, specular reflection is almost always a subset of diffuse reflection, it is reflection after all. When the reflectance of a chart is measured, it is done with a precise geometry, e.g. 0:45 (light aligned to the tangent, receiver/sensor at 45deg) and what is measured is the total reflection of the incident light. Because the light irradiance is known, you can then deduce the reflectance of the surface.
Having never done any real-life measurements myself, I’ll just sit and listen. Here’s the final part :
What this tells you is that there is no specular vs diffuse reflection separation, it is all encompassing. It also tells you that if you are expecting to achieve a match, you should be matching the measurement geometry in the render engine because it is the only geometry where the match can be fully achieved.
Even though the Pointer’s Gamut is 40 years old, it is still a valid reference for albedo values in shaders. But we should certainly not become a slave of it and use it as a guideline to give consistency between assets. As usual, these choices depend on your art direction and workflow. Recently, Björn Ottosson, a software engineer tweeted about the Pointer’s Gamut and generated these two images.
Shows what the brightest and darkest natural surface reflectance colors are (using sRGB HSV). If you are making diffuse textures, you want to stay in this range most of the time. Brighter and darker reflectance colors are physically possible, but will be quite rare. This is a good sanity check rather than strict limit. Also fluorescent colors can appear brighter.
This concept of limiting the range of textures values can generate quite some debate. But I think it is useful to take these decisions keeping in mind these notions. And we’ll finish with Thomas’ quote :
Well, everything is contextual, anchoring your values in the real world, as a starting point, is often the key to greater quality, this is to a degree the basis of PBR. Keep in mind that Pointer’s Gamut is about reflectance only […]. It is about Digital Asset authoring, specifically Look Development of shaders and textures where more often than never, one needs to strive for physical accuracy. […] This is not about image quality but physical correctness. Like any tools, it must be used with understanding. PBR validation tools […] arguably help improving Lookdev consistency and overall image quality.
A possible improvement of this post could be to generate two charts : one for Diffuse Reflectance and one for Specular Reflectance. Sounds like some interesting homework for the future !
- Substance PBR guide.
- Cinematic Color.
- Albedo definition on Wikipedia.
- Everything is shiny.
- The Arnold documentation.
- A plea for colour analysis tools in DCC applications.
- Great article on the Pointer’s Gamut.
- Useful data on the RIT website.
- Pointer’s Gamut Checker Tool.
- DONTNOD Physically based rendering chart for Unreal Engine 4.
- Sebastien’s Lagarde albedo chart.
- Iri Shinsoj Albedo Chart.
- Feeding a physically based shading model.
- Björn Ottosson’s posts.