ACES.hlsl 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309
  1. #ifndef __ACES__
  2. #define __ACES__
  3. /**
  4. * https://github.com/ampas/aces-dev
  5. *
  6. * Academy Color Encoding System (ACES) software and tools are provided by the
  7. * Academy under the following terms and conditions: A worldwide, royalty-free,
  8. * non-exclusive right to copy, modify, create derivatives, and use, in source and
  9. * binary forms, is hereby granted, subject to acceptance of this license.
  10. *
  11. * Copyright 2015 Academy of Motion Picture Arts and Sciences (A.M.P.A.S.).
  12. * Portions contributed by others as indicated. All rights reserved.
  13. *
  14. * Performance of any of the aforementioned acts indicates acceptance to be bound
  15. * by the following terms and conditions:
  16. *
  17. * * Copies of source code, in whole or in part, must retain the above copyright
  18. * notice, this list of conditions and the Disclaimer of Warranty.
  19. *
  20. * * Use in binary form must retain the above copyright notice, this list of
  21. * conditions and the Disclaimer of Warranty in the documentation and/or other
  22. * materials provided with the distribution.
  23. *
  24. * * Nothing in this license shall be deemed to grant any rights to trademarks,
  25. * copyrights, patents, trade secrets or any other intellectual property of
  26. * A.M.P.A.S. or any contributors, except as expressly stated herein.
  27. *
  28. * * Neither the name "A.M.P.A.S." nor the name of any other contributors to this
  29. * software may be used to endorse or promote products derivative of or based on
  30. * this software without express prior written permission of A.M.P.A.S. or the
  31. * contributors, as appropriate.
  32. *
  33. * This license shall be construed pursuant to the laws of the State of
  34. * California, and any disputes related thereto shall be subject to the
  35. * jurisdiction of the courts therein.
  36. *
  37. * Disclaimer of Warranty: THIS SOFTWARE IS PROVIDED BY A.M.P.A.S. AND CONTRIBUTORS
  38. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  39. * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
  40. * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL A.M.P.A.S., OR ANY
  41. * CONTRIBUTORS OR DISTRIBUTORS, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  42. * SPECIAL, EXEMPLARY, RESITUTIONARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  43. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  44. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  45. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  46. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  47. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  48. *
  49. * WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE ACADEMY SPECIFICALLY
  50. * DISCLAIMS ANY REPRESENTATIONS OR WARRANTIES WHATSOEVER RELATED TO PATENT OR
  51. * OTHER INTELLECTUAL PROPERTY RIGHTS IN THE ACADEMY COLOR ENCODING SYSTEM, OR
  52. * APPLICATIONS THEREOF, HELD BY PARTIES OTHER THAN A.M.P.A.S.,WHETHER DISCLOSED OR
  53. * UNDISCLOSED.
  54. */
  55. #include "StdLib.hlsl"
  56. #define ACEScc_MAX 1.4679964
  57. #define ACEScc_MIDGRAY 0.4135884
  58. //
  59. // Precomputed matrices (pre-transposed)
  60. // See https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
  61. //
  62. static const half3x3 sRGB_2_AP0 = {
  63. 0.4397010, 0.3829780, 0.1773350,
  64. 0.0897923, 0.8134230, 0.0967616,
  65. 0.0175440, 0.1115440, 0.8707040
  66. };
  67. static const half3x3 sRGB_2_AP1 = {
  68. 0.61319, 0.33951, 0.04737,
  69. 0.07021, 0.91634, 0.01345,
  70. 0.02062, 0.10957, 0.86961
  71. };
  72. static const half3x3 AP0_2_sRGB = {
  73. 2.52169, -1.13413, -0.38756,
  74. -0.27648, 1.37272, -0.09624,
  75. -0.01538, -0.15298, 1.16835,
  76. };
  77. static const half3x3 AP1_2_sRGB = {
  78. 1.70505, -0.62179, -0.08326,
  79. -0.13026, 1.14080, -0.01055,
  80. -0.02400, -0.12897, 1.15297,
  81. };
  82. static const half3x3 AP0_2_AP1_MAT = {
  83. 1.4514393161, -0.2365107469, -0.2149285693,
  84. -0.0765537734, 1.1762296998, -0.0996759264,
  85. 0.0083161484, -0.0060324498, 0.9977163014
  86. };
  87. static const half3x3 AP1_2_AP0_MAT = {
  88. 0.6954522414, 0.1406786965, 0.1638690622,
  89. 0.0447945634, 0.8596711185, 0.0955343182,
  90. -0.0055258826, 0.0040252103, 1.0015006723
  91. };
  92. static const half3x3 AP1_2_XYZ_MAT = {
  93. 0.6624541811, 0.1340042065, 0.1561876870,
  94. 0.2722287168, 0.6740817658, 0.0536895174,
  95. -0.0055746495, 0.0040607335, 1.0103391003
  96. };
  97. static const half3x3 XYZ_2_AP1_MAT = {
  98. 1.6410233797, -0.3248032942, -0.2364246952,
  99. -0.6636628587, 1.6153315917, 0.0167563477,
  100. 0.0117218943, -0.0082844420, 0.9883948585
  101. };
  102. static const half3x3 XYZ_2_REC709_MAT = {
  103. 3.2409699419, -1.5373831776, -0.4986107603,
  104. -0.9692436363, 1.8759675015, 0.0415550574,
  105. 0.0556300797, -0.2039769589, 1.0569715142
  106. };
  107. static const half3x3 XYZ_2_REC2020_MAT = {
  108. 1.7166511880, -0.3556707838, -0.2533662814,
  109. -0.6666843518, 1.6164812366, 0.0157685458,
  110. 0.0176398574, -0.0427706133, 0.9421031212
  111. };
  112. static const half3x3 XYZ_2_DCIP3_MAT = {
  113. 2.7253940305, -1.0180030062, -0.4401631952,
  114. -0.7951680258, 1.6897320548, 0.0226471906,
  115. 0.0412418914, -0.0876390192, 1.1009293786
  116. };
  117. static const half3 AP1_RGB2Y = half3(0.272229, 0.674082, 0.0536895);
  118. static const half3x3 RRT_SAT_MAT = {
  119. 0.9708890, 0.0269633, 0.00214758,
  120. 0.0108892, 0.9869630, 0.00214758,
  121. 0.0108892, 0.0269633, 0.96214800
  122. };
  123. static const half3x3 ODT_SAT_MAT = {
  124. 0.949056, 0.0471857, 0.00375827,
  125. 0.019056, 0.9771860, 0.00375827,
  126. 0.019056, 0.0471857, 0.93375800
  127. };
  128. static const half3x3 D60_2_D65_CAT = {
  129. 0.98722400, -0.00611327, 0.0159533,
  130. -0.00759836, 1.00186000, 0.0053302,
  131. 0.00307257, -0.00509595, 1.0816800
  132. };
  133. //
  134. // Unity to ACES
  135. //
  136. // converts Unity raw (sRGB primaries) to
  137. // ACES2065-1 (AP0 w/ linear encoding)
  138. //
  139. half3 unity_to_ACES(half3 x)
  140. {
  141. x = mul(sRGB_2_AP0, x);
  142. return x;
  143. }
  144. //
  145. // ACES to Unity
  146. //
  147. // converts ACES2065-1 (AP0 w/ linear encoding)
  148. // Unity raw (sRGB primaries) to
  149. //
  150. half3 ACES_to_unity(half3 x)
  151. {
  152. x = mul(AP0_2_sRGB, x);
  153. return x;
  154. }
  155. //
  156. // Unity to ACEScg
  157. //
  158. // converts Unity raw (sRGB primaries) to
  159. // ACEScg (AP1 w/ linear encoding)
  160. //
  161. half3 unity_to_ACEScg(half3 x)
  162. {
  163. x = mul(sRGB_2_AP1, x);
  164. return x;
  165. }
  166. //
  167. // ACEScg to Unity
  168. //
  169. // converts ACEScg (AP1 w/ linear encoding) to
  170. // Unity raw (sRGB primaries)
  171. //
  172. half3 ACEScg_to_unity(half3 x)
  173. {
  174. x = mul(AP1_2_sRGB, x);
  175. return x;
  176. }
  177. //
  178. // ACES Color Space Conversion - ACES to ACEScc
  179. //
  180. // converts ACES2065-1 (AP0 w/ linear encoding) to
  181. // ACEScc (AP1 w/ logarithmic encoding)
  182. //
  183. // This transform follows the formulas from section 4.4 in S-2014-003
  184. //
  185. half ACES_to_ACEScc(half x)
  186. {
  187. if (x <= 0.0)
  188. return -0.35828683; // = (log2(pow(2.0, -15.0) * 0.5) + 9.72) / 17.52
  189. else if (x < pow(2.0, -15.0))
  190. return (log2(pow(2.0, -16.0) + x * 0.5) + 9.72) / 17.52;
  191. else // (x >= pow(2.0, -15.0))
  192. return (log2(x) + 9.72) / 17.52;
  193. }
  194. half3 ACES_to_ACEScc(half3 x)
  195. {
  196. x = clamp(x, 0.0, HALF_MAX);
  197. // x is clamped to [0, HALF_MAX], skip the <= 0 check
  198. return (x < 0.00003051757) ? (log2(0.00001525878 + x * 0.5) + 9.72) / 17.52 : (log2(x) + 9.72) / 17.52;
  199. /*
  200. return half3(
  201. ACES_to_ACEScc(x.r),
  202. ACES_to_ACEScc(x.g),
  203. ACES_to_ACEScc(x.b)
  204. );
  205. */
  206. }
  207. //
  208. // ACES Color Space Conversion - ACEScc to ACES
  209. //
  210. // converts ACEScc (AP1 w/ ACESlog encoding) to
  211. // ACES2065-1 (AP0 w/ linear encoding)
  212. //
  213. // This transform follows the formulas from section 4.4 in S-2014-003
  214. //
  215. half ACEScc_to_ACES(half x)
  216. {
  217. // TODO: Optimize me
  218. if (x < -0.3013698630) // (9.72 - 15) / 17.52
  219. return (pow(2.0, x * 17.52 - 9.72) - pow(2.0, -16.0)) * 2.0;
  220. else if (x < (log2(HALF_MAX) + 9.72) / 17.52)
  221. return pow(2.0, x * 17.52 - 9.72);
  222. else // (x >= (log2(HALF_MAX) + 9.72) / 17.52)
  223. return HALF_MAX;
  224. }
  225. half3 ACEScc_to_ACES(half3 x)
  226. {
  227. return half3(
  228. ACEScc_to_ACES(x.r),
  229. ACEScc_to_ACES(x.g),
  230. ACEScc_to_ACES(x.b)
  231. );
  232. }
  233. //
  234. // ACES Color Space Conversion - ACES to ACEScg
  235. //
  236. // converts ACES2065-1 (AP0 w/ linear encoding) to
  237. // ACEScg (AP1 w/ linear encoding)
  238. //
  239. half3 ACES_to_ACEScg(half3 x)
  240. {
  241. return mul(AP0_2_AP1_MAT, x);
  242. }
  243. //
  244. // ACES Color Space Conversion - ACEScg to ACES
  245. //
  246. // converts ACEScg (AP1 w/ linear encoding) to
  247. // ACES2065-1 (AP0 w/ linear encoding)
  248. //
  249. half3 ACEScg_to_ACES(half3 x)
  250. {
  251. return mul(AP1_2_AP0_MAT, x);
  252. }
  253. //
  254. // Reference Rendering Transform (RRT)
  255. //
  256. // Input is ACES
  257. // Output is OCES
  258. //
  259. half rgb_2_saturation(half3 rgb)
  260. {
  261. const half TINY = 1e-4;
  262. half mi = Min3(rgb.r, rgb.g, rgb.b);
  263. half ma = Max3(rgb.r, rgb.g, rgb.b);
  264. return (max(ma, TINY) - max(mi, TINY)) / max(ma, 1e-2);
  265. }
  266. half rgb_2_yc(half3 rgb)
  267. {
  268. const half ycRadiusWeight = 1.75;
  269. // Converts RGB to a luminance proxy, here called YC
  270. // YC is ~ Y + K * Chroma
  271. // Constant YC is a cone-shaped surface in RGB space, with the tip on the
  272. // neutral axis, towards white.
  273. // YC is normalized: RGB 1 1 1 maps to YC = 1
  274. //
  275. // ycRadiusWeight defaults to 1.75, although can be overridden in function
  276. // call to rgb_2_yc
  277. // ycRadiusWeight = 1 -> YC for pure cyan, magenta, yellow == YC for neutral
  278. // of same value
  279. // ycRadiusWeight = 2 -> YC for pure red, green, blue == YC for neutral of
  280. // same value.
  281. half r = rgb.x;
  282. half g = rgb.y;
  283. half b = rgb.z;
  284. half chroma = sqrt(b * (b - g) + g * (g - r) + r * (r - b));
  285. return (b + g + r + ycRadiusWeight * chroma) / 3.0;
  286. }
  287. half rgb_2_hue(half3 rgb)
  288. {
  289. // Returns a geometric hue angle in degrees (0-360) based on RGB values.
  290. // For neutral colors, hue is undefined and the function will return a quiet NaN value.
  291. half hue;
  292. if (rgb.x == rgb.y && rgb.y == rgb.z)
  293. hue = 0.0; // RGB triplets where RGB are equal have an undefined hue
  294. else
  295. hue = (180.0 / PI) * atan2(sqrt(3.0) * (rgb.y - rgb.z), 2.0 * rgb.x - rgb.y - rgb.z);
  296. if (hue < 0.0) hue = hue + 360.0;
  297. return hue;
  298. }
  299. half center_hue(half hue, half centerH)
  300. {
  301. half hueCentered = hue - centerH;
  302. if (hueCentered < -180.0) hueCentered = hueCentered + 360.0;
  303. else if (hueCentered > 180.0) hueCentered = hueCentered - 360.0;
  304. return hueCentered;
  305. }
  306. half sigmoid_shaper(half x)
  307. {
  308. // Sigmoid function in the range 0 to 1 spanning -2 to +2.
  309. half t = max(1.0 - abs(x / 2.0), 0.0);
  310. half y = 1.0 + FastSign(x) * (1.0 - t * t);
  311. return y / 2.0;
  312. }
  313. half glow_fwd(half ycIn, half glowGainIn, half glowMid)
  314. {
  315. half glowGainOut;
  316. if (ycIn <= 2.0 / 3.0 * glowMid)
  317. glowGainOut = glowGainIn;
  318. else if (ycIn >= 2.0 * glowMid)
  319. glowGainOut = 0.0;
  320. else
  321. glowGainOut = glowGainIn * (glowMid / ycIn - 1.0 / 2.0);
  322. return glowGainOut;
  323. }
  324. /*
  325. half cubic_basis_shaper
  326. (
  327. half x,
  328. half w // full base width of the shaper function (in degrees)
  329. )
  330. {
  331. half M[4][4] = {
  332. { -1.0 / 6, 3.0 / 6, -3.0 / 6, 1.0 / 6 },
  333. { 3.0 / 6, -6.0 / 6, 3.0 / 6, 0.0 / 6 },
  334. { -3.0 / 6, 0.0 / 6, 3.0 / 6, 0.0 / 6 },
  335. { 1.0 / 6, 4.0 / 6, 1.0 / 6, 0.0 / 6 }
  336. };
  337. half knots[5] = {
  338. -w / 2.0,
  339. -w / 4.0,
  340. 0.0,
  341. w / 4.0,
  342. w / 2.0
  343. };
  344. half y = 0.0;
  345. if ((x > knots[0]) && (x < knots[4]))
  346. {
  347. half knot_coord = (x - knots[0]) * 4.0 / w;
  348. int j = knot_coord;
  349. half t = knot_coord - j;
  350. half monomials[4] = { t*t*t, t*t, t, 1.0 };
  351. // (if/else structure required for compatibility with CTL < v1.5.)
  352. if (j == 3)
  353. {
  354. y = monomials[0] * M[0][0] + monomials[1] * M[1][0] +
  355. monomials[2] * M[2][0] + monomials[3] * M[3][0];
  356. }
  357. else if (j == 2)
  358. {
  359. y = monomials[0] * M[0][1] + monomials[1] * M[1][1] +
  360. monomials[2] * M[2][1] + monomials[3] * M[3][1];
  361. }
  362. else if (j == 1)
  363. {
  364. y = monomials[0] * M[0][2] + monomials[1] * M[1][2] +
  365. monomials[2] * M[2][2] + monomials[3] * M[3][2];
  366. }
  367. else if (j == 0)
  368. {
  369. y = monomials[0] * M[0][3] + monomials[1] * M[1][3] +
  370. monomials[2] * M[2][3] + monomials[3] * M[3][3];
  371. }
  372. else
  373. {
  374. y = 0.0;
  375. }
  376. }
  377. return y * 3.0 / 2.0;
  378. }
  379. */
  380. static const half3x3 M = {
  381. 0.5, -1.0, 0.5,
  382. -1.0, 1.0, 0.0,
  383. 0.5, 0.5, 0.0
  384. };
  385. half segmented_spline_c5_fwd(half x)
  386. {
  387. const half coefsLow[6] = { -4.0000000000, -4.0000000000, -3.1573765773, -0.4852499958, 1.8477324706, 1.8477324706 }; // coefs for B-spline between minPoint and midPoint (units of log luminance)
  388. const half coefsHigh[6] = { -0.7185482425, 2.0810307172, 3.6681241237, 4.0000000000, 4.0000000000, 4.0000000000 }; // coefs for B-spline between midPoint and maxPoint (units of log luminance)
  389. const half2 minPoint = half2(0.18 * exp2(-15.0), 0.0001); // {luminance, luminance} linear extension below this
  390. const half2 midPoint = half2(0.18, 0.48); // {luminance, luminance}
  391. const half2 maxPoint = half2(0.18 * exp2(18.0), 10000.0); // {luminance, luminance} linear extension above this
  392. const half slopeLow = 0.0; // log-log slope of low linear extension
  393. const half slopeHigh = 0.0; // log-log slope of high linear extension
  394. const int N_KNOTS_LOW = 4;
  395. const int N_KNOTS_HIGH = 4;
  396. // Check for negatives or zero before taking the log. If negative or zero,
  397. // set to ACESMIN.1
  398. float xCheck = x;
  399. if (xCheck <= 0.0) xCheck = 0.00006103515; // = pow(2.0, -14.0);
  400. half logx = log10(xCheck);
  401. half logy;
  402. if (logx <= log10(minPoint.x))
  403. {
  404. logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
  405. }
  406. else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
  407. {
  408. half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
  409. int j = knot_coord;
  410. half t = knot_coord - j;
  411. half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
  412. half3 monomials = half3(t * t, t, 1.0);
  413. logy = dot(monomials, mul(M, cf));
  414. }
  415. else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
  416. {
  417. half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
  418. int j = knot_coord;
  419. half t = knot_coord - j;
  420. half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
  421. half3 monomials = half3(t * t, t, 1.0);
  422. logy = dot(monomials, mul(M, cf));
  423. }
  424. else
  425. { //if (logIn >= log10(maxPoint.x)) {
  426. logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
  427. }
  428. return pow(10.0, logy);
  429. }
  430. half segmented_spline_c9_fwd(half x)
  431. {
  432. const half coefsLow[10] = { -1.6989700043, -1.6989700043, -1.4779000000, -1.2291000000, -0.8648000000, -0.4480000000, 0.0051800000, 0.4511080334, 0.9113744414, 0.9113744414 }; // coefs for B-spline between minPoint and midPoint (units of log luminance)
  433. const half coefsHigh[10] = { 0.5154386965, 0.8470437783, 1.1358000000, 1.3802000000, 1.5197000000, 1.5985000000, 1.6467000000, 1.6746091357, 1.6878733390, 1.6878733390 }; // coefs for B-spline between midPoint and maxPoint (units of log luminance)
  434. const half2 minPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(-6.5)), 0.02); // {luminance, luminance} linear extension below this
  435. const half2 midPoint = half2(segmented_spline_c5_fwd(0.18), 4.8); // {luminance, luminance}
  436. const half2 maxPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(6.5)), 48.0); // {luminance, luminance} linear extension above this
  437. const half slopeLow = 0.0; // log-log slope of low linear extension
  438. const half slopeHigh = 0.04; // log-log slope of high linear extension
  439. const int N_KNOTS_LOW = 8;
  440. const int N_KNOTS_HIGH = 8;
  441. // Check for negatives or zero before taking the log. If negative or zero,
  442. // set to OCESMIN.
  443. half xCheck = x;
  444. if (xCheck <= 0.0) xCheck = 1e-4;
  445. half logx = log10(xCheck);
  446. half logy;
  447. if (logx <= log10(minPoint.x))
  448. {
  449. logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
  450. }
  451. else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
  452. {
  453. half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
  454. int j = knot_coord;
  455. half t = knot_coord - j;
  456. half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
  457. half3 monomials = half3(t * t, t, 1.0);
  458. logy = dot(monomials, mul(M, cf));
  459. }
  460. else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
  461. {
  462. half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
  463. int j = knot_coord;
  464. half t = knot_coord - j;
  465. half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
  466. half3 monomials = half3(t * t, t, 1.0);
  467. logy = dot(monomials, mul(M, cf));
  468. }
  469. else
  470. { //if (logIn >= log10(maxPoint.x)) {
  471. logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
  472. }
  473. return pow(10.0, logy);
  474. }
  475. static const half RRT_GLOW_GAIN = 0.05;
  476. static const half RRT_GLOW_MID = 0.08;
  477. static const half RRT_RED_SCALE = 0.82;
  478. static const half RRT_RED_PIVOT = 0.03;
  479. static const half RRT_RED_HUE = 0.0;
  480. static const half RRT_RED_WIDTH = 135.0;
  481. static const half RRT_SAT_FACTOR = 0.96;
  482. half3 RRT(half3 aces)
  483. {
  484. // --- Glow module --- //
  485. half saturation = rgb_2_saturation(aces);
  486. half ycIn = rgb_2_yc(aces);
  487. half s = sigmoid_shaper((saturation - 0.4) / 0.2);
  488. half addedGlow = 1.0 + glow_fwd(ycIn, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
  489. aces *= addedGlow;
  490. // --- Red modifier --- //
  491. half hue = rgb_2_hue(aces);
  492. half centeredHue = center_hue(hue, RRT_RED_HUE);
  493. half hueWeight;
  494. {
  495. //hueWeight = cubic_basis_shaper(centeredHue, RRT_RED_WIDTH);
  496. hueWeight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centeredHue / RRT_RED_WIDTH));
  497. hueWeight *= hueWeight;
  498. }
  499. aces.r += hueWeight * saturation * (RRT_RED_PIVOT - aces.r) * (1.0 - RRT_RED_SCALE);
  500. // --- ACES to RGB rendering space --- //
  501. aces = clamp(aces, 0.0, HALF_MAX); // avoids saturated negative colors from becoming positive in the matrix
  502. half3 rgbPre = mul(AP0_2_AP1_MAT, aces);
  503. rgbPre = clamp(rgbPre, 0, HALF_MAX);
  504. // --- Global desaturation --- //
  505. //rgbPre = mul(RRT_SAT_MAT, rgbPre);
  506. rgbPre = lerp(dot(rgbPre, AP1_RGB2Y).xxx, rgbPre, RRT_SAT_FACTOR.xxx);
  507. // --- Apply the tonescale independently in rendering-space RGB --- //
  508. half3 rgbPost;
  509. rgbPost.x = segmented_spline_c5_fwd(rgbPre.x);
  510. rgbPost.y = segmented_spline_c5_fwd(rgbPre.y);
  511. rgbPost.z = segmented_spline_c5_fwd(rgbPre.z);
  512. // --- RGB rendering space to OCES --- //
  513. half3 rgbOces = mul(AP1_2_AP0_MAT, rgbPost);
  514. return rgbOces;
  515. }
  516. //
  517. // Output Device Transform
  518. //
  519. half3 Y_2_linCV(half3 Y, half Ymax, half Ymin)
  520. {
  521. return (Y - Ymin) / (Ymax - Ymin);
  522. }
  523. half3 XYZ_2_xyY(half3 XYZ)
  524. {
  525. half divisor = max(dot(XYZ, (1.0).xxx), 1e-4);
  526. return half3(XYZ.xy / divisor, XYZ.y);
  527. }
  528. half3 xyY_2_XYZ(half3 xyY)
  529. {
  530. half m = xyY.z / max(xyY.y, 1e-4);
  531. half3 XYZ = half3(xyY.xz, (1.0 - xyY.x - xyY.y));
  532. XYZ.xz *= m;
  533. return XYZ;
  534. }
  535. static const half DIM_SURROUND_GAMMA = 0.9811;
  536. half3 darkSurround_to_dimSurround(half3 linearCV)
  537. {
  538. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  539. half3 xyY = XYZ_2_xyY(XYZ);
  540. xyY.z = clamp(xyY.z, 0.0, HALF_MAX);
  541. xyY.z = pow(xyY.z, DIM_SURROUND_GAMMA);
  542. XYZ = xyY_2_XYZ(xyY);
  543. return mul(XYZ_2_AP1_MAT, XYZ);
  544. }
  545. half moncurve_r(half y, half gamma, half offs)
  546. {
  547. // Reverse monitor curve
  548. half x;
  549. const half yb = pow(offs * gamma / ((gamma - 1.0) * (1.0 + offs)), gamma);
  550. const half rs = pow((gamma - 1.0) / offs, gamma - 1.0) * pow((1.0 + offs) / gamma, gamma);
  551. if (y >= yb)
  552. x = (1.0 + offs) * pow(y, 1.0 / gamma) - offs;
  553. else
  554. x = y * rs;
  555. return x;
  556. }
  557. half bt1886_r(half L, half gamma, half Lw, half Lb)
  558. {
  559. // The reference EOTF specified in Rec. ITU-R BT.1886
  560. // L = a(max[(V+b),0])^g
  561. half a = pow(pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma), gamma);
  562. half b = pow(Lb, 1.0 / gamma) / (pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma));
  563. half V = pow(max(L / a, 0.0), 1.0 / gamma) - b;
  564. return V;
  565. }
  566. half roll_white_fwd(
  567. half x, // color value to adjust (white scaled to around 1.0)
  568. half new_wht, // white adjustment (e.g. 0.9 for 10% darkening)
  569. half width // adjusted width (e.g. 0.25 for top quarter of the tone scale)
  570. )
  571. {
  572. const half x0 = -1.0;
  573. const half x1 = x0 + width;
  574. const half y0 = -new_wht;
  575. const half y1 = x1;
  576. const half m1 = (x1 - x0);
  577. const half a = y0 - y1 + m1;
  578. const half b = 2.0 * (y1 - y0) - m1;
  579. const half c = y0;
  580. const half t = (-x - x0) / (x1 - x0);
  581. half o = 0.0;
  582. if (t < 0.0)
  583. o = -(t * b + c);
  584. else if (t > 1.0)
  585. o = x;
  586. else
  587. o = -((t * a + b) * t + c);
  588. return o;
  589. }
  590. half3 linear_to_sRGB(half3 x)
  591. {
  592. return (x <= 0.0031308 ? (x * 12.9232102) : 1.055 * pow(x, 1.0 / 2.4) - 0.055);
  593. }
  594. half3 linear_to_bt1886(half3 x, half gamma, half Lw, half Lb)
  595. {
  596. // Good enough approximation for now, may consider using the exact formula instead
  597. // TODO: Experiment
  598. return pow(max(x, 0.0), 1.0 / 2.4);
  599. // Correct implementation (Reference EOTF specified in Rec. ITU-R BT.1886) :
  600. // L = a(max[(V+b),0])^g
  601. half invgamma = 1.0 / gamma;
  602. half p_Lw = pow(Lw, invgamma);
  603. half p_Lb = pow(Lb, invgamma);
  604. half3 a = pow(p_Lw - p_Lb, gamma).xxx;
  605. half3 b = (p_Lb / p_Lw - p_Lb).xxx;
  606. half3 V = pow(max(x / a, 0.0), invgamma.xxx) - b;
  607. return V;
  608. }
  609. static const half CINEMA_WHITE = 48.0;
  610. static const half CINEMA_BLACK = CINEMA_WHITE / 2400.0;
  611. static const half ODT_SAT_FACTOR = 0.93;
  612. // <ACEStransformID>ODT.Academy.RGBmonitor_100nits_dim.a1.0.3</ACEStransformID>
  613. // <ACESuserName>ACES 1.0 Output - sRGB</ACESuserName>
  614. //
  615. // Output Device Transform - RGB computer monitor
  616. //
  617. //
  618. // Summary :
  619. // This transform is intended for mapping OCES onto a desktop computer monitor
  620. // typical of those used in motion picture visual effects production. These
  621. // monitors may occasionally be referred to as "sRGB" displays, however, the
  622. // monitor for which this transform is designed does not exactly match the
  623. // specifications in IEC 61966-2-1:1999.
  624. //
  625. // The assumed observer adapted white is D65, and the viewing environment is
  626. // that of a dim surround.
  627. //
  628. // The monitor specified is intended to be more typical of those found in
  629. // visual effects production.
  630. //
  631. // Device Primaries :
  632. // Primaries are those specified in Rec. ITU-R BT.709
  633. // CIE 1931 chromaticities: x y Y
  634. // Red: 0.64 0.33
  635. // Green: 0.3 0.6
  636. // Blue: 0.15 0.06
  637. // White: 0.3127 0.329 100 cd/m^2
  638. //
  639. // Display EOTF :
  640. // The reference electro-optical transfer function specified in
  641. // IEC 61966-2-1:1999.
  642. //
  643. // Signal Range:
  644. // This transform outputs full range code values.
  645. //
  646. // Assumed observer adapted white point:
  647. // CIE 1931 chromaticities: x y
  648. // 0.3127 0.329
  649. //
  650. // Viewing Environment:
  651. // This ODT has a compensation for viewing environment variables more typical
  652. // of those associated with video mastering.
  653. //
  654. half3 ODT_RGBmonitor_100nits_dim(half3 oces)
  655. {
  656. // OCES to RGB rendering space
  657. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  658. // Apply the tonescale independently in rendering-space RGB
  659. half3 rgbPost;
  660. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  661. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  662. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  663. // Scale luminance to linear code value
  664. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  665. // Apply gamma adjustment to compensate for dim surround
  666. linearCV = darkSurround_to_dimSurround(linearCV);
  667. // Apply desaturation to compensate for luminance difference
  668. //linearCV = mul(ODT_SAT_MAT, linearCV);
  669. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  670. // Convert to display primary encoding
  671. // Rendering space RGB to XYZ
  672. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  673. // Apply CAT from ACES white point to assumed observer adapted white point
  674. XYZ = mul(D60_2_D65_CAT, XYZ);
  675. // CIE XYZ to display primaries
  676. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  677. // Handle out-of-gamut values
  678. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  679. linearCV = saturate(linearCV);
  680. // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
  681. // with sRGB opto-electrical transfer function (OETF).
  682. /*
  683. // Encode linear code values with transfer function
  684. half3 outputCV;
  685. // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
  686. const half DISPGAMMA = 2.4;
  687. const half OFFSET = 0.055;
  688. outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
  689. outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
  690. outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
  691. outputCV = linear_to_sRGB(linearCV);
  692. */
  693. // Unity already draws to a sRGB target
  694. return linearCV;
  695. }
  696. // <ACEStransformID>ODT.Academy.RGBmonitor_D60sim_100nits_dim.a1.0.3</ACEStransformID>
  697. // <ACESuserName>ACES 1.0 Output - sRGB (D60 sim.)</ACESuserName>
  698. //
  699. // Output Device Transform - RGB computer monitor (D60 simulation)
  700. //
  701. //
  702. // Summary :
  703. // This transform is intended for mapping OCES onto a desktop computer monitor
  704. // typical of those used in motion picture visual effects production. These
  705. // monitors may occasionally be referred to as "sRGB" displays, however, the
  706. // monitor for which this transform is designed does not exactly match the
  707. // specifications in IEC 61966-2-1:1999.
  708. //
  709. // The assumed observer adapted white is D60, and the viewing environment is
  710. // that of a dim surround.
  711. //
  712. // The monitor specified is intended to be more typical of those found in
  713. // visual effects production.
  714. //
  715. // Device Primaries :
  716. // Primaries are those specified in Rec. ITU-R BT.709
  717. // CIE 1931 chromaticities: x y Y
  718. // Red: 0.64 0.33
  719. // Green: 0.3 0.6
  720. // Blue: 0.15 0.06
  721. // White: 0.3127 0.329 100 cd/m^2
  722. //
  723. // Display EOTF :
  724. // The reference electro-optical transfer function specified in
  725. // IEC 61966-2-1:1999.
  726. //
  727. // Signal Range:
  728. // This transform outputs full range code values.
  729. //
  730. // Assumed observer adapted white point:
  731. // CIE 1931 chromaticities: x y
  732. // 0.32168 0.33767
  733. //
  734. // Viewing Environment:
  735. // This ODT has a compensation for viewing environment variables more typical
  736. // of those associated with video mastering.
  737. //
  738. half3 ODT_RGBmonitor_D60sim_100nits_dim(half3 oces)
  739. {
  740. // OCES to RGB rendering space
  741. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  742. // Apply the tonescale independently in rendering-space RGB
  743. half3 rgbPost;
  744. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  745. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  746. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  747. // Scale luminance to linear code value
  748. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  749. // --- Compensate for different white point being darker --- //
  750. // This adjustment is to correct an issue that exists in ODTs where the device
  751. // is calibrated to a white chromaticity other than D60. In order to simulate
  752. // D60 on such devices, unequal code values are sent to the display to achieve
  753. // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
  754. // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
  755. // 0.351) the red channel is higher than green and blue to compensate for the
  756. // "greenish" DCI white. This is the correct behavior but it means that as
  757. // highlight increase, the red channel will hit the device maximum first and
  758. // clip, resulting in a chromaticity shift as the green and blue channels
  759. // continue to increase.
  760. // To avoid this clipping error, a slight scale factor is applied to allow the
  761. // ODTs to simulate D60 within the D65 calibration white point.
  762. // Scale and clamp white to avoid casted highlights due to D60 simulation
  763. const half SCALE = 0.955;
  764. linearCV = min(linearCV, 1.0) * SCALE;
  765. // Apply gamma adjustment to compensate for dim surround
  766. linearCV = darkSurround_to_dimSurround(linearCV);
  767. // Apply desaturation to compensate for luminance difference
  768. //linearCV = mul(ODT_SAT_MAT, linearCV);
  769. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  770. // Convert to display primary encoding
  771. // Rendering space RGB to XYZ
  772. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  773. // CIE XYZ to display primaries
  774. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  775. // Handle out-of-gamut values
  776. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  777. linearCV = saturate(linearCV);
  778. // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
  779. // with sRGB opto-electrical transfer function (OETF).
  780. /*
  781. // Encode linear code values with transfer function
  782. half3 outputCV;
  783. // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
  784. const half DISPGAMMA = 2.4;
  785. const half OFFSET = 0.055;
  786. outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
  787. outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
  788. outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
  789. outputCV = linear_to_sRGB(linearCV);
  790. */
  791. // Unity already draws to a sRGB target
  792. return linearCV;
  793. }
  794. // <ACEStransformID>ODT.Academy.Rec709_100nits_dim.a1.0.3</ACEStransformID>
  795. // <ACESuserName>ACES 1.0 Output - Rec.709</ACESuserName>
  796. //
  797. // Output Device Transform - Rec709
  798. //
  799. //
  800. // Summary :
  801. // This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
  802. // that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
  803. // adapted white is D65, and the viewing environment is a dim surround.
  804. //
  805. // A possible use case for this transform would be HDTV/video mastering.
  806. //
  807. // Device Primaries :
  808. // Primaries are those specified in Rec. ITU-R BT.709
  809. // CIE 1931 chromaticities: x y Y
  810. // Red: 0.64 0.33
  811. // Green: 0.3 0.6
  812. // Blue: 0.15 0.06
  813. // White: 0.3127 0.329 100 cd/m^2
  814. //
  815. // Display EOTF :
  816. // The reference electro-optical transfer function specified in
  817. // Rec. ITU-R BT.1886.
  818. //
  819. // Signal Range:
  820. // By default, this transform outputs full range code values. If instead a
  821. // SMPTE "legal" signal is desired, there is a runtime flag to output
  822. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  823. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  824. //
  825. // Assumed observer adapted white point:
  826. // CIE 1931 chromaticities: x y
  827. // 0.3127 0.329
  828. //
  829. // Viewing Environment:
  830. // This ODT has a compensation for viewing environment variables more typical
  831. // of those associated with video mastering.
  832. //
  833. half3 ODT_Rec709_100nits_dim(half3 oces)
  834. {
  835. // OCES to RGB rendering space
  836. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  837. // Apply the tonescale independently in rendering-space RGB
  838. half3 rgbPost;
  839. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  840. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  841. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  842. // Scale luminance to linear code value
  843. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  844. // Apply gamma adjustment to compensate for dim surround
  845. linearCV = darkSurround_to_dimSurround(linearCV);
  846. // Apply desaturation to compensate for luminance difference
  847. //linearCV = mul(ODT_SAT_MAT, linearCV);
  848. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  849. // Convert to display primary encoding
  850. // Rendering space RGB to XYZ
  851. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  852. // Apply CAT from ACES white point to assumed observer adapted white point
  853. XYZ = mul(D60_2_D65_CAT, XYZ);
  854. // CIE XYZ to display primaries
  855. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  856. // Handle out-of-gamut values
  857. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  858. linearCV = saturate(linearCV);
  859. // Encode linear code values with transfer function
  860. const half DISPGAMMA = 2.4;
  861. const half L_W = 1.0;
  862. const half L_B = 0.0;
  863. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  864. // TODO: Implement support for legal range.
  865. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  866. // by default which will result in double perceptual encoding, thus for now if one want to use
  867. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  868. // compensate for Unity default behaviour.
  869. return outputCV;
  870. }
  871. // <ACEStransformID>ODT.Academy.Rec709_D60sim_100nits_dim.a1.0.3</ACEStransformID>
  872. // <ACESuserName>ACES 1.0 Output - Rec.709 (D60 sim.)</ACESuserName>
  873. //
  874. // Output Device Transform - Rec709 (D60 simulation)
  875. //
  876. //
  877. // Summary :
  878. // This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
  879. // that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
  880. // adapted white is D60, and the viewing environment is a dim surround.
  881. //
  882. // A possible use case for this transform would be cinema "soft-proofing".
  883. //
  884. // Device Primaries :
  885. // Primaries are those specified in Rec. ITU-R BT.709
  886. // CIE 1931 chromaticities: x y Y
  887. // Red: 0.64 0.33
  888. // Green: 0.3 0.6
  889. // Blue: 0.15 0.06
  890. // White: 0.3127 0.329 100 cd/m^2
  891. //
  892. // Display EOTF :
  893. // The reference electro-optical transfer function specified in
  894. // Rec. ITU-R BT.1886.
  895. //
  896. // Signal Range:
  897. // By default, this transform outputs full range code values. If instead a
  898. // SMPTE "legal" signal is desired, there is a runtime flag to output
  899. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  900. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  901. //
  902. // Assumed observer adapted white point:
  903. // CIE 1931 chromaticities: x y
  904. // 0.32168 0.33767
  905. //
  906. // Viewing Environment:
  907. // This ODT has a compensation for viewing environment variables more typical
  908. // of those associated with video mastering.
  909. //
  910. half3 ODT_Rec709_D60sim_100nits_dim(half3 oces)
  911. {
  912. // OCES to RGB rendering space
  913. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  914. // Apply the tonescale independently in rendering-space RGB
  915. half3 rgbPost;
  916. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  917. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  918. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  919. // Scale luminance to linear code value
  920. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  921. // --- Compensate for different white point being darker --- //
  922. // This adjustment is to correct an issue that exists in ODTs where the device
  923. // is calibrated to a white chromaticity other than D60. In order to simulate
  924. // D60 on such devices, unequal code values must be sent to the display to achieve
  925. // the chromaticities of D60. More specifically, in order to produce D60 on a device
  926. // calibrated to a D65 white point (i.e. equal code values yield CIE x,y
  927. // chromaticities of 0.3127, 0.329) the red channel must be slightly higher than
  928. // that of green and blue in order to compensate for the relatively more "blue-ish"
  929. // D65 white. This unequalness of color channels is the correct behavior but it
  930. // means that as neutral highlights increase, the red channel will hit the
  931. // device maximum first and clip, resulting in a small chromaticity shift as the
  932. // green and blue channels continue to increase to their maximums.
  933. // To avoid this clipping error, a slight scale factor is applied to allow the
  934. // ODTs to simulate D60 within the D65 calibration white point.
  935. // Scale and clamp white to avoid casted highlights due to D60 simulation
  936. const half SCALE = 0.955;
  937. linearCV = min(linearCV, 1.0) * SCALE;
  938. // Apply gamma adjustment to compensate for dim surround
  939. linearCV = darkSurround_to_dimSurround(linearCV);
  940. // Apply desaturation to compensate for luminance difference
  941. //linearCV = mul(ODT_SAT_MAT, linearCV);
  942. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  943. // Convert to display primary encoding
  944. // Rendering space RGB to XYZ
  945. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  946. // CIE XYZ to display primaries
  947. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  948. // Handle out-of-gamut values
  949. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  950. linearCV = saturate(linearCV);
  951. // Encode linear code values with transfer function
  952. const half DISPGAMMA = 2.4;
  953. const half L_W = 1.0;
  954. const half L_B = 0.0;
  955. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  956. // TODO: Implement support for legal range.
  957. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  958. // by default which will result in double perceptual encoding, thus for now if one want to use
  959. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  960. // compensate for Unity default behaviour.
  961. return outputCV;
  962. }
  963. // <ACEStransformID>ODT.Academy.Rec2020_100nits_dim.a1.0.3</ACEStransformID>
  964. // <ACESuserName>ACES 1.0 Output - Rec.2020</ACESuserName>
  965. //
  966. // Output Device Transform - Rec2020
  967. //
  968. //
  969. // Summary :
  970. // This transform is intended for mapping OCES onto a Rec.2020 broadcast
  971. // monitor that is calibrated to a D65 white point at 100 cd/m^2. The assumed
  972. // observer adapted white is D65, and the viewing environment is that of a dim
  973. // surround.
  974. //
  975. // A possible use case for this transform would be UHDTV/video mastering.
  976. //
  977. // Device Primaries :
  978. // Primaries are those specified in Rec. ITU-R BT.2020
  979. // CIE 1931 chromaticities: x y Y
  980. // Red: 0.708 0.292
  981. // Green: 0.17 0.797
  982. // Blue: 0.131 0.046
  983. // White: 0.3127 0.329 100 cd/m^2
  984. //
  985. // Display EOTF :
  986. // The reference electro-optical transfer function specified in
  987. // Rec. ITU-R BT.1886.
  988. //
  989. // Signal Range:
  990. // By default, this transform outputs full range code values. If instead a
  991. // SMPTE "legal" signal is desired, there is a runtime flag to output
  992. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  993. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  994. //
  995. // Assumed observer adapted white point:
  996. // CIE 1931 chromaticities: x y
  997. // 0.3127 0.329
  998. //
  999. // Viewing Environment:
  1000. // This ODT has a compensation for viewing environment variables more typical
  1001. // of those associated with video mastering.
  1002. //
  1003. half3 ODT_Rec2020_100nits_dim(half3 oces)
  1004. {
  1005. // OCES to RGB rendering space
  1006. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1007. // Apply the tonescale independently in rendering-space RGB
  1008. half3 rgbPost;
  1009. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  1010. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  1011. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  1012. // Scale luminance to linear code value
  1013. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  1014. // Apply gamma adjustment to compensate for dim surround
  1015. linearCV = darkSurround_to_dimSurround(linearCV);
  1016. // Apply desaturation to compensate for luminance difference
  1017. //linearCV = mul(ODT_SAT_MAT, linearCV);
  1018. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  1019. // Convert to display primary encoding
  1020. // Rendering space RGB to XYZ
  1021. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1022. // Apply CAT from ACES white point to assumed observer adapted white point
  1023. XYZ = mul(D60_2_D65_CAT, XYZ);
  1024. // CIE XYZ to display primaries
  1025. linearCV = mul(XYZ_2_REC2020_MAT, XYZ);
  1026. // Handle out-of-gamut values
  1027. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  1028. linearCV = saturate(linearCV);
  1029. // Encode linear code values with transfer function
  1030. const half DISPGAMMA = 2.4;
  1031. const half L_W = 1.0;
  1032. const half L_B = 0.0;
  1033. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  1034. // TODO: Implement support for legal range.
  1035. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  1036. // by default which will result in double perceptual encoding, thus for now if one want to use
  1037. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  1038. // compensate for Unity default behaviour.
  1039. return outputCV;
  1040. }
  1041. // <ACEStransformID>ODT.Academy.P3DCI_48nits.a1.0.3</ACEStransformID>
  1042. // <ACESuserName>ACES 1.0 Output - P3-DCI</ACESuserName>
  1043. //
  1044. // Output Device Transform - P3DCI (D60 Simulation)
  1045. //
  1046. //
  1047. // Summary :
  1048. // This transform is intended for mapping OCES onto a P3 digital cinema
  1049. // projector that is calibrated to a DCI white point at 48 cd/m^2. The assumed
  1050. // observer adapted white is D60, and the viewing environment is that of a dark
  1051. // theater.
  1052. //
  1053. // Device Primaries :
  1054. // CIE 1931 chromaticities: x y Y
  1055. // Red: 0.68 0.32
  1056. // Green: 0.265 0.69
  1057. // Blue: 0.15 0.06
  1058. // White: 0.314 0.351 48 cd/m^2
  1059. //
  1060. // Display EOTF :
  1061. // Gamma: 2.6
  1062. //
  1063. // Assumed observer adapted white point:
  1064. // CIE 1931 chromaticities: x y
  1065. // 0.32168 0.33767
  1066. //
  1067. // Viewing Environment:
  1068. // Environment specified in SMPTE RP 431-2-2007
  1069. //
  1070. half3 ODT_P3DCI_48nits(half3 oces)
  1071. {
  1072. // OCES to RGB rendering space
  1073. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1074. // Apply the tonescale independently in rendering-space RGB
  1075. half3 rgbPost;
  1076. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  1077. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  1078. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  1079. // Scale luminance to linear code value
  1080. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  1081. // --- Compensate for different white point being darker --- //
  1082. // This adjustment is to correct an issue that exists in ODTs where the device
  1083. // is calibrated to a white chromaticity other than D60. In order to simulate
  1084. // D60 on such devices, unequal code values are sent to the display to achieve
  1085. // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
  1086. // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
  1087. // 0.351) the red channel is higher than green and blue to compensate for the
  1088. // "greenish" DCI white. This is the correct behavior but it means that as
  1089. // highlight increase, the red channel will hit the device maximum first and
  1090. // clip, resulting in a chromaticity shift as the green and blue channels
  1091. // continue to increase.
  1092. // To avoid this clipping error, a slight scale factor is applied to allow the
  1093. // ODTs to simulate D60 within the D65 calibration white point. However, the
  1094. // magnitude of the scale factor required for the P3DCI ODT was considered too
  1095. // large. Therefore, the scale factor was reduced and the additional required
  1096. // compression was achieved via a reshaping of the highlight rolloff in
  1097. // conjunction with the scale. The shape of this rolloff was determined
  1098. // throught subjective experiments and deemed to best reproduce the
  1099. // "character" of the highlights in the P3D60 ODT.
  1100. // Roll off highlights to avoid need for as much scaling
  1101. const half NEW_WHT = 0.918;
  1102. const half ROLL_WIDTH = 0.5;
  1103. linearCV.x = roll_white_fwd(linearCV.x, NEW_WHT, ROLL_WIDTH);
  1104. linearCV.y = roll_white_fwd(linearCV.y, NEW_WHT, ROLL_WIDTH);
  1105. linearCV.z = roll_white_fwd(linearCV.z, NEW_WHT, ROLL_WIDTH);
  1106. // Scale and clamp white to avoid casted highlights due to D60 simulation
  1107. const half SCALE = 0.96;
  1108. linearCV = min(linearCV, NEW_WHT) * SCALE;
  1109. // Convert to display primary encoding
  1110. // Rendering space RGB to XYZ
  1111. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1112. // CIE XYZ to display primaries
  1113. linearCV = mul(XYZ_2_DCIP3_MAT, XYZ);
  1114. // Handle out-of-gamut values
  1115. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  1116. linearCV = saturate(linearCV);
  1117. // Encode linear code values with transfer function
  1118. const half DISPGAMMA = 2.6;
  1119. half3 outputCV = pow(linearCV, 1.0 / DISPGAMMA);
  1120. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  1121. // by default which will result in double perceptual encoding, thus for now if one want to use
  1122. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  1123. // compensate for Unity default behaviour.
  1124. return outputCV;
  1125. }
  1126. #endif // __ACES__