Color-management.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <base href="../../../" />
  6. <script src="page.js"></script>
  7. <link type="text/css" rel="stylesheet" href="page.css" />
  8. <style>
  9. blockquote {
  10. font-size: 0.8em;
  11. line-height: 1.5em;
  12. margin-left: 0;
  13. border-left: 4px solid #cccccc;
  14. padding: 1em 2em 1em 2em;
  15. }
  16. blockquote p:first-child {
  17. margin-top: 0;
  18. }
  19. blockquote p:last-child {
  20. margin-bottom: 0;
  21. }
  22. figure {
  23. width: 100%;
  24. margin: 1em 0;
  25. font-style: italic;
  26. }
  27. figure img {
  28. width: 100%;
  29. }
  30. figure.float {
  31. float: right;
  32. max-width: 30%;
  33. margin: 1em;
  34. }
  35. @media all and ( max-width: 640px ) {
  36. figure.float {
  37. float: none;
  38. max-width: 100%;
  39. }
  40. }
  41. </style>
  42. </head>
  43. <body>
  44. <h1>[name]</h1>
  45. <h2>What is a color space?</h2>
  46. <p>
  47. Every color space is a collection of several design decisions, chosen together to support a
  48. large range of colors while satisfying technical constraints related to precision and display
  49. technologies. When creating a 3D asset, or assembling 3D assets together into a scene, it is
  50. important to know what these properties are, and how the properties of one color space relate
  51. to other color spaces in the scene.
  52. </p>
  53. <figure class="float">
  54. <img src="resources/srgb_gamut.png" alt="">
  55. <figcaption>
  56. sRGB colors and white point (D65) displayed in the reference CIE 1931 chromaticity
  57. diagram. Colored region represents a 2D projection of the sRGB gamut, which is a 3D
  58. volume. Source: <a href="https://en.wikipedia.org/wiki/SRGB" target="_blank" rel="noopener">Wikipedia</a>
  59. </figcaption>
  60. </figure>
  61. <ul>
  62. <li>
  63. <b>Color primaries:</b> Primary colors (e.g. red, green, blue) are not absolutes; they are
  64. selected from the visible spectrum based on constraints of limited precision and
  65. capabilities of available display devices. Colors are expressed as a ratio of the primary colors.
  66. </li>
  67. <li>
  68. <b>White point:</b> Most color spaces are engineered such that an equally weighted sum of
  69. primaries <i>R = G = B</i> will appear to be without color, or "achromatic". The appearance
  70. of achromatic values (like white or grey) depend on human perception, which in turn depends
  71. heavily on the context of the observer. A color space specifies its "white point" to balance
  72. these needs. The white point defined by the sRGB color space is
  73. [link:https://en.wikipedia.org/wiki/Illuminant_D65 D65].
  74. </li>
  75. <li>
  76. <b>Transfer functions:</b> After choosing the color gamut and a color model, we still need to
  77. define mappings ("transfer functions") of numerical values to/from the color space. Does <i>r = 0.5</i>
  78. represent 50% less physical illumination than <i>r = 1.0</i>? Or 50% less bright, as perceived
  79. by an average human eye? These are different things, and that difference can be represented as
  80. a mathematical function. Transfer functions may be <i>linear</i> or <i>nonlinear</i>, depending
  81. on the objectives of the color space. sRGB defines nonlinear transfer functions. Those
  82. functions are sometimes approximated as <i>gamma functions</i>, but the term "gamma" is
  83. ambiguous and should be avoided in this context.
  84. </li>
  85. </ul>
  86. These three parameters — color primaries, white point, and transfer functions — define a color
  87. space, with each chosen for particular goals. Having defined the parameters, a few additional terms
  88. are helpful:
  89. <ul>
  90. <li>
  91. <b>Color model:</b> Syntax for numerically identifying colors within chosen the color gamut —
  92. a coordinate system for colors. In three.js we're mainly concerned with the RGB color
  93. model, having three coordinates <i>r, g, b ∈ [0,1]</i> ("closed domain") or
  94. <i>r, g, b ∈ [0,∞]</i> ("open domain") each representing a fraction of a primary
  95. color. Other color models (HSL, Lab, LCH) are commonly used for artistic control.
  96. </li>
  97. <li>
  98. <b>Color gamut:</b> Once color primaries and a white point have been chosen, these represent
  99. a volume within the visible spectrum (a "gamut"). Colors not within this volume ("out of gamut")
  100. cannot be expressed by closed domain [0,1] RGB values. In the open domain [0,∞], the gamut is
  101. technically infinite.
  102. </li>
  103. </ul>
  104. <p>
  105. Consider two very common color spaces: [page:SRGBColorSpace] ("sRGB") and
  106. [page:LinearSRGBColorSpace] ("Linear-sRGB"). Both use the same primaries and white point,
  107. and therefore have the same color gamut. Both use the RGB color model. They differ only in
  108. the transfer functions — Linear-sRGB is linear with respect to physical light intensity.
  109. sRGB uses the nonlinear sRGB transfer functions, and more closely resembles the way that
  110. the human eye perceives light and the responsiveness of common display devices.
  111. </p>
  112. <p>
  113. That difference is important. Lighting calculations and other rendering operations must
  114. generally occur in a linear color space. However, a linear colors are less efficient to
  115. store in an image or framebuffer, and do not look correct when viewed by a human observer.
  116. As a result, input textures and the final rendered image will generally use the nonlinear
  117. sRGB color space.
  118. </p>
  119. <blockquote>
  120. <p>
  121. ℹ️ <i><b>NOTICE:</b> While some modern displays support wider gamuts like Display-P3,
  122. the web platform's graphics APIs largely rely on sRGB. Applications using three.js
  123. today will typically use only the sRGB and Linear-sRGB color spaces.</i>
  124. </p>
  125. </blockquote>
  126. <h2>Roles of color spaces</h2>
  127. <p>
  128. Linear workflows — required for modern rendering methods — generally involve more than
  129. one color space, each assigned to a particular role. Linear and nonlinear color spaces are
  130. appropriate for different roles, explained below.
  131. </p>
  132. <h3>Input color space</h3>
  133. <p>
  134. Colors supplied to three.js — from color pickers, textures, 3D models, and other sources —
  135. each have an associated color space. Those not already in the Linear-sRGB working color
  136. space must be converted, and textures be given the correct <i>texture.colorSpace</i> assignment.
  137. Certain conversions (for hexadecimal and CSS colors in sRGB) can be made automatically if
  138. the THREE.ColorManagement API is enabled before initializing colors:
  139. </p>
  140. <code>
  141. THREE.ColorManagement.enabled = true;
  142. </code>
  143. <ul>
  144. <li>
  145. <b>Materials, lights, and shaders:</b> Colors in materials, lights, and shaders store
  146. RGB components in the Linear-sRGB working color space.
  147. </li>
  148. <li>
  149. <b>Vertex colors:</b> [page:BufferAttribute BufferAttributes] store RGB components in the
  150. Linear-sRGB working color space.
  151. </li>
  152. <li>
  153. <b>Color textures:</b> PNG or JPEG [page:Texture Textures] containing color information
  154. (like .map or .emissiveMap) use the closed domain sRGB color space, and must be annotated with
  155. <i>texture.colorSpace = SRGBColorSpace</i>. Formats like OpenEXR (sometimes used for .envMap or
  156. .lightMap) use the Linear-sRGB color space indicated with <i>texture.colorSpace = LinearSRGBColorSpace</i>,
  157. and may contain values in the open domain [0,∞].
  158. </li>
  159. <li>
  160. <b>Non-color textures:</b> Textures that do not store color information (like .normalMap
  161. or .roughnessMap) do not have an associated color space, and generally use the (default) texture
  162. annotation of <i>texture.colorSpace = NoColorSpace</i>. In rare cases, non-color data
  163. may be represented with other nonlinear encodings for technical reasons.
  164. </li>
  165. </ul>
  166. <blockquote>
  167. <p>
  168. ⚠️ <i><b>WARNING:</b> Many formats for 3D models do not correctly or consistently
  169. define color space information. While three.js attempts to handle most cases, problems
  170. are common with older file formats. For best results, use glTF 2.0 ([page:GLTFLoader])
  171. and test 3D models in online viewers early to confirm the asset itself is correct.</i>
  172. </p>
  173. </blockquote>
  174. <h3>Working color space</h3>
  175. <p>
  176. Rendering, interpolation, and many other operations must be performed in an open domain
  177. linear working color space, in which RGB components are proportional to physical
  178. illumination. In three.js, the working color space is Linear-sRGB.
  179. </p>
  180. <h3>Output color space</h3>
  181. <p>
  182. Output to a display device, image, or video may involve conversion from the open domain
  183. Linear-sRGB working color space to another color space. This conversion may be performed in
  184. the main render pass ([page:WebGLRenderer.outputColorSpace]), or during post-processing.
  185. </p>
  186. <code>
  187. renderer.outputColorSpace = THREE.SRGBColorSpace; // optional with post-processing
  188. </code>
  189. <ul>
  190. <li>
  191. <b>Display:</b> Colors written to a WebGL canvas for display should be in the sRGB
  192. color space.
  193. </li>
  194. <li>
  195. <b>Image:</b> Colors written to an image should use the color space appropriate for
  196. the format and usage. Fully-rendered images written to PNG or JPEG textures generally
  197. use the sRGB color space. Images containing emission, light maps, or other data not
  198. confined to the [0,1] range will generally use the open domain Linear-sRGB color space,
  199. and a compatible image format like OpenEXR.
  200. </li>
  201. </ul>
  202. <blockquote>
  203. <p>
  204. ⚠️ <i><b>WARNING:</b> Render targets may use either sRGB or Linear-sRGB. sRGB makes
  205. better use of limited precision. In the closed domain, 8 bits often suffice for sRGB
  206. whereas ≥12 bits (half float) may be required for Linear-sRGB. If later pipeline
  207. stages require Linear-sRGB input, the additional conversions may have a small
  208. performance cost.</i>
  209. </p>
  210. </blockquote>
  211. <p>
  212. Custom materials based on [page:ShaderMaterial] and [page:RawShaderMaterial] have to implement their own output color space conversion.
  213. For instances of `ShaderMaterial`, adding the `colorspace_fragment` shader chunk to the fragment shader's `main()` function should be sufficient.
  214. </p>
  215. <h2>Working with THREE.Color instances</h2>
  216. <p>
  217. Methods reading or modifying [page:Color] instances assume data is already in the
  218. three.js working color space, Linear-sRGB. RGB and HSL components are direct
  219. representations of data stored by the Color instance, and are never converted
  220. implicitly. Color data may be explicitly converted with <i>.convertLinearToSRGB()</i>
  221. or <i>.convertSRGBToLinear()</i>.
  222. </p>
  223. <code>
  224. // RGB components (no change).
  225. color.r = color.g = color.b = 0.5;
  226. console.log( color.r ); // → 0.5
  227. // Manual conversion.
  228. color.r = 0.5;
  229. color.convertSRGBToLinear();
  230. console.log( color.r ); // → 0.214041140
  231. </code>
  232. <p>
  233. With <i>ColorManagement.enabled = true</i> set (recommended), certain conversions
  234. are made automatically. Because hexadecimal and CSS colors are generally sRGB, [page:Color]
  235. methods will automatically convert these inputs from sRGB to Linear-sRGB in setters, or
  236. convert from Linear-sRGB to sRGB when returning hexadecimal or CSS output from getters.
  237. </p>
  238. <code>
  239. // Hexadecimal conversion.
  240. color.setHex( 0x808080 );
  241. console.log( color.r ); // → 0.214041140
  242. console.log( color.getHex() ); // → 0x808080
  243. // CSS conversion.
  244. color.setStyle( 'rgb( 0.5, 0.5, 0.5 )' );
  245. console.log( color.r ); // → 0.214041140
  246. // Override conversion with 'colorSpace' argument.
  247. color.setHex( 0x808080, LinearSRGBColorSpace );
  248. console.log( color.r ); // → 0.5
  249. console.log( color.getHex( LinearSRGBColorSpace ) ); // → 0x808080
  250. console.log( color.getHex( SRGBColorSpace ) ); // → 0xBCBCBC
  251. </code>
  252. <h2>Common mistakes</h2>
  253. <p>
  254. When an individual color or texture is misconfigured, it will appear darker or lighter than
  255. expected. When the renderer's output color space is misconfigured, the entire scene may appear
  256. darker (e.g. missing conversion to sRGB) or lighter (e.g. a double conversion to sRGB with
  257. post-processing). In each case the problem may not be uniform, and simply increasing/decreasing
  258. lighting does not solve it.
  259. </p>
  260. <p>
  261. A more subtle issue appears when <i>both</i> the input color spaces and the output color
  262. spaces are incorrect — the overall brightness levels may be fine, but colors may change
  263. unexpectedly under different lighting, or shading may appear more blown-out and less soft
  264. than intended. These two wrongs do not make a right, and it's important that the working
  265. color space be linear ("scene referred") and the output color space be nonlinear
  266. ("display referred").
  267. </p>
  268. <h2>Further reading</h2>
  269. <ul>
  270. <li>
  271. <a href="https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear" target="_blank" rel="noopener">GPU Gems 3: The Importance of Being Linear</a>, by Larry Gritz and Eugene d'Eon
  272. </li>
  273. <li>
  274. <a href="https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/" target="_blank" rel="noopener">What every coder should know about gamma</a>, by John Novak
  275. </li>
  276. <li>
  277. <a href="https://hg2dc.com/" target="_blank" rel="noopener">The Hitchhiker's Guide to Digital Color</a>, by Troy Sobotka
  278. </li>
  279. <li>
  280. <a href="https://docs.blender.org/manual/en/latest/render/color_management.html" target="_blank" rel="noopener">Color Management</a>, Blender
  281. </li>
  282. </ul>
  283. </body>
  284. </html>