Docs: Which material textures must be `SRGBColorSpace`?

These are the maps in MeshPhysicalMaterial:

map
aoMap
envMap
bumpMap
alphaMap
lightMap
normalMap
emissiveMap
clearcoatMap
roughnessMap
thicknessMap
metalnessMap
anisotropyMap
sheenColorMap
iridescenceMap
displacementMap
transmissionMap
specularColorMap
sheenRoughnessMap
clearcoatNormalMap
specularIntensityMap
clearcoatRoughnessMap
iridescenceThicknessMap

Which of these need to be in SRGBColorSpace?

Which of them should be LinearSRGBColorSpace?

Which of them should be NoColorSpace?

The “Color Spaces” section of Core Constants mentions four of those maps are NoColorSpace:

  • normalMap
  • roughnessMap
  • metalnessMap
  • ambientOcclusionMap

and other “data” textures (more may be inferred).

It then mentions to look at “Color management” (the mention is not linked), which mentions two more maps that need SRGBColorSpace:

  • map
  • emissiveMap

and two more that need LinearSRGBColorSpace:

  • sometimes envMap
  • sometimes lightMap

The list is spread across two pages and not comprehensive. That’s 6, maybe 8, maps that we know the color space for.

When do envMap and lightMap not need LinearSRGBColorSpace? How about the rest?

I would be great is MeshPhysicalMaterial (and other materials) applied the correct color spaces by default, so that users don’t have to memorize all of them.

Either that, or the docs could have a definitive list of color spaces for all maps (default behavior would be nicer).

1 Like

So far I came up with this:

export const defaultColorSpaces = {
	map: SRGBColorSpace, // ref: Color Management
	aoMap: NoColorSpace, // ref: Core Constants
	envMap: LinearSRGBColorSpace, // ref: Color Management
	bumpMap: NoColorSpace,
	alphaMap: NoColorSpace,
	lightMap: LinearSRGBColorSpace, // ref: Color Management
	normalMap: NoColorSpace, // ref: Core Constants
	emissiveMap: SRGBColorSpace, // ref: Color Management
	clearcoatMap: NoColorSpace,
	roughnessMap: NoColorSpace, // ref: Core Constants
	thicknessMap: NoColorSpace,
	metalnessMap: NoColorSpace, // ref: Core Constants
	anisotropyMap: NoColorSpace,
	sheenColorMap: SRGBColorSpace,
	iridescenceMap: NoColorSpace,
	displacementMap: NoColorSpace,
	transmissionMap: NoColorSpace,
	specularColorMap: SRGBColorSpace,
	sheenRoughnessMap: NoColorSpace,
	clearcoatNormalMap: NoColorSpace,
	specularIntensityMap: NoColorSpace,
	clearcoatRoughnessMap: NoColorSpace,
	iridescenceThicknessMap: NoColorSpace,
}

Is this correct?

1 Like

The only real requirement is that .colorSpace should tell three.js how the texture was exported. For example: it is entirely possible to export a .map or .emissiveMap texture using Linear-sRGB. Unusual, yes, but as long as you assign .colorSpace accordingly, that’s fine!

But I think all of the material slot vs. color space mappings you listed do reflect the most common choices, and so if you’re not sure how the textures were exported, that’s a great starting point.

Personally, I think of it like this:

  • non-color data: always NoColorspace
  • closed domain [0–1] color: typically SRGBColorSpace
  • open domain [0–infinity] color: typically LinearSRGBColorSpace

That framing helps when thinking about color slots that can reasonably exceed [0–1], like .envMap, .lightMap, and .emissiveMap. If the texture’s format can represent values >1 (.exr, .hdr) then it’s very likely LinearSRGBColorSpace. If the texture format is limited to [0–1] (PNG, JPG) then it’s very likely SRGBColorSpace. But again, these are just heuristics to guess how the texture was likely exported.

6 Likes

That’s interesting. Thanks.

What I recall is that before the r151/r152 update that requires people manually specify .colorSpace for every color texture, things were just easier for people getting into 3D with Three.js.

This easiness is reduced now, as colors aren’t exactly as good anymore without setting .colorSpace manually IIRC.

Could it be worth having common color spaces be the default inside of Three.js to avoid having to always define them (makes Three.js easier to use out of the box), and let the experts opt into non-default values as they see fit?

Attempted and discussed in Material: Infer texture color space (WIP) by donmccurdy · Pull Request #25857 · mrdoob/three.js · GitHub. But I don’t really think there’s a path forward there, especially considering node-based materials and perhaps wide-gamut color in the future, which we cannot detect. It is necessary to keep track of which textures are used for color vs. for data and to set .colorSpace accordingly. I do wish the web platform let us get color space metadata from the textures directly — which might help — but even then, the metadata is wildly unreliable.

You can turn all color management off, if you want to return to pre-r152 rendering, but that’s both technically and artistically worse for most use cases, in my opinion.

2 Likes