ACES.hlsl 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314
  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 "Common.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 k = b * (b - g) + g * (g - r) + r * (r - b);
  285. #if defined(SHADER_API_SWITCH)
  286. half chroma = k == 0.0 ? 0.0 : sqrt(k); // Fix NaN on Nintendo Switch (should not happen in theory).
  287. #else
  288. half chroma = sqrt(k);
  289. #endif
  290. return (b + g + r + ycRadiusWeight * chroma) / 3.0;
  291. }
  292. half rgb_2_hue(half3 rgb)
  293. {
  294. // Returns a geometric hue angle in degrees (0-360) based on RGB values.
  295. // For neutral colors, hue is undefined and the function will return a quiet NaN value.
  296. half hue;
  297. if (rgb.x == rgb.y && rgb.y == rgb.z)
  298. hue = 0.0; // RGB triplets where RGB are equal have an undefined hue
  299. else
  300. hue = (180.0 / PI) * atan2(sqrt(3.0) * (rgb.y - rgb.z), 2.0 * rgb.x - rgb.y - rgb.z);
  301. if (hue < 0.0) hue = hue + 360.0;
  302. return hue;
  303. }
  304. half center_hue(half hue, half centerH)
  305. {
  306. half hueCentered = hue - centerH;
  307. if (hueCentered < -180.0) hueCentered = hueCentered + 360.0;
  308. else if (hueCentered > 180.0) hueCentered = hueCentered - 360.0;
  309. return hueCentered;
  310. }
  311. half sigmoid_shaper(half x)
  312. {
  313. // Sigmoid function in the range 0 to 1 spanning -2 to +2.
  314. half t = max(1.0 - abs(x / 2.0), 0.0);
  315. half y = 1.0 + FastSign(x) * (1.0 - t * t);
  316. return y / 2.0;
  317. }
  318. half glow_fwd(half ycIn, half glowGainIn, half glowMid)
  319. {
  320. half glowGainOut;
  321. if (ycIn <= 2.0 / 3.0 * glowMid)
  322. glowGainOut = glowGainIn;
  323. else if (ycIn >= 2.0 * glowMid)
  324. glowGainOut = 0.0;
  325. else
  326. glowGainOut = glowGainIn * (glowMid / ycIn - 1.0 / 2.0);
  327. return glowGainOut;
  328. }
  329. /*
  330. half cubic_basis_shaper
  331. (
  332. half x,
  333. half w // full base width of the shaper function (in degrees)
  334. )
  335. {
  336. half M[4][4] = {
  337. { -1.0 / 6, 3.0 / 6, -3.0 / 6, 1.0 / 6 },
  338. { 3.0 / 6, -6.0 / 6, 3.0 / 6, 0.0 / 6 },
  339. { -3.0 / 6, 0.0 / 6, 3.0 / 6, 0.0 / 6 },
  340. { 1.0 / 6, 4.0 / 6, 1.0 / 6, 0.0 / 6 }
  341. };
  342. half knots[5] = {
  343. -w / 2.0,
  344. -w / 4.0,
  345. 0.0,
  346. w / 4.0,
  347. w / 2.0
  348. };
  349. half y = 0.0;
  350. if ((x > knots[0]) && (x < knots[4]))
  351. {
  352. half knot_coord = (x - knots[0]) * 4.0 / w;
  353. int j = knot_coord;
  354. half t = knot_coord - j;
  355. half monomials[4] = { t*t*t, t*t, t, 1.0 };
  356. // (if/else structure required for compatibility with CTL < v1.5.)
  357. if (j == 3)
  358. {
  359. y = monomials[0] * M[0][0] + monomials[1] * M[1][0] +
  360. monomials[2] * M[2][0] + monomials[3] * M[3][0];
  361. }
  362. else if (j == 2)
  363. {
  364. y = monomials[0] * M[0][1] + monomials[1] * M[1][1] +
  365. monomials[2] * M[2][1] + monomials[3] * M[3][1];
  366. }
  367. else if (j == 1)
  368. {
  369. y = monomials[0] * M[0][2] + monomials[1] * M[1][2] +
  370. monomials[2] * M[2][2] + monomials[3] * M[3][2];
  371. }
  372. else if (j == 0)
  373. {
  374. y = monomials[0] * M[0][3] + monomials[1] * M[1][3] +
  375. monomials[2] * M[2][3] + monomials[3] * M[3][3];
  376. }
  377. else
  378. {
  379. y = 0.0;
  380. }
  381. }
  382. return y * 3.0 / 2.0;
  383. }
  384. */
  385. static const half3x3 M = {
  386. 0.5, -1.0, 0.5,
  387. -1.0, 1.0, 0.0,
  388. 0.5, 0.5, 0.0
  389. };
  390. half segmented_spline_c5_fwd(half x)
  391. {
  392. 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)
  393. 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)
  394. const half2 minPoint = half2(0.18 * exp2(-15.0), 0.0001); // {luminance, luminance} linear extension below this
  395. const half2 midPoint = half2(0.18, 0.48); // {luminance, luminance}
  396. const half2 maxPoint = half2(0.18 * exp2(18.0), 10000.0); // {luminance, luminance} linear extension above this
  397. const half slopeLow = 0.0; // log-log slope of low linear extension
  398. const half slopeHigh = 0.0; // log-log slope of high linear extension
  399. const int N_KNOTS_LOW = 4;
  400. const int N_KNOTS_HIGH = 4;
  401. // Check for negatives or zero before taking the log. If negative or zero,
  402. // set to ACESMIN.1
  403. float xCheck = x;
  404. if (xCheck <= 0.0) xCheck = 0.00006103515; // = pow(2.0, -14.0);
  405. half logx = log10(xCheck);
  406. half logy;
  407. if (logx <= log10(minPoint.x))
  408. {
  409. logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
  410. }
  411. else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
  412. {
  413. half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
  414. int j = knot_coord;
  415. half t = knot_coord - j;
  416. half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
  417. half3 monomials = half3(t * t, t, 1.0);
  418. logy = dot(monomials, mul(M, cf));
  419. }
  420. else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
  421. {
  422. half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
  423. int j = knot_coord;
  424. half t = knot_coord - j;
  425. half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
  426. half3 monomials = half3(t * t, t, 1.0);
  427. logy = dot(monomials, mul(M, cf));
  428. }
  429. else
  430. { //if (logIn >= log10(maxPoint.x)) {
  431. logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
  432. }
  433. return pow(10.0, logy);
  434. }
  435. half segmented_spline_c9_fwd(half x)
  436. {
  437. 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)
  438. 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)
  439. const half2 minPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(-6.5)), 0.02); // {luminance, luminance} linear extension below this
  440. const half2 midPoint = half2(segmented_spline_c5_fwd(0.18), 4.8); // {luminance, luminance}
  441. const half2 maxPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(6.5)), 48.0); // {luminance, luminance} linear extension above this
  442. const half slopeLow = 0.0; // log-log slope of low linear extension
  443. const half slopeHigh = 0.04; // log-log slope of high linear extension
  444. const int N_KNOTS_LOW = 8;
  445. const int N_KNOTS_HIGH = 8;
  446. // Check for negatives or zero before taking the log. If negative or zero,
  447. // set to OCESMIN.
  448. half xCheck = x;
  449. if (xCheck <= 0.0) xCheck = 1e-4;
  450. half logx = log10(xCheck);
  451. half logy;
  452. if (logx <= log10(minPoint.x))
  453. {
  454. logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
  455. }
  456. else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
  457. {
  458. half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
  459. int j = knot_coord;
  460. half t = knot_coord - j;
  461. half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
  462. half3 monomials = half3(t * t, t, 1.0);
  463. logy = dot(monomials, mul(M, cf));
  464. }
  465. else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
  466. {
  467. half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
  468. int j = knot_coord;
  469. half t = knot_coord - j;
  470. half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
  471. half3 monomials = half3(t * t, t, 1.0);
  472. logy = dot(monomials, mul(M, cf));
  473. }
  474. else
  475. { //if (logIn >= log10(maxPoint.x)) {
  476. logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
  477. }
  478. return pow(10.0, logy);
  479. }
  480. static const half RRT_GLOW_GAIN = 0.05;
  481. static const half RRT_GLOW_MID = 0.08;
  482. static const half RRT_RED_SCALE = 0.82;
  483. static const half RRT_RED_PIVOT = 0.03;
  484. static const half RRT_RED_HUE = 0.0;
  485. static const half RRT_RED_WIDTH = 135.0;
  486. static const half RRT_SAT_FACTOR = 0.96;
  487. half3 RRT(half3 aces)
  488. {
  489. // --- Glow module --- //
  490. half saturation = rgb_2_saturation(aces);
  491. half ycIn = rgb_2_yc(aces);
  492. half s = sigmoid_shaper((saturation - 0.4) / 0.2);
  493. half addedGlow = 1.0 + glow_fwd(ycIn, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
  494. aces *= addedGlow;
  495. // --- Red modifier --- //
  496. half hue = rgb_2_hue(aces);
  497. half centeredHue = center_hue(hue, RRT_RED_HUE);
  498. half hueWeight;
  499. {
  500. //hueWeight = cubic_basis_shaper(centeredHue, RRT_RED_WIDTH);
  501. hueWeight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centeredHue / RRT_RED_WIDTH));
  502. hueWeight *= hueWeight;
  503. }
  504. aces.r += hueWeight * saturation * (RRT_RED_PIVOT - aces.r) * (1.0 - RRT_RED_SCALE);
  505. // --- ACES to RGB rendering space --- //
  506. aces = clamp(aces, 0.0, HALF_MAX); // avoids saturated negative colors from becoming positive in the matrix
  507. half3 rgbPre = mul(AP0_2_AP1_MAT, aces);
  508. rgbPre = clamp(rgbPre, 0, HALF_MAX);
  509. // --- Global desaturation --- //
  510. //rgbPre = mul(RRT_SAT_MAT, rgbPre);
  511. rgbPre = lerp(dot(rgbPre, AP1_RGB2Y).xxx, rgbPre, RRT_SAT_FACTOR.xxx);
  512. // --- Apply the tonescale independently in rendering-space RGB --- //
  513. half3 rgbPost;
  514. rgbPost.x = segmented_spline_c5_fwd(rgbPre.x);
  515. rgbPost.y = segmented_spline_c5_fwd(rgbPre.y);
  516. rgbPost.z = segmented_spline_c5_fwd(rgbPre.z);
  517. // --- RGB rendering space to OCES --- //
  518. half3 rgbOces = mul(AP1_2_AP0_MAT, rgbPost);
  519. return rgbOces;
  520. }
  521. //
  522. // Output Device Transform
  523. //
  524. half3 Y_2_linCV(half3 Y, half Ymax, half Ymin)
  525. {
  526. return (Y - Ymin) / (Ymax - Ymin);
  527. }
  528. half3 XYZ_2_xyY(half3 XYZ)
  529. {
  530. half divisor = max(dot(XYZ, (1.0).xxx), 1e-4);
  531. return half3(XYZ.xy / divisor, XYZ.y);
  532. }
  533. half3 xyY_2_XYZ(half3 xyY)
  534. {
  535. half m = xyY.z / max(xyY.y, 1e-4);
  536. half3 XYZ = half3(xyY.xz, (1.0 - xyY.x - xyY.y));
  537. XYZ.xz *= m;
  538. return XYZ;
  539. }
  540. static const half DIM_SURROUND_GAMMA = 0.9811;
  541. half3 darkSurround_to_dimSurround(half3 linearCV)
  542. {
  543. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  544. half3 xyY = XYZ_2_xyY(XYZ);
  545. xyY.z = clamp(xyY.z, 0.0, HALF_MAX);
  546. xyY.z = pow(xyY.z, DIM_SURROUND_GAMMA);
  547. XYZ = xyY_2_XYZ(xyY);
  548. return mul(XYZ_2_AP1_MAT, XYZ);
  549. }
  550. half moncurve_r(half y, half gamma, half offs)
  551. {
  552. // Reverse monitor curve
  553. half x;
  554. const half yb = pow(offs * gamma / ((gamma - 1.0) * (1.0 + offs)), gamma);
  555. const half rs = pow((gamma - 1.0) / offs, gamma - 1.0) * pow((1.0 + offs) / gamma, gamma);
  556. if (y >= yb)
  557. x = (1.0 + offs) * pow(y, 1.0 / gamma) - offs;
  558. else
  559. x = y * rs;
  560. return x;
  561. }
  562. half bt1886_r(half L, half gamma, half Lw, half Lb)
  563. {
  564. // The reference EOTF specified in Rec. ITU-R BT.1886
  565. // L = a(max[(V+b),0])^g
  566. half a = pow(pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma), gamma);
  567. half b = pow(Lb, 1.0 / gamma) / (pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma));
  568. half V = pow(max(L / a, 0.0), 1.0 / gamma) - b;
  569. return V;
  570. }
  571. half roll_white_fwd(
  572. half x, // color value to adjust (white scaled to around 1.0)
  573. half new_wht, // white adjustment (e.g. 0.9 for 10% darkening)
  574. half width // adjusted width (e.g. 0.25 for top quarter of the tone scale)
  575. )
  576. {
  577. const half x0 = -1.0;
  578. const half x1 = x0 + width;
  579. const half y0 = -new_wht;
  580. const half y1 = x1;
  581. const half m1 = (x1 - x0);
  582. const half a = y0 - y1 + m1;
  583. const half b = 2.0 * (y1 - y0) - m1;
  584. const half c = y0;
  585. const half t = (-x - x0) / (x1 - x0);
  586. half o = 0.0;
  587. if (t < 0.0)
  588. o = -(t * b + c);
  589. else if (t > 1.0)
  590. o = x;
  591. else
  592. o = -((t * a + b) * t + c);
  593. return o;
  594. }
  595. half3 linear_to_sRGB(half3 x)
  596. {
  597. return (x <= 0.0031308 ? (x * 12.9232102) : 1.055 * pow(x, 1.0 / 2.4) - 0.055);
  598. }
  599. half3 linear_to_bt1886(half3 x, half gamma, half Lw, half Lb)
  600. {
  601. // Good enough approximation for now, may consider using the exact formula instead
  602. // TODO: Experiment
  603. return pow(max(x, 0.0), 1.0 / 2.4);
  604. // Correct implementation (Reference EOTF specified in Rec. ITU-R BT.1886) :
  605. // L = a(max[(V+b),0])^g
  606. half invgamma = 1.0 / gamma;
  607. half p_Lw = pow(Lw, invgamma);
  608. half p_Lb = pow(Lb, invgamma);
  609. half3 a = pow(p_Lw - p_Lb, gamma).xxx;
  610. half3 b = (p_Lb / p_Lw - p_Lb).xxx;
  611. half3 V = pow(max(x / a, 0.0), invgamma.xxx) - b;
  612. return V;
  613. }
  614. static const half CINEMA_WHITE = 48.0;
  615. static const half CINEMA_BLACK = CINEMA_WHITE / 2400.0;
  616. static const half ODT_SAT_FACTOR = 0.93;
  617. // <ACEStransformID>ODT.Academy.RGBmonitor_100nits_dim.a1.0.3</ACEStransformID>
  618. // <ACESuserName>ACES 1.0 Output - sRGB</ACESuserName>
  619. //
  620. // Output Device Transform - RGB computer monitor
  621. //
  622. //
  623. // Summary :
  624. // This transform is intended for mapping OCES onto a desktop computer monitor
  625. // typical of those used in motion picture visual effects production. These
  626. // monitors may occasionally be referred to as "sRGB" displays, however, the
  627. // monitor for which this transform is designed does not exactly match the
  628. // specifications in IEC 61966-2-1:1999.
  629. //
  630. // The assumed observer adapted white is D65, and the viewing environment is
  631. // that of a dim surround.
  632. //
  633. // The monitor specified is intended to be more typical of those found in
  634. // visual effects production.
  635. //
  636. // Device Primaries :
  637. // Primaries are those specified in Rec. ITU-R BT.709
  638. // CIE 1931 chromaticities: x y Y
  639. // Red: 0.64 0.33
  640. // Green: 0.3 0.6
  641. // Blue: 0.15 0.06
  642. // White: 0.3127 0.329 100 cd/m^2
  643. //
  644. // Display EOTF :
  645. // The reference electro-optical transfer function specified in
  646. // IEC 61966-2-1:1999.
  647. //
  648. // Signal Range:
  649. // This transform outputs full range code values.
  650. //
  651. // Assumed observer adapted white point:
  652. // CIE 1931 chromaticities: x y
  653. // 0.3127 0.329
  654. //
  655. // Viewing Environment:
  656. // This ODT has a compensation for viewing environment variables more typical
  657. // of those associated with video mastering.
  658. //
  659. half3 ODT_RGBmonitor_100nits_dim(half3 oces)
  660. {
  661. // OCES to RGB rendering space
  662. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  663. // Apply the tonescale independently in rendering-space RGB
  664. half3 rgbPost;
  665. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  666. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  667. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  668. // Scale luminance to linear code value
  669. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  670. // Apply gamma adjustment to compensate for dim surround
  671. linearCV = darkSurround_to_dimSurround(linearCV);
  672. // Apply desaturation to compensate for luminance difference
  673. //linearCV = mul(ODT_SAT_MAT, linearCV);
  674. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  675. // Convert to display primary encoding
  676. // Rendering space RGB to XYZ
  677. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  678. // Apply CAT from ACES white point to assumed observer adapted white point
  679. XYZ = mul(D60_2_D65_CAT, XYZ);
  680. // CIE XYZ to display primaries
  681. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  682. // Handle out-of-gamut values
  683. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  684. linearCV = saturate(linearCV);
  685. // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
  686. // with sRGB opto-electrical transfer function (OETF).
  687. /*
  688. // Encode linear code values with transfer function
  689. half3 outputCV;
  690. // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
  691. const half DISPGAMMA = 2.4;
  692. const half OFFSET = 0.055;
  693. outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
  694. outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
  695. outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
  696. outputCV = linear_to_sRGB(linearCV);
  697. */
  698. // Unity already draws to a sRGB target
  699. return linearCV;
  700. }
  701. // <ACEStransformID>ODT.Academy.RGBmonitor_D60sim_100nits_dim.a1.0.3</ACEStransformID>
  702. // <ACESuserName>ACES 1.0 Output - sRGB (D60 sim.)</ACESuserName>
  703. //
  704. // Output Device Transform - RGB computer monitor (D60 simulation)
  705. //
  706. //
  707. // Summary :
  708. // This transform is intended for mapping OCES onto a desktop computer monitor
  709. // typical of those used in motion picture visual effects production. These
  710. // monitors may occasionally be referred to as "sRGB" displays, however, the
  711. // monitor for which this transform is designed does not exactly match the
  712. // specifications in IEC 61966-2-1:1999.
  713. //
  714. // The assumed observer adapted white is D60, and the viewing environment is
  715. // that of a dim surround.
  716. //
  717. // The monitor specified is intended to be more typical of those found in
  718. // visual effects production.
  719. //
  720. // Device Primaries :
  721. // Primaries are those specified in Rec. ITU-R BT.709
  722. // CIE 1931 chromaticities: x y Y
  723. // Red: 0.64 0.33
  724. // Green: 0.3 0.6
  725. // Blue: 0.15 0.06
  726. // White: 0.3127 0.329 100 cd/m^2
  727. //
  728. // Display EOTF :
  729. // The reference electro-optical transfer function specified in
  730. // IEC 61966-2-1:1999.
  731. //
  732. // Signal Range:
  733. // This transform outputs full range code values.
  734. //
  735. // Assumed observer adapted white point:
  736. // CIE 1931 chromaticities: x y
  737. // 0.32168 0.33767
  738. //
  739. // Viewing Environment:
  740. // This ODT has a compensation for viewing environment variables more typical
  741. // of those associated with video mastering.
  742. //
  743. half3 ODT_RGBmonitor_D60sim_100nits_dim(half3 oces)
  744. {
  745. // OCES to RGB rendering space
  746. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  747. // Apply the tonescale independently in rendering-space RGB
  748. half3 rgbPost;
  749. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  750. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  751. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  752. // Scale luminance to linear code value
  753. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  754. // --- Compensate for different white point being darker --- //
  755. // This adjustment is to correct an issue that exists in ODTs where the device
  756. // is calibrated to a white chromaticity other than D60. In order to simulate
  757. // D60 on such devices, unequal code values are sent to the display to achieve
  758. // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
  759. // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
  760. // 0.351) the red channel is higher than green and blue to compensate for the
  761. // "greenish" DCI white. This is the correct behavior but it means that as
  762. // highlight increase, the red channel will hit the device maximum first and
  763. // clip, resulting in a chromaticity shift as the green and blue channels
  764. // continue to increase.
  765. // To avoid this clipping error, a slight scale factor is applied to allow the
  766. // ODTs to simulate D60 within the D65 calibration white point.
  767. // Scale and clamp white to avoid casted highlights due to D60 simulation
  768. const half SCALE = 0.955;
  769. linearCV = min(linearCV, 1.0) * SCALE;
  770. // Apply gamma adjustment to compensate for dim surround
  771. linearCV = darkSurround_to_dimSurround(linearCV);
  772. // Apply desaturation to compensate for luminance difference
  773. //linearCV = mul(ODT_SAT_MAT, linearCV);
  774. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  775. // Convert to display primary encoding
  776. // Rendering space RGB to XYZ
  777. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  778. // CIE XYZ to display primaries
  779. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  780. // Handle out-of-gamut values
  781. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  782. linearCV = saturate(linearCV);
  783. // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
  784. // with sRGB opto-electrical transfer function (OETF).
  785. /*
  786. // Encode linear code values with transfer function
  787. half3 outputCV;
  788. // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
  789. const half DISPGAMMA = 2.4;
  790. const half OFFSET = 0.055;
  791. outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
  792. outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
  793. outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
  794. outputCV = linear_to_sRGB(linearCV);
  795. */
  796. // Unity already draws to a sRGB target
  797. return linearCV;
  798. }
  799. // <ACEStransformID>ODT.Academy.Rec709_100nits_dim.a1.0.3</ACEStransformID>
  800. // <ACESuserName>ACES 1.0 Output - Rec.709</ACESuserName>
  801. //
  802. // Output Device Transform - Rec709
  803. //
  804. //
  805. // Summary :
  806. // This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
  807. // that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
  808. // adapted white is D65, and the viewing environment is a dim surround.
  809. //
  810. // A possible use case for this transform would be HDTV/video mastering.
  811. //
  812. // Device Primaries :
  813. // Primaries are those specified in Rec. ITU-R BT.709
  814. // CIE 1931 chromaticities: x y Y
  815. // Red: 0.64 0.33
  816. // Green: 0.3 0.6
  817. // Blue: 0.15 0.06
  818. // White: 0.3127 0.329 100 cd/m^2
  819. //
  820. // Display EOTF :
  821. // The reference electro-optical transfer function specified in
  822. // Rec. ITU-R BT.1886.
  823. //
  824. // Signal Range:
  825. // By default, this transform outputs full range code values. If instead a
  826. // SMPTE "legal" signal is desired, there is a runtime flag to output
  827. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  828. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  829. //
  830. // Assumed observer adapted white point:
  831. // CIE 1931 chromaticities: x y
  832. // 0.3127 0.329
  833. //
  834. // Viewing Environment:
  835. // This ODT has a compensation for viewing environment variables more typical
  836. // of those associated with video mastering.
  837. //
  838. half3 ODT_Rec709_100nits_dim(half3 oces)
  839. {
  840. // OCES to RGB rendering space
  841. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  842. // Apply the tonescale independently in rendering-space RGB
  843. half3 rgbPost;
  844. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  845. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  846. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  847. // Scale luminance to linear code value
  848. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  849. // Apply gamma adjustment to compensate for dim surround
  850. linearCV = darkSurround_to_dimSurround(linearCV);
  851. // Apply desaturation to compensate for luminance difference
  852. //linearCV = mul(ODT_SAT_MAT, linearCV);
  853. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  854. // Convert to display primary encoding
  855. // Rendering space RGB to XYZ
  856. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  857. // Apply CAT from ACES white point to assumed observer adapted white point
  858. XYZ = mul(D60_2_D65_CAT, XYZ);
  859. // CIE XYZ to display primaries
  860. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  861. // Handle out-of-gamut values
  862. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  863. linearCV = saturate(linearCV);
  864. // Encode linear code values with transfer function
  865. const half DISPGAMMA = 2.4;
  866. const half L_W = 1.0;
  867. const half L_B = 0.0;
  868. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  869. // TODO: Implement support for legal range.
  870. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  871. // by default which will result in double perceptual encoding, thus for now if one want to use
  872. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  873. // compensate for Unity default behaviour.
  874. return outputCV;
  875. }
  876. // <ACEStransformID>ODT.Academy.Rec709_D60sim_100nits_dim.a1.0.3</ACEStransformID>
  877. // <ACESuserName>ACES 1.0 Output - Rec.709 (D60 sim.)</ACESuserName>
  878. //
  879. // Output Device Transform - Rec709 (D60 simulation)
  880. //
  881. //
  882. // Summary :
  883. // This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
  884. // that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
  885. // adapted white is D60, and the viewing environment is a dim surround.
  886. //
  887. // A possible use case for this transform would be cinema "soft-proofing".
  888. //
  889. // Device Primaries :
  890. // Primaries are those specified in Rec. ITU-R BT.709
  891. // CIE 1931 chromaticities: x y Y
  892. // Red: 0.64 0.33
  893. // Green: 0.3 0.6
  894. // Blue: 0.15 0.06
  895. // White: 0.3127 0.329 100 cd/m^2
  896. //
  897. // Display EOTF :
  898. // The reference electro-optical transfer function specified in
  899. // Rec. ITU-R BT.1886.
  900. //
  901. // Signal Range:
  902. // By default, this transform outputs full range code values. If instead a
  903. // SMPTE "legal" signal is desired, there is a runtime flag to output
  904. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  905. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  906. //
  907. // Assumed observer adapted white point:
  908. // CIE 1931 chromaticities: x y
  909. // 0.32168 0.33767
  910. //
  911. // Viewing Environment:
  912. // This ODT has a compensation for viewing environment variables more typical
  913. // of those associated with video mastering.
  914. //
  915. half3 ODT_Rec709_D60sim_100nits_dim(half3 oces)
  916. {
  917. // OCES to RGB rendering space
  918. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  919. // Apply the tonescale independently in rendering-space RGB
  920. half3 rgbPost;
  921. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  922. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  923. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  924. // Scale luminance to linear code value
  925. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  926. // --- Compensate for different white point being darker --- //
  927. // This adjustment is to correct an issue that exists in ODTs where the device
  928. // is calibrated to a white chromaticity other than D60. In order to simulate
  929. // D60 on such devices, unequal code values must be sent to the display to achieve
  930. // the chromaticities of D60. More specifically, in order to produce D60 on a device
  931. // calibrated to a D65 white point (i.e. equal code values yield CIE x,y
  932. // chromaticities of 0.3127, 0.329) the red channel must be slightly higher than
  933. // that of green and blue in order to compensate for the relatively more "blue-ish"
  934. // D65 white. This unequalness of color channels is the correct behavior but it
  935. // means that as neutral highlights increase, the red channel will hit the
  936. // device maximum first and clip, resulting in a small chromaticity shift as the
  937. // green and blue channels continue to increase to their maximums.
  938. // To avoid this clipping error, a slight scale factor is applied to allow the
  939. // ODTs to simulate D60 within the D65 calibration white point.
  940. // Scale and clamp white to avoid casted highlights due to D60 simulation
  941. const half SCALE = 0.955;
  942. linearCV = min(linearCV, 1.0) * SCALE;
  943. // Apply gamma adjustment to compensate for dim surround
  944. linearCV = darkSurround_to_dimSurround(linearCV);
  945. // Apply desaturation to compensate for luminance difference
  946. //linearCV = mul(ODT_SAT_MAT, linearCV);
  947. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  948. // Convert to display primary encoding
  949. // Rendering space RGB to XYZ
  950. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  951. // CIE XYZ to display primaries
  952. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  953. // Handle out-of-gamut values
  954. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  955. linearCV = saturate(linearCV);
  956. // Encode linear code values with transfer function
  957. const half DISPGAMMA = 2.4;
  958. const half L_W = 1.0;
  959. const half L_B = 0.0;
  960. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  961. // TODO: Implement support for legal range.
  962. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  963. // by default which will result in double perceptual encoding, thus for now if one want to use
  964. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  965. // compensate for Unity default behaviour.
  966. return outputCV;
  967. }
  968. // <ACEStransformID>ODT.Academy.Rec2020_100nits_dim.a1.0.3</ACEStransformID>
  969. // <ACESuserName>ACES 1.0 Output - Rec.2020</ACESuserName>
  970. //
  971. // Output Device Transform - Rec2020
  972. //
  973. //
  974. // Summary :
  975. // This transform is intended for mapping OCES onto a Rec.2020 broadcast
  976. // monitor that is calibrated to a D65 white point at 100 cd/m^2. The assumed
  977. // observer adapted white is D65, and the viewing environment is that of a dim
  978. // surround.
  979. //
  980. // A possible use case for this transform would be UHDTV/video mastering.
  981. //
  982. // Device Primaries :
  983. // Primaries are those specified in Rec. ITU-R BT.2020
  984. // CIE 1931 chromaticities: x y Y
  985. // Red: 0.708 0.292
  986. // Green: 0.17 0.797
  987. // Blue: 0.131 0.046
  988. // White: 0.3127 0.329 100 cd/m^2
  989. //
  990. // Display EOTF :
  991. // The reference electro-optical transfer function specified in
  992. // Rec. ITU-R BT.1886.
  993. //
  994. // Signal Range:
  995. // By default, this transform outputs full range code values. If instead a
  996. // SMPTE "legal" signal is desired, there is a runtime flag to output
  997. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  998. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  999. //
  1000. // Assumed observer adapted white point:
  1001. // CIE 1931 chromaticities: x y
  1002. // 0.3127 0.329
  1003. //
  1004. // Viewing Environment:
  1005. // This ODT has a compensation for viewing environment variables more typical
  1006. // of those associated with video mastering.
  1007. //
  1008. half3 ODT_Rec2020_100nits_dim(half3 oces)
  1009. {
  1010. // OCES to RGB rendering space
  1011. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1012. // Apply the tonescale independently in rendering-space RGB
  1013. half3 rgbPost;
  1014. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  1015. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  1016. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  1017. // Scale luminance to linear code value
  1018. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  1019. // Apply gamma adjustment to compensate for dim surround
  1020. linearCV = darkSurround_to_dimSurround(linearCV);
  1021. // Apply desaturation to compensate for luminance difference
  1022. //linearCV = mul(ODT_SAT_MAT, linearCV);
  1023. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  1024. // Convert to display primary encoding
  1025. // Rendering space RGB to XYZ
  1026. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1027. // Apply CAT from ACES white point to assumed observer adapted white point
  1028. XYZ = mul(D60_2_D65_CAT, XYZ);
  1029. // CIE XYZ to display primaries
  1030. linearCV = mul(XYZ_2_REC2020_MAT, XYZ);
  1031. // Handle out-of-gamut values
  1032. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  1033. linearCV = saturate(linearCV);
  1034. // Encode linear code values with transfer function
  1035. const half DISPGAMMA = 2.4;
  1036. const half L_W = 1.0;
  1037. const half L_B = 0.0;
  1038. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  1039. // TODO: Implement support for legal range.
  1040. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  1041. // by default which will result in double perceptual encoding, thus for now if one want to use
  1042. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  1043. // compensate for Unity default behaviour.
  1044. return outputCV;
  1045. }
  1046. // <ACEStransformID>ODT.Academy.P3DCI_48nits.a1.0.3</ACEStransformID>
  1047. // <ACESuserName>ACES 1.0 Output - P3-DCI</ACESuserName>
  1048. //
  1049. // Output Device Transform - P3DCI (D60 Simulation)
  1050. //
  1051. //
  1052. // Summary :
  1053. // This transform is intended for mapping OCES onto a P3 digital cinema
  1054. // projector that is calibrated to a DCI white point at 48 cd/m^2. The assumed
  1055. // observer adapted white is D60, and the viewing environment is that of a dark
  1056. // theater.
  1057. //
  1058. // Device Primaries :
  1059. // CIE 1931 chromaticities: x y Y
  1060. // Red: 0.68 0.32
  1061. // Green: 0.265 0.69
  1062. // Blue: 0.15 0.06
  1063. // White: 0.314 0.351 48 cd/m^2
  1064. //
  1065. // Display EOTF :
  1066. // Gamma: 2.6
  1067. //
  1068. // Assumed observer adapted white point:
  1069. // CIE 1931 chromaticities: x y
  1070. // 0.32168 0.33767
  1071. //
  1072. // Viewing Environment:
  1073. // Environment specified in SMPTE RP 431-2-2007
  1074. //
  1075. half3 ODT_P3DCI_48nits(half3 oces)
  1076. {
  1077. // OCES to RGB rendering space
  1078. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1079. // Apply the tonescale independently in rendering-space RGB
  1080. half3 rgbPost;
  1081. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
  1082. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
  1083. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
  1084. // Scale luminance to linear code value
  1085. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  1086. // --- Compensate for different white point being darker --- //
  1087. // This adjustment is to correct an issue that exists in ODTs where the device
  1088. // is calibrated to a white chromaticity other than D60. In order to simulate
  1089. // D60 on such devices, unequal code values are sent to the display to achieve
  1090. // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
  1091. // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
  1092. // 0.351) the red channel is higher than green and blue to compensate for the
  1093. // "greenish" DCI white. This is the correct behavior but it means that as
  1094. // highlight increase, the red channel will hit the device maximum first and
  1095. // clip, resulting in a chromaticity shift as the green and blue channels
  1096. // continue to increase.
  1097. // To avoid this clipping error, a slight scale factor is applied to allow the
  1098. // ODTs to simulate D60 within the D65 calibration white point. However, the
  1099. // magnitude of the scale factor required for the P3DCI ODT was considered too
  1100. // large. Therefore, the scale factor was reduced and the additional required
  1101. // compression was achieved via a reshaping of the highlight rolloff in
  1102. // conjunction with the scale. The shape of this rolloff was determined
  1103. // throught subjective experiments and deemed to best reproduce the
  1104. // "character" of the highlights in the P3D60 ODT.
  1105. // Roll off highlights to avoid need for as much scaling
  1106. const half NEW_WHT = 0.918;
  1107. const half ROLL_WIDTH = 0.5;
  1108. linearCV.x = roll_white_fwd(linearCV.x, NEW_WHT, ROLL_WIDTH);
  1109. linearCV.y = roll_white_fwd(linearCV.y, NEW_WHT, ROLL_WIDTH);
  1110. linearCV.z = roll_white_fwd(linearCV.z, NEW_WHT, ROLL_WIDTH);
  1111. // Scale and clamp white to avoid casted highlights due to D60 simulation
  1112. const half SCALE = 0.96;
  1113. linearCV = min(linearCV, NEW_WHT) * SCALE;
  1114. // Convert to display primary encoding
  1115. // Rendering space RGB to XYZ
  1116. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1117. // CIE XYZ to display primaries
  1118. linearCV = mul(XYZ_2_DCIP3_MAT, XYZ);
  1119. // Handle out-of-gamut values
  1120. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  1121. linearCV = saturate(linearCV);
  1122. // Encode linear code values with transfer function
  1123. const half DISPGAMMA = 2.6;
  1124. half3 outputCV = pow(linearCV, 1.0 / DISPGAMMA);
  1125. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  1126. // by default which will result in double perceptual encoding, thus for now if one want to use
  1127. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  1128. // compensate for Unity default behaviour.
  1129. return outputCV;
  1130. }
  1131. #endif // __ACES__