ACES.hlsl 46 KB

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