Bez popisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ACES.hlsl 53KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521
  1. #ifndef __ACES__
  2. #define __ACES__
  3. #if SHADER_API_MOBILE || SHADER_API_GLES3 || SHADER_API_SWITCH || defined(UNITY_UNIFIED_SHADER_PRECISION_MODEL)
  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 half3x3 XYZ_2_P3D65_MAT = {
  121. 2.4934969119, -0.9313836179, -0.4027107845,
  122. -0.8294889696, 1.7626640603, 0.0236246858,
  123. 0.0358458302, -0.0761723893, 0.9568845240
  124. };
  125. static const half3 AP1_RGB2Y = half3(0.272229, 0.674082, 0.0536895);
  126. static const half3x3 RRT_SAT_MAT = {
  127. 0.9708890, 0.0269633, 0.00214758,
  128. 0.0108892, 0.9869630, 0.00214758,
  129. 0.0108892, 0.0269633, 0.96214800
  130. };
  131. static const half3x3 ODT_SAT_MAT = {
  132. 0.949056, 0.0471857, 0.00375827,
  133. 0.019056, 0.9771860, 0.00375827,
  134. 0.019056, 0.0471857, 0.93375800
  135. };
  136. static const half3x3 D60_2_D65_CAT = {
  137. 0.98722400, -0.00611327, 0.0159533,
  138. -0.00759836, 1.00186000, 0.0053302,
  139. 0.00307257, -0.00509595, 1.0816800
  140. };
  141. //
  142. // Unity to ACES
  143. //
  144. // converts Unity raw (sRGB primaries) to
  145. // ACES2065-1 (AP0 w/ linear encoding)
  146. //
  147. half3 unity_to_ACES(half3 x)
  148. {
  149. x = mul(sRGB_2_AP0, x);
  150. return x;
  151. }
  152. //
  153. // ACES to Unity
  154. //
  155. // converts ACES2065-1 (AP0 w/ linear encoding)
  156. // Unity raw (sRGB primaries) to
  157. //
  158. half3 ACES_to_unity(half3 x)
  159. {
  160. x = mul(AP0_2_sRGB, x);
  161. return x;
  162. }
  163. //
  164. // Unity to ACEScg
  165. //
  166. // converts Unity raw (sRGB primaries) to
  167. // ACEScg (AP1 w/ linear encoding)
  168. //
  169. half3 unity_to_ACEScg(half3 x)
  170. {
  171. x = mul(sRGB_2_AP1, x);
  172. return x;
  173. }
  174. //
  175. // ACEScg to Unity
  176. //
  177. // converts ACEScg (AP1 w/ linear encoding) to
  178. // Unity raw (sRGB primaries)
  179. //
  180. half3 ACEScg_to_unity(half3 x)
  181. {
  182. x = mul(AP1_2_sRGB, x);
  183. return x;
  184. }
  185. half3 ACEScg_to_Rec2020(half3 x)
  186. {
  187. half3 xyz = mul(AP1_2_XYZ_MAT, x);
  188. return mul(XYZ_2_REC2020_MAT, xyz);
  189. }
  190. //
  191. // ACES Color Space Conversion - ACES to ACEScc
  192. //
  193. // converts ACES2065-1 (AP0 w/ linear encoding) to
  194. // ACEScc (AP1 w/ logarithmic encoding)
  195. //
  196. // This transform follows the formulas from section 4.4 in S-2014-003
  197. //
  198. half ACES_to_ACEScc(half x)
  199. {
  200. if (x <= 0.0)
  201. return -0.35828683; // = (log2(pow(2.0, -15.0) * 0.5) + 9.72) / 17.52
  202. else if (x < pow(2.0, -15.0))
  203. return (log2(pow(2.0, -16.0) + x * 0.5) + 9.72) / 17.52;
  204. else // (x >= pow(2.0, -15.0))
  205. return (log2(x) + 9.72) / 17.52;
  206. }
  207. half ACES_to_ACEScc_fast(half x)
  208. {
  209. // x is clamped to [0, HALF_MAX], skip the <= 0 check
  210. return (x < 0.00003051757) ? (log2(0.00001525878 + x * 0.5) + 9.72) / 17.52 : (log2(x) + 9.72) / 17.52;
  211. }
  212. half3 ACES_to_ACEScc(half3 x)
  213. {
  214. x = clamp(x, 0.0, HALF_MAX);
  215. // x is clamped to [0, HALF_MAX], skip the <= 0 check
  216. return half3(
  217. ACES_to_ACEScc_fast(x.r),
  218. ACES_to_ACEScc_fast(x.g),
  219. ACES_to_ACEScc_fast(x.b)
  220. );
  221. /*
  222. return half3(
  223. ACES_to_ACEScc(x.r),
  224. ACES_to_ACEScc(x.g),
  225. ACES_to_ACEScc(x.b)
  226. );
  227. */
  228. }
  229. //
  230. // ACES Color Space Conversion - ACEScc to ACES
  231. //
  232. // converts ACEScc (AP1 w/ ACESlog encoding) to
  233. // ACES2065-1 (AP0 w/ linear encoding)
  234. //
  235. // This transform follows the formulas from section 4.4 in S-2014-003
  236. //
  237. half ACEScc_to_ACES(half x)
  238. {
  239. // TODO: Optimize me
  240. if (x < -0.3013698630) // (9.72 - 15) / 17.52
  241. return (pow(2.0, x * 17.52 - 9.72) - pow(2.0, -16.0)) * 2.0;
  242. else if (x < (log2(HALF_MAX) + 9.72) / 17.52)
  243. return pow(2.0, x * 17.52 - 9.72);
  244. else // (x >= (log2(HALF_MAX) + 9.72) / 17.52)
  245. return HALF_MAX;
  246. }
  247. half3 ACEScc_to_ACES(half3 x)
  248. {
  249. return half3(
  250. ACEScc_to_ACES(x.r),
  251. ACEScc_to_ACES(x.g),
  252. ACEScc_to_ACES(x.b)
  253. );
  254. }
  255. //
  256. // ACES Color Space Conversion - ACES to ACEScg
  257. //
  258. // converts ACES2065-1 (AP0 w/ linear encoding) to
  259. // ACEScg (AP1 w/ linear encoding)
  260. //
  261. // Uses float3 to avoid going out of half-precision bounds
  262. //
  263. float3 ACES_to_ACEScg(float3 x)
  264. {
  265. return mul(AP0_2_AP1_MAT, x);
  266. }
  267. //
  268. // ACES Color Space Conversion - ACEScg to ACES
  269. //
  270. // converts ACEScg (AP1 w/ linear encoding) to
  271. // ACES2065-1 (AP0 w/ linear encoding)
  272. //
  273. // Uses float3 to avoid going out of half-precision bounds
  274. //
  275. float3 ACEScg_to_ACES(float3 x)
  276. {
  277. return mul(AP1_2_AP0_MAT, x);
  278. }
  279. //
  280. // Reference Rendering Transform (RRT)
  281. //
  282. // Input is ACES
  283. // Output is OCES
  284. //
  285. half rgb_2_saturation(half3 rgb)
  286. {
  287. const half TINY = 1e-4;
  288. half mi = Min3(rgb.r, rgb.g, rgb.b);
  289. half ma = Max3(rgb.r, rgb.g, rgb.b);
  290. return (max(ma, TINY) - max(mi, TINY)) / max(ma, 1e-2);
  291. }
  292. half rgb_2_yc(half3 rgb)
  293. {
  294. const half ycRadiusWeight = 1.75;
  295. // Converts RGB to a luminance proxy, here called YC
  296. // YC is ~ Y + K * Chroma
  297. // Constant YC is a cone-shaped surface in RGB space, with the tip on the
  298. // neutral axis, towards white.
  299. // YC is normalized: RGB 1 1 1 maps to YC = 1
  300. //
  301. // ycRadiusWeight defaults to 1.75, although can be overridden in function
  302. // call to rgb_2_yc
  303. // ycRadiusWeight = 1 -> YC for pure cyan, magenta, yellow == YC for neutral
  304. // of same value
  305. // ycRadiusWeight = 2 -> YC for pure red, green, blue == YC for neutral of
  306. // same value.
  307. half r = rgb.x;
  308. half g = rgb.y;
  309. half b = rgb.z;
  310. half k = b * (b - g) + g * (g - r) + r * (r - b);
  311. k = max(k, 0.0); // Clamp to avoid precision issue causing k < 0, making sqrt(k) undefined
  312. #if defined(SHADER_API_SWITCH)
  313. half chroma = k == 0.0 ? 0.0 : sqrt(k); // Avoid Nan
  314. #else
  315. half chroma = sqrt(k);
  316. #endif
  317. return (b + g + r + ycRadiusWeight * chroma) / 3.0;
  318. }
  319. half rgb_2_hue(half3 rgb)
  320. {
  321. // Returns a geometric hue angle in degrees (0-360) based on RGB values.
  322. // For neutral colors, hue is undefined and the function will return a quiet NaN value.
  323. half hue;
  324. if (rgb.x == rgb.y && rgb.y == rgb.z)
  325. hue = 0.0; // RGB triplets where RGB are equal have an undefined hue
  326. else
  327. hue = (180.0 / PI) * atan2(sqrt(3.0) * (rgb.y - rgb.z), 2.0 * rgb.x - rgb.y - rgb.z);
  328. if (hue < 0.0) hue = hue + 360.0;
  329. return hue;
  330. }
  331. half center_hue(half hue, half centerH)
  332. {
  333. half hueCentered = hue - centerH;
  334. if (hueCentered < -180.0) hueCentered = hueCentered + 360.0;
  335. else if (hueCentered > 180.0) hueCentered = hueCentered - 360.0;
  336. return hueCentered;
  337. }
  338. half sigmoid_shaper(half x)
  339. {
  340. // Sigmoid function in the range 0 to 1 spanning -2 to +2.
  341. half t = max(1.0 - abs(x / 2.0), 0.0);
  342. half y = 1.0 + half(FastSign(x)) * (1.0 - t * t);
  343. return y / 2.0;
  344. }
  345. half glow_fwd(half ycIn, half glowGainIn, half glowMid)
  346. {
  347. half glowGainOut;
  348. if (ycIn <= 2.0 / 3.0 * glowMid)
  349. glowGainOut = glowGainIn;
  350. else if (ycIn >= 2.0 * glowMid)
  351. glowGainOut = 0.0;
  352. else
  353. glowGainOut = glowGainIn * (glowMid / ycIn - 1.0 / 2.0);
  354. return glowGainOut;
  355. }
  356. /*
  357. half cubic_basis_shaper
  358. (
  359. half x,
  360. half w // full base width of the shaper function (in degrees)
  361. )
  362. {
  363. half M[4][4] = {
  364. { -1.0 / 6, 3.0 / 6, -3.0 / 6, 1.0 / 6 },
  365. { 3.0 / 6, -6.0 / 6, 3.0 / 6, 0.0 / 6 },
  366. { -3.0 / 6, 0.0 / 6, 3.0 / 6, 0.0 / 6 },
  367. { 1.0 / 6, 4.0 / 6, 1.0 / 6, 0.0 / 6 }
  368. };
  369. half knots[5] = {
  370. -w / 2.0,
  371. -w / 4.0,
  372. 0.0,
  373. w / 4.0,
  374. w / 2.0
  375. };
  376. half y = 0.0;
  377. if ((x > knots[0]) && (x < knots[4]))
  378. {
  379. half knot_coord = (x - knots[0]) * 4.0 / w;
  380. int j = knot_coord;
  381. half t = knot_coord - j;
  382. half monomials[4] = { t*t*t, t*t, t, 1.0 };
  383. // (if/else structure required for compatibility with CTL < v1.5.)
  384. if (j == 3)
  385. {
  386. y = monomials[0] * M[0][0] + monomials[1] * M[1][0] +
  387. monomials[2] * M[2][0] + monomials[3] * M[3][0];
  388. }
  389. else if (j == 2)
  390. {
  391. y = monomials[0] * M[0][1] + monomials[1] * M[1][1] +
  392. monomials[2] * M[2][1] + monomials[3] * M[3][1];
  393. }
  394. else if (j == 1)
  395. {
  396. y = monomials[0] * M[0][2] + monomials[1] * M[1][2] +
  397. monomials[2] * M[2][2] + monomials[3] * M[3][2];
  398. }
  399. else if (j == 0)
  400. {
  401. y = monomials[0] * M[0][3] + monomials[1] * M[1][3] +
  402. monomials[2] * M[2][3] + monomials[3] * M[3][3];
  403. }
  404. else
  405. {
  406. y = 0.0;
  407. }
  408. }
  409. return y * 3.0 / 2.0;
  410. }
  411. */
  412. static const half3x3 M = {
  413. 0.5, -1.0, 0.5,
  414. -1.0, 1.0, 0.0,
  415. 0.5, 0.5, 0.0
  416. };
  417. half segmented_spline_c5_fwd(half x)
  418. {
  419. 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)
  420. 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)
  421. const half2 minPoint = half2(0.18 * exp2(-15.0), 0.0001); // {luminance, luminance} linear extension below this
  422. const half2 midPoint = half2(0.18, 0.48); // {luminance, luminance}
  423. const half2 maxPoint = half2(0.18 * exp2(18.0), 10000.0); // {luminance, luminance} linear extension above this
  424. const half slopeLow = 0.0; // log-log slope of low linear extension
  425. const half slopeHigh = 0.0; // log-log slope of high linear extension
  426. const int N_KNOTS_LOW = 4;
  427. const int N_KNOTS_HIGH = 4;
  428. // Check for negatives or zero before taking the log. If negative or zero,
  429. // set to ACESMIN.1
  430. half xCheck = x;
  431. if (xCheck <= 0.0) xCheck = HALF_MIN;
  432. half logx = log10(xCheck);
  433. half logy;
  434. if (logx <= log10(minPoint.x))
  435. {
  436. logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
  437. }
  438. else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
  439. {
  440. half knot_coord = half(N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
  441. int j = knot_coord;
  442. half t = knot_coord - half(j);
  443. half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
  444. half3 monomials = half3(t * t, t, 1.0);
  445. logy = dot(monomials, mul(M, cf));
  446. }
  447. else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
  448. {
  449. half knot_coord = half(N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
  450. int j = knot_coord;
  451. half t = knot_coord - half(j);
  452. half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
  453. half3 monomials = half3(t * t, t, 1.0);
  454. logy = dot(monomials, mul(M, cf));
  455. }
  456. else
  457. { //if (logIn >= log10(maxPoint.x)) {
  458. logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
  459. }
  460. return pow(10.0, logy);
  461. }
  462. struct SegmentedSplineParams_c9
  463. {
  464. float coefsLow[10]; // coefs for B-spline between minPoint and midPoint (units of log luminance)
  465. float coefsHigh[10]; // coefs for B-spline between midPoint and maxPoint (units of log luminance)
  466. half2 minPoint; // {luminance, luminance} linear extension below this
  467. half2 midPoint; // {luminance, luminance}
  468. half2 maxPoint; // {luminance, luminance} linear extension above this
  469. float slopeLow; // log-log slope of low linear extension
  470. float slopeHigh; // log-log slope of high linear extension
  471. };
  472. half segmented_spline_c9_fwd(half x, SegmentedSplineParams_c9 params)
  473. {
  474. const int N_KNOTS_LOW = 8;
  475. const int N_KNOTS_HIGH = 8;
  476. // Check for negatives or zero before taking the log. If negative or zero,
  477. // set to OCESMIN.
  478. half xCheck = x;
  479. if (xCheck <= 0.0) xCheck = 1e-4;
  480. half logx = log10(xCheck);
  481. half logy;
  482. if (logx <= log10(params.minPoint.x))
  483. {
  484. logy = logx * half(params.slopeLow) + (log10(params.minPoint.y) - half(params.slopeLow) * log10(params.minPoint.x));
  485. }
  486. else if ((logx > log10(params.minPoint.x)) && (logx < log10(params.midPoint.x)))
  487. {
  488. half knot_coord = half(N_KNOTS_LOW - 1) * (logx - log10(params.minPoint.x)) / (log10(params.midPoint.x) - log10(params.minPoint.x));
  489. int j = knot_coord;
  490. half t = knot_coord - half(j);
  491. half3 cf = half3(params.coefsLow[j], params.coefsLow[j + 1], params.coefsLow[j + 2]);
  492. half3 monomials = half3(t * t, t, 1.0);
  493. logy = dot(monomials, mul(M, cf));
  494. }
  495. else if ((logx >= log10(params.midPoint.x)) && (logx < log10(params.maxPoint.x)))
  496. {
  497. half knot_coord = half(N_KNOTS_HIGH - 1) * (logx - log10(params.midPoint.x)) / (log10(params.maxPoint.x) - log10(params.midPoint.x));
  498. int j = knot_coord;
  499. half t = knot_coord - half(j);
  500. half3 cf = half3(params.coefsHigh[j], params.coefsHigh[j + 1], params.coefsHigh[j + 2]);
  501. half3 monomials = half3(t * t, t, 1.0);
  502. logy = dot(monomials, mul(M, cf));
  503. }
  504. else
  505. { //if (logIn >= log10(maxPoint.x)) {
  506. logy = logx * half(params.slopeHigh) + (log10(params.maxPoint.y) - half(params.slopeHigh) * log10(params.maxPoint.x));
  507. }
  508. return pow(10.0, logy);
  509. }
  510. // > 48 Nits from https://github.com/ampas/aces-dev/blob/dev/transforms/ctl/lib/ACESlib.Tonescales.ctl
  511. SegmentedSplineParams_c9 GetSplineParams_ODT48Nits()
  512. {
  513. const SegmentedSplineParams_c9 ODT_48nits =
  514. {
  515. // coefsLow[10]
  516. { -1.6989700043, -1.6989700043, -1.4779000000, -1.2291000000, -0.8648000000, -0.4480000000, 0.0051800000, 0.4511080334, 0.9113744414, 0.9113744414},
  517. // coefsHigh[10]
  518. { 0.5154386965, 0.8470437783, 1.1358000000, 1.3802000000, 1.5197000000, 1.5985000000, 1.6467000000, 1.6746091357, 1.6878733390, 1.6878733390 },
  519. {segmented_spline_c5_fwd(0.18*pow(2.,-6.5)), 0.02}, // minPoint
  520. {segmented_spline_c5_fwd(0.18), 4.8}, // midPoint
  521. {segmented_spline_c5_fwd(0.18*pow(2.,6.5)), 48.0}, // maxPoint
  522. 0.0, // slopeLow
  523. 0.04 // slopeHigh
  524. };
  525. return ODT_48nits;
  526. }
  527. SegmentedSplineParams_c9 GetSplineParams_ODT1000Nits()
  528. {
  529. const SegmentedSplineParams_c9 ODT_1000nits =
  530. {
  531. // coefsLow[10]
  532. { -4.9706219331, -3.0293780669, -2.1262, -1.5105, -1.0578, -0.4668, 0.11938, 0.7088134201, 1.2911865799, 1.2911865799 },
  533. // coefsHigh[10]
  534. { 0.8089132070, 1.1910867930, 1.5683, 1.9483, 2.3083, 2.6384, 2.8595, 2.9872608805, 3.0127391195, 3.0127391195 },
  535. {segmented_spline_c5_fwd(0.18*pow(2.,-12.)), 0.0001}, // minPoint
  536. {segmented_spline_c5_fwd(0.18), 10.0}, // midPoint
  537. {segmented_spline_c5_fwd(0.18*pow(2.,10.)), 1000.0}, // maxPoint
  538. 3.0, // slopeLow
  539. 0.06 // slopeHigh
  540. };
  541. return ODT_1000nits;
  542. }
  543. SegmentedSplineParams_c9 GetSplineParams_ODT2000Nits()
  544. {
  545. const SegmentedSplineParams_c9 ODT_2000nits =
  546. {
  547. // coefsLow[10]
  548. { -4.9706219331, -3.0293780669, -2.1262, -1.5105, -1.0578, -0.4668, 0.11938, 0.7088134201, 1.2911865799, 1.2911865799 },
  549. // coefsHigh[10]
  550. { 0.8019952042, 1.1980047958, 1.5943000000, 1.9973000000, 2.3783000000, 2.7684000000, 3.0515000000, 3.2746293562, 3.3274306351, 3.3274306351 },
  551. {segmented_spline_c5_fwd(0.18*pow(2.,-12.)), 0.0001}, // minPoint
  552. {segmented_spline_c5_fwd(0.18), 10.0}, // midPoint
  553. {segmented_spline_c5_fwd(0.18*pow(2.,11.)), 2000.0}, // maxPoint
  554. 3.0, // slopeLow
  555. 0.12 // slopeHigh
  556. };
  557. return ODT_2000nits;
  558. }
  559. SegmentedSplineParams_c9 GetSplineParams_ODT4000Nits()
  560. {
  561. const SegmentedSplineParams_c9 ODT_4000nits =
  562. {
  563. // coefsLow[10]
  564. { -4.9706219331, -3.0293780669, -2.1262, -1.5105, -1.0578, -0.4668, 0.11938, 0.7088134201, 1.2911865799, 1.2911865799 },
  565. // coefsHigh[10]
  566. { 0.7973186613, 1.2026813387, 1.6093000000, 2.0108000000, 2.4148000000, 2.8179000000, 3.1725000000, 3.5344995451, 3.6696204376, 3.6696204376 },
  567. {segmented_spline_c5_fwd(0.18*pow(2.,-12.)), 0.0001}, // minPoint
  568. {segmented_spline_c5_fwd(0.18), 10.0}, // midPoint
  569. {segmented_spline_c5_fwd(0.18*pow(2.,12.)), 4000.0}, // maxPoint
  570. 3.0, // slopeLow
  571. 0.3 // slopeHigh
  572. };
  573. return ODT_4000nits;
  574. }
  575. half segmented_spline_c9_fwd(half x)
  576. {
  577. return segmented_spline_c9_fwd(x, GetSplineParams_ODT48Nits());
  578. }
  579. static const half RRT_GLOW_GAIN = 0.05;
  580. static const half RRT_GLOW_MID = 0.08;
  581. static const half RRT_RED_SCALE = 0.82;
  582. static const half RRT_RED_PIVOT = 0.03;
  583. static const half RRT_RED_HUE = 0.0;
  584. static const half RRT_RED_WIDTH = 135.0;
  585. static const half RRT_SAT_FACTOR = 0.96;
  586. half3 RRT(half3 aces)
  587. {
  588. // --- Glow module --- //
  589. half saturation = rgb_2_saturation(aces);
  590. half ycIn = rgb_2_yc(aces);
  591. half s = sigmoid_shaper((saturation - 0.4) / 0.2);
  592. half addedGlow = 1.0 + glow_fwd(ycIn, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
  593. aces *= addedGlow;
  594. // --- Red modifier --- //
  595. half hue = rgb_2_hue(aces);
  596. half centeredHue = center_hue(hue, RRT_RED_HUE);
  597. half hueWeight;
  598. {
  599. //hueWeight = cubic_basis_shaper(centeredHue, RRT_RED_WIDTH);
  600. hueWeight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centeredHue / RRT_RED_WIDTH));
  601. hueWeight *= hueWeight;
  602. }
  603. aces.r += hueWeight * saturation * (RRT_RED_PIVOT - aces.r) * (1.0 - RRT_RED_SCALE);
  604. // --- ACES to RGB rendering space --- //
  605. aces = clamp(aces, 0.0, HALF_MAX); // avoids saturated negative colors from becoming positive in the matrix
  606. half3 rgbPre = mul(AP0_2_AP1_MAT, aces);
  607. rgbPre = clamp(rgbPre, 0, HALF_MAX);
  608. // --- Global desaturation --- //
  609. //rgbPre = mul(RRT_SAT_MAT, rgbPre);
  610. rgbPre = lerp(dot(rgbPre, AP1_RGB2Y).xxx, rgbPre, RRT_SAT_FACTOR.xxx);
  611. // --- Apply the tonescale independently in rendering-space RGB --- //
  612. half3 rgbPost;
  613. rgbPost.x = segmented_spline_c5_fwd(rgbPre.x);
  614. rgbPost.y = segmented_spline_c5_fwd(rgbPre.y);
  615. rgbPost.z = segmented_spline_c5_fwd(rgbPre.z);
  616. // --- RGB rendering space to OCES --- //
  617. half3 outputVal = mul(AP1_2_AP0_MAT, rgbPost);
  618. return outputVal;
  619. }
  620. //
  621. // Output Device Transform
  622. //
  623. half3 Y_2_linCV(half3 Y, half Ymax, half Ymin)
  624. {
  625. return (Y - Ymin) / (Ymax - Ymin);
  626. }
  627. half3 XYZ_2_xyY(half3 XYZ)
  628. {
  629. half divisor = max(dot(XYZ, (1.0).xxx), 1e-4);
  630. return half3(XYZ.xy / divisor, XYZ.y);
  631. }
  632. half3 xyY_2_XYZ(half3 xyY)
  633. {
  634. half m = xyY.z / max(xyY.y, 1e-4);
  635. half3 XYZ = half3(xyY.xz, (1.0 - xyY.x - xyY.y));
  636. XYZ.xz *= m;
  637. return XYZ;
  638. }
  639. static const half DIM_SURROUND_GAMMA = 0.9811;
  640. half3 darkSurround_to_dimSurround(half3 linearCV)
  641. {
  642. // Extra conversions to float3/half3 are required to avoid floating-point precision issues on some platforms.
  643. half3 XYZ = (half3)mul(AP1_2_XYZ_MAT, (float3)linearCV);
  644. half3 xyY = XYZ_2_xyY(XYZ);
  645. xyY.z = clamp(xyY.z, 0.0, HALF_MAX);
  646. xyY.z = pow(xyY.z, DIM_SURROUND_GAMMA);
  647. XYZ = xyY_2_XYZ(xyY);
  648. return mul(XYZ_2_AP1_MAT, XYZ);
  649. }
  650. half moncurve_r(half y, half gamma, half offs)
  651. {
  652. // Reverse monitor curve
  653. half x;
  654. const half yb = pow(offs * gamma / ((gamma - 1.0) * (1.0 + offs)), gamma);
  655. const half rs = pow((gamma - 1.0) / offs, gamma - 1.0) * pow((1.0 + offs) / gamma, gamma);
  656. if (y >= yb)
  657. x = (1.0 + offs) * pow(y, 1.0 / gamma) - offs;
  658. else
  659. x = y * rs;
  660. return x;
  661. }
  662. half bt1886_r(half L, half gamma, half Lw, half Lb)
  663. {
  664. // The reference EOTF specified in Rec. ITU-R BT.1886
  665. // L = a(max[(V+b),0])^g
  666. half a = pow(pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma), gamma);
  667. half b = pow(Lb, 1.0 / gamma) / (pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma));
  668. half V = pow(max(L / a, 0.0), 1.0 / gamma) - b;
  669. return V;
  670. }
  671. half roll_white_fwd(
  672. half x, // color value to adjust (white scaled to around 1.0)
  673. half new_wht, // white adjustment (e.g. 0.9 for 10% darkening)
  674. half width // adjusted width (e.g. 0.25 for top quarter of the tone scale)
  675. )
  676. {
  677. const half x0 = -1.0;
  678. const half x1 = x0 + width;
  679. const half y0 = -new_wht;
  680. const half y1 = x1;
  681. const half m1 = (x1 - x0);
  682. const half a = y0 - y1 + m1;
  683. const half b = 2.0 * (y1 - y0) - m1;
  684. const half c = y0;
  685. const half t = (-x - x0) / (x1 - x0);
  686. half o = 0.0;
  687. if (t < 0.0)
  688. o = -(t * b + c);
  689. else if (t > 1.0)
  690. o = x;
  691. else
  692. o = -((t * a + b) * t + c);
  693. return o;
  694. }
  695. half3 linear_to_bt1886(half3 x, half gamma, half Lw, half Lb)
  696. {
  697. // Good enough approximation for now, may consider using the exact formula instead
  698. // TODO: Experiment
  699. return pow(max(x, 0.0), 1.0 / 2.4);
  700. // Correct implementation (Reference EOTF specified in Rec. ITU-R BT.1886) :
  701. // L = a(max[(V+b),0])^g
  702. half invgamma = 1.0 / gamma;
  703. half p_Lw = pow(Lw, invgamma);
  704. half p_Lb = pow(Lb, invgamma);
  705. half3 a = pow(p_Lw - p_Lb, gamma).xxx;
  706. half3 b = (p_Lb / p_Lw - p_Lb).xxx;
  707. half3 V = pow(max(x / a, 0.0), invgamma.xxx) - b;
  708. return V;
  709. }
  710. static const half CINEMA_WHITE = 48.0;
  711. static const half CINEMA_BLACK = CINEMA_WHITE / 2400.0;
  712. static const half ODT_SAT_FACTOR = 0.93;
  713. // <ACEStransformID>ODT.Academy.RGBmonitor_100nits_dim.a1.0.3</ACEStransformID>
  714. // <ACESuserName>ACES 1.0 Output - sRGB</ACESuserName>
  715. //
  716. // Output Device Transform - RGB computer monitor
  717. //
  718. //
  719. // Summary :
  720. // This transform is intended for mapping OCES onto a desktop computer monitor
  721. // typical of those used in motion picture visual effects production. These
  722. // monitors may occasionally be referred to as "sRGB" displays, however, the
  723. // monitor for which this transform is designed does not exactly match the
  724. // specifications in IEC 61966-2-1:1999.
  725. //
  726. // The assumed observer adapted white is D65, and the viewing environment is
  727. // that of a dim surround.
  728. //
  729. // The monitor specified is intended to be more typical of those found in
  730. // visual effects production.
  731. //
  732. // Device Primaries :
  733. // Primaries are those specified in Rec. ITU-R BT.709
  734. // CIE 1931 chromaticities: x y Y
  735. // Red: 0.64 0.33
  736. // Green: 0.3 0.6
  737. // Blue: 0.15 0.06
  738. // White: 0.3127 0.329 100 cd/m^2
  739. //
  740. // Display EOTF :
  741. // The reference electro-optical transfer function specified in
  742. // IEC 61966-2-1:1999.
  743. //
  744. // Signal Range:
  745. // This transform outputs full range code values.
  746. //
  747. // Assumed observer adapted white point:
  748. // CIE 1931 chromaticities: x y
  749. // 0.3127 0.329
  750. //
  751. // Viewing Environment:
  752. // This ODT has a compensation for viewing environment variables more typical
  753. // of those associated with video mastering.
  754. //
  755. half3 ODT_RGBmonitor_100nits_dim(half3 oces)
  756. {
  757. const SegmentedSplineParams_c9 ODT_48nits = GetSplineParams_ODT48Nits();
  758. // OCES to RGB rendering space
  759. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  760. // Apply the tonescale independently in rendering-space RGB
  761. half3 rgbPost;
  762. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_48nits);
  763. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_48nits);
  764. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_48nits);
  765. // Scale luminance to linear code value
  766. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  767. // Apply gamma adjustment to compensate for dim surround
  768. linearCV = darkSurround_to_dimSurround(linearCV);
  769. // Apply desaturation to compensate for luminance difference
  770. //linearCV = mul(ODT_SAT_MAT, linearCV);
  771. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  772. // Convert to display primary encoding
  773. // Rendering space RGB to XYZ
  774. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  775. // Apply CAT from ACES white point to assumed observer adapted white point
  776. XYZ = mul(D60_2_D65_CAT, XYZ);
  777. // CIE XYZ to display primaries
  778. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  779. // Handle out-of-gamut values
  780. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  781. linearCV = saturate(linearCV);
  782. // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
  783. // with sRGB opto-electrical transfer function (OETF).
  784. /*
  785. // Encode linear code values with transfer function
  786. half3 outputCV;
  787. // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
  788. const half DISPGAMMA = 2.4;
  789. const half OFFSET = 0.055;
  790. outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
  791. outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
  792. outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
  793. outputCV = linear_to_sRGB(linearCV);
  794. */
  795. // Unity already draws to a sRGB target
  796. return linearCV;
  797. }
  798. // <ACEStransformID>ODT.Academy.RGBmonitor_D60sim_100nits_dim.a1.0.3</ACEStransformID>
  799. // <ACESuserName>ACES 1.0 Output - sRGB (D60 sim.)</ACESuserName>
  800. //
  801. // Output Device Transform - RGB computer monitor (D60 simulation)
  802. //
  803. //
  804. // Summary :
  805. // This transform is intended for mapping OCES onto a desktop computer monitor
  806. // typical of those used in motion picture visual effects production. These
  807. // monitors may occasionally be referred to as "sRGB" displays, however, the
  808. // monitor for which this transform is designed does not exactly match the
  809. // specifications in IEC 61966-2-1:1999.
  810. //
  811. // The assumed observer adapted white is D60, and the viewing environment is
  812. // that of a dim surround.
  813. //
  814. // The monitor specified is intended to be more typical of those found in
  815. // visual effects production.
  816. //
  817. // Device Primaries :
  818. // Primaries are those specified in Rec. ITU-R BT.709
  819. // CIE 1931 chromaticities: x y Y
  820. // Red: 0.64 0.33
  821. // Green: 0.3 0.6
  822. // Blue: 0.15 0.06
  823. // White: 0.3127 0.329 100 cd/m^2
  824. //
  825. // Display EOTF :
  826. // The reference electro-optical transfer function specified in
  827. // IEC 61966-2-1:1999.
  828. //
  829. // Signal Range:
  830. // This transform outputs full range code values.
  831. //
  832. // Assumed observer adapted white point:
  833. // CIE 1931 chromaticities: x y
  834. // 0.32168 0.33767
  835. //
  836. // Viewing Environment:
  837. // This ODT has a compensation for viewing environment variables more typical
  838. // of those associated with video mastering.
  839. //
  840. half3 ODT_RGBmonitor_D60sim_100nits_dim(half3 oces)
  841. {
  842. const SegmentedSplineParams_c9 ODT_48nits = GetSplineParams_ODT48Nits();
  843. // OCES to RGB rendering space
  844. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  845. // Apply the tonescale independently in rendering-space RGB
  846. half3 rgbPost;
  847. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_48nits);
  848. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_48nits);
  849. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_48nits);
  850. // Scale luminance to linear code value
  851. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  852. // --- Compensate for different white point being darker --- //
  853. // This adjustment is to correct an issue that exists in ODTs where the device
  854. // is calibrated to a white chromaticity other than D60. In order to simulate
  855. // D60 on such devices, unequal code values are sent to the display to achieve
  856. // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
  857. // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
  858. // 0.351) the red channel is higher than green and blue to compensate for the
  859. // "greenish" DCI white. This is the correct behavior but it means that as
  860. // highlight increase, the red channel will hit the device maximum first and
  861. // clip, resulting in a chromaticity shift as the green and blue channels
  862. // continue to increase.
  863. // To avoid this clipping error, a slight scale factor is applied to allow the
  864. // ODTs to simulate D60 within the D65 calibration white point.
  865. // Scale and clamp white to avoid casted highlights due to D60 simulation
  866. const half SCALE = 0.955;
  867. linearCV = min(linearCV, 1.0) * SCALE;
  868. // Apply gamma adjustment to compensate for dim surround
  869. linearCV = darkSurround_to_dimSurround(linearCV);
  870. // Apply desaturation to compensate for luminance difference
  871. //linearCV = mul(ODT_SAT_MAT, linearCV);
  872. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  873. // Convert to display primary encoding
  874. // Rendering space RGB to XYZ
  875. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  876. // CIE XYZ to display primaries
  877. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  878. // Handle out-of-gamut values
  879. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  880. linearCV = saturate(linearCV);
  881. // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
  882. // with sRGB opto-electrical transfer function (OETF).
  883. /*
  884. // Encode linear code values with transfer function
  885. half3 outputCV;
  886. // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
  887. const half DISPGAMMA = 2.4;
  888. const half OFFSET = 0.055;
  889. outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
  890. outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
  891. outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
  892. outputCV = linear_to_sRGB(linearCV);
  893. */
  894. // Unity already draws to a sRGB target
  895. return linearCV;
  896. }
  897. // <ACEStransformID>ODT.Academy.Rec709_100nits_dim.a1.0.3</ACEStransformID>
  898. // <ACESuserName>ACES 1.0 Output - Rec.709</ACESuserName>
  899. //
  900. // Output Device Transform - Rec709
  901. //
  902. //
  903. // Summary :
  904. // This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
  905. // that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
  906. // adapted white is D65, and the viewing environment is a dim surround.
  907. //
  908. // A possible use case for this transform would be HDTV/video mastering.
  909. //
  910. // Device Primaries :
  911. // Primaries are those specified in Rec. ITU-R BT.709
  912. // CIE 1931 chromaticities: x y Y
  913. // Red: 0.64 0.33
  914. // Green: 0.3 0.6
  915. // Blue: 0.15 0.06
  916. // White: 0.3127 0.329 100 cd/m^2
  917. //
  918. // Display EOTF :
  919. // The reference electro-optical transfer function specified in
  920. // Rec. ITU-R BT.1886.
  921. //
  922. // Signal Range:
  923. // By default, this transform outputs full range code values. If instead a
  924. // SMPTE "legal" signal is desired, there is a runtime flag to output
  925. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  926. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  927. //
  928. // Assumed observer adapted white point:
  929. // CIE 1931 chromaticities: x y
  930. // 0.3127 0.329
  931. //
  932. // Viewing Environment:
  933. // This ODT has a compensation for viewing environment variables more typical
  934. // of those associated with video mastering.
  935. //
  936. half3 ODT_Rec709_100nits_dim(half3 oces)
  937. {
  938. const SegmentedSplineParams_c9 ODT_48nits = GetSplineParams_ODT48Nits();
  939. // OCES to RGB rendering space
  940. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  941. // Apply the tonescale independently in rendering-space RGB
  942. half3 rgbPost;
  943. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_48nits);
  944. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_48nits);
  945. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_48nits);
  946. // Scale luminance to linear code value
  947. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  948. // Apply gamma adjustment to compensate for dim surround
  949. linearCV = darkSurround_to_dimSurround(linearCV);
  950. // Apply desaturation to compensate for luminance difference
  951. //linearCV = mul(ODT_SAT_MAT, linearCV);
  952. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  953. // Convert to display primary encoding
  954. // Rendering space RGB to XYZ
  955. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  956. // Apply CAT from ACES white point to assumed observer adapted white point
  957. XYZ = mul(D60_2_D65_CAT, XYZ);
  958. // CIE XYZ to display primaries
  959. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  960. // Handle out-of-gamut values
  961. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  962. linearCV = saturate(linearCV);
  963. // Encode linear code values with transfer function
  964. const half DISPGAMMA = 2.4;
  965. const half L_W = 1.0;
  966. const half L_B = 0.0;
  967. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  968. // TODO: Implement support for legal range.
  969. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  970. // by default which will result in double perceptual encoding, thus for now if one want to use
  971. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  972. // compensate for Unity default behaviour.
  973. return outputCV;
  974. }
  975. // <ACEStransformID>ODT.Academy.Rec709_D60sim_100nits_dim.a1.0.3</ACEStransformID>
  976. // <ACESuserName>ACES 1.0 Output - Rec.709 (D60 sim.)</ACESuserName>
  977. //
  978. // Output Device Transform - Rec709 (D60 simulation)
  979. //
  980. //
  981. // Summary :
  982. // This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
  983. // that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
  984. // adapted white is D60, and the viewing environment is a dim surround.
  985. //
  986. // A possible use case for this transform would be cinema "soft-proofing".
  987. //
  988. // Device Primaries :
  989. // Primaries are those specified in Rec. ITU-R BT.709
  990. // CIE 1931 chromaticities: x y Y
  991. // Red: 0.64 0.33
  992. // Green: 0.3 0.6
  993. // Blue: 0.15 0.06
  994. // White: 0.3127 0.329 100 cd/m^2
  995. //
  996. // Display EOTF :
  997. // The reference electro-optical transfer function specified in
  998. // Rec. ITU-R BT.1886.
  999. //
  1000. // Signal Range:
  1001. // By default, this transform outputs full range code values. If instead a
  1002. // SMPTE "legal" signal is desired, there is a runtime flag to output
  1003. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  1004. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  1005. //
  1006. // Assumed observer adapted white point:
  1007. // CIE 1931 chromaticities: x y
  1008. // 0.32168 0.33767
  1009. //
  1010. // Viewing Environment:
  1011. // This ODT has a compensation for viewing environment variables more typical
  1012. // of those associated with video mastering.
  1013. //
  1014. half3 ODT_Rec709_D60sim_100nits_dim(half3 oces)
  1015. {
  1016. const SegmentedSplineParams_c9 ODT_48nits = GetSplineParams_ODT48Nits();
  1017. // OCES to RGB rendering space
  1018. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1019. // Apply the tonescale independently in rendering-space RGB
  1020. half3 rgbPost;
  1021. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_48nits);
  1022. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_48nits);
  1023. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_48nits);
  1024. // Scale luminance to linear code value
  1025. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  1026. // --- Compensate for different white point being darker --- //
  1027. // This adjustment is to correct an issue that exists in ODTs where the device
  1028. // is calibrated to a white chromaticity other than D60. In order to simulate
  1029. // D60 on such devices, unequal code values must be sent to the display to achieve
  1030. // the chromaticities of D60. More specifically, in order to produce D60 on a device
  1031. // calibrated to a D65 white point (i.e. equal code values yield CIE x,y
  1032. // chromaticities of 0.3127, 0.329) the red channel must be slightly higher than
  1033. // that of green and blue in order to compensate for the relatively more "blue-ish"
  1034. // D65 white. This unequalness of color channels is the correct behavior but it
  1035. // means that as neutral highlights increase, the red channel will hit the
  1036. // device maximum first and clip, resulting in a small chromaticity shift as the
  1037. // green and blue channels continue to increase to their maximums.
  1038. // To avoid this clipping error, a slight scale factor is applied to allow the
  1039. // ODTs to simulate D60 within the D65 calibration white point.
  1040. // Scale and clamp white to avoid casted highlights due to D60 simulation
  1041. const half SCALE = 0.955;
  1042. linearCV = min(linearCV, 1.0) * SCALE;
  1043. // Apply gamma adjustment to compensate for dim surround
  1044. linearCV = darkSurround_to_dimSurround(linearCV);
  1045. // Apply desaturation to compensate for luminance difference
  1046. //linearCV = mul(ODT_SAT_MAT, linearCV);
  1047. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  1048. // Convert to display primary encoding
  1049. // Rendering space RGB to XYZ
  1050. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1051. // CIE XYZ to display primaries
  1052. linearCV = mul(XYZ_2_REC709_MAT, XYZ);
  1053. // Handle out-of-gamut values
  1054. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  1055. linearCV = saturate(linearCV);
  1056. // Encode linear code values with transfer function
  1057. const half DISPGAMMA = 2.4;
  1058. const half L_W = 1.0;
  1059. const half L_B = 0.0;
  1060. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  1061. // TODO: Implement support for legal range.
  1062. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  1063. // by default which will result in double perceptual encoding, thus for now if one want to use
  1064. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  1065. // compensate for Unity default behaviour.
  1066. return outputCV;
  1067. }
  1068. // <ACEStransformID>ODT.Academy.Rec2020_100nits_dim.a1.0.3</ACEStransformID>
  1069. // <ACESuserName>ACES 1.0 Output - Rec.2020</ACESuserName>
  1070. //
  1071. // Output Device Transform - Rec2020
  1072. //
  1073. //
  1074. // Summary :
  1075. // This transform is intended for mapping OCES onto a Rec.2020 broadcast
  1076. // monitor that is calibrated to a D65 white point at 100 cd/m^2. The assumed
  1077. // observer adapted white is D65, and the viewing environment is that of a dim
  1078. // surround.
  1079. //
  1080. // A possible use case for this transform would be UHDTV/video mastering.
  1081. //
  1082. // Device Primaries :
  1083. // Primaries are those specified in Rec. ITU-R BT.2020
  1084. // CIE 1931 chromaticities: x y Y
  1085. // Red: 0.708 0.292
  1086. // Green: 0.17 0.797
  1087. // Blue: 0.131 0.046
  1088. // White: 0.3127 0.329 100 cd/m^2
  1089. //
  1090. // Display EOTF :
  1091. // The reference electro-optical transfer function specified in
  1092. // Rec. ITU-R BT.1886.
  1093. //
  1094. // Signal Range:
  1095. // By default, this transform outputs full range code values. If instead a
  1096. // SMPTE "legal" signal is desired, there is a runtime flag to output
  1097. // SMPTE legal signal. In ctlrender, this can be achieved by appending
  1098. // '-param1 legalRange 1' after the '-ctl odt.ctl' string.
  1099. //
  1100. // Assumed observer adapted white point:
  1101. // CIE 1931 chromaticities: x y
  1102. // 0.3127 0.329
  1103. //
  1104. // Viewing Environment:
  1105. // This ODT has a compensation for viewing environment variables more typical
  1106. // of those associated with video mastering.
  1107. //
  1108. half3 ODT_Rec2020_100nits_dim(half3 oces)
  1109. {
  1110. const SegmentedSplineParams_c9 ODT_48nits = GetSplineParams_ODT48Nits();
  1111. // OCES to RGB rendering space
  1112. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1113. // Apply the tonescale independently in rendering-space RGB
  1114. half3 rgbPost;
  1115. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_48nits);
  1116. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_48nits);
  1117. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_48nits);
  1118. // Scale luminance to linear code value
  1119. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  1120. // Apply gamma adjustment to compensate for dim surround
  1121. linearCV = darkSurround_to_dimSurround(linearCV);
  1122. // Apply desaturation to compensate for luminance difference
  1123. //linearCV = mul(ODT_SAT_MAT, linearCV);
  1124. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  1125. // Convert to display primary encoding
  1126. // Rendering space RGB to XYZ
  1127. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1128. // Apply CAT from ACES white point to assumed observer adapted white point
  1129. XYZ = mul(D60_2_D65_CAT, XYZ);
  1130. // CIE XYZ to display primaries
  1131. linearCV = mul(XYZ_2_REC2020_MAT, XYZ);
  1132. // Handle out-of-gamut values
  1133. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  1134. linearCV = saturate(linearCV);
  1135. // Encode linear code values with transfer function
  1136. const half DISPGAMMA = 2.4;
  1137. const half L_W = 1.0;
  1138. const half L_B = 0.0;
  1139. half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
  1140. // TODO: Implement support for legal range.
  1141. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  1142. // by default which will result in double perceptual encoding, thus for now if one want to use
  1143. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  1144. // compensate for Unity default behaviour.
  1145. return outputCV;
  1146. }
  1147. // <ACEStransformID>ODT.Academy.P3DCI_48nits.a1.0.3</ACEStransformID>
  1148. // <ACESuserName>ACES 1.0 Output - P3-DCI</ACESuserName>
  1149. //
  1150. // Output Device Transform - P3DCI (D60 Simulation)
  1151. //
  1152. //
  1153. // Summary :
  1154. // This transform is intended for mapping OCES onto a P3 digital cinema
  1155. // projector that is calibrated to a DCI white point at 48 cd/m^2. The assumed
  1156. // observer adapted white is D60, and the viewing environment is that of a dark
  1157. // theater.
  1158. //
  1159. // Device Primaries :
  1160. // CIE 1931 chromaticities: x y Y
  1161. // Red: 0.68 0.32
  1162. // Green: 0.265 0.69
  1163. // Blue: 0.15 0.06
  1164. // White: 0.314 0.351 48 cd/m^2
  1165. //
  1166. // Display EOTF :
  1167. // Gamma: 2.6
  1168. //
  1169. // Assumed observer adapted white point:
  1170. // CIE 1931 chromaticities: x y
  1171. // 0.32168 0.33767
  1172. //
  1173. // Viewing Environment:
  1174. // Environment specified in SMPTE RP 431-2-2007
  1175. //
  1176. half3 ODT_P3DCI_48nits(half3 oces)
  1177. {
  1178. const SegmentedSplineParams_c9 ODT_48nits = GetSplineParams_ODT48Nits();
  1179. // OCES to RGB rendering space
  1180. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1181. // Apply the tonescale independently in rendering-space RGB
  1182. half3 rgbPost;
  1183. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_48nits);
  1184. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_48nits);
  1185. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_48nits);
  1186. // Scale luminance to linear code value
  1187. half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
  1188. // --- Compensate for different white point being darker --- //
  1189. // This adjustment is to correct an issue that exists in ODTs where the device
  1190. // is calibrated to a white chromaticity other than D60. In order to simulate
  1191. // D60 on such devices, unequal code values are sent to the display to achieve
  1192. // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
  1193. // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
  1194. // 0.351) the red channel is higher than green and blue to compensate for the
  1195. // "greenish" DCI white. This is the correct behavior but it means that as
  1196. // highlight increase, the red channel will hit the device maximum first and
  1197. // clip, resulting in a chromaticity shift as the green and blue channels
  1198. // continue to increase.
  1199. // To avoid this clipping error, a slight scale factor is applied to allow the
  1200. // ODTs to simulate D60 within the D65 calibration white point. However, the
  1201. // magnitude of the scale factor required for the P3DCI ODT was considered too
  1202. // large. Therefore, the scale factor was reduced and the additional required
  1203. // compression was achieved via a reshaping of the highlight rolloff in
  1204. // conjunction with the scale. The shape of this rolloff was determined
  1205. // throught subjective experiments and deemed to best reproduce the
  1206. // "character" of the highlights in the P3D60 ODT.
  1207. // Roll off highlights to avoid need for as much scaling
  1208. const half NEW_WHT = 0.918;
  1209. const half ROLL_WIDTH = 0.5;
  1210. linearCV.x = roll_white_fwd(linearCV.x, NEW_WHT, ROLL_WIDTH);
  1211. linearCV.y = roll_white_fwd(linearCV.y, NEW_WHT, ROLL_WIDTH);
  1212. linearCV.z = roll_white_fwd(linearCV.z, NEW_WHT, ROLL_WIDTH);
  1213. // Scale and clamp white to avoid casted highlights due to D60 simulation
  1214. const half SCALE = 0.96;
  1215. linearCV = min(linearCV, NEW_WHT) * SCALE;
  1216. // Convert to display primary encoding
  1217. // Rendering space RGB to XYZ
  1218. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1219. // CIE XYZ to display primaries
  1220. linearCV = mul(XYZ_2_DCIP3_MAT, XYZ);
  1221. // Handle out-of-gamut values
  1222. // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
  1223. linearCV = saturate(linearCV);
  1224. // Encode linear code values with transfer function
  1225. const half DISPGAMMA = 2.6;
  1226. half3 outputCV = pow(linearCV, 1.0 / DISPGAMMA);
  1227. // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
  1228. // by default which will result in double perceptual encoding, thus for now if one want to use
  1229. // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
  1230. // compensate for Unity default behaviour.
  1231. return outputCV;
  1232. }
  1233. // IMPORTANT: This will need transforming to the final output space after unlike the standard ODT.
  1234. half3 ODT_Rec2020_1000nits_ToLinear(half3 oces)
  1235. {
  1236. const SegmentedSplineParams_c9 ODT_1000nits = GetSplineParams_ODT1000Nits();
  1237. // OCES to RGB rendering space
  1238. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1239. // Apply the tonescale independently in rendering-space RGB
  1240. half3 rgbPost;
  1241. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_1000nits);
  1242. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_1000nits);
  1243. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_1000nits);
  1244. // Scale luminance to linear code value
  1245. half3 linearCV = Y_2_linCV(rgbPost, ODT_1000nits.maxPoint.y, ODT_1000nits.minPoint.y);
  1246. // Apply desaturation to compensate for luminance difference
  1247. //linearCV = mul(ODT_SAT_MAT, linearCV);
  1248. linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
  1249. // Convert to display primary encoding
  1250. // Rendering space RGB to XYZ
  1251. half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
  1252. // Apply CAT from ACES white point to assumed observer adapted white point
  1253. XYZ = mul(D60_2_D65_CAT, XYZ);
  1254. // CIE XYZ to display primaries
  1255. linearCV = mul(XYZ_2_REC2020_MAT, XYZ);
  1256. // Handle out-of-gamut values
  1257. linearCV = max(linearCV, 0.);
  1258. return linearCV;
  1259. }
  1260. half3 ODT_1000nits_ToAP1(half3 oces)
  1261. {
  1262. const SegmentedSplineParams_c9 ODT_1000nits = GetSplineParams_ODT1000Nits();
  1263. // OCES to RGB rendering space
  1264. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1265. // Apply the tonescale independently in rendering-space RGB
  1266. half3 rgbPost;
  1267. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_1000nits);
  1268. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_1000nits);
  1269. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_1000nits);
  1270. return rgbPost;
  1271. }
  1272. half3 ODT_2000nits_ToAP1(half3 oces)
  1273. {
  1274. const SegmentedSplineParams_c9 ODT_2000nits = GetSplineParams_ODT2000Nits();
  1275. // OCES to RGB rendering space
  1276. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1277. // Apply the tonescale independently in rendering-space RGB
  1278. half3 rgbPost;
  1279. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_2000nits);
  1280. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_2000nits);
  1281. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_2000nits);
  1282. return rgbPost;
  1283. }
  1284. half3 ODT_4000nits_ToAP1(half3 oces)
  1285. {
  1286. const SegmentedSplineParams_c9 ODT_4000nits = GetSplineParams_ODT4000Nits();
  1287. // OCES to RGB rendering space
  1288. half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
  1289. // Apply the tonescale independently in rendering-space RGB
  1290. half3 rgbPost;
  1291. rgbPost.x = segmented_spline_c9_fwd(rgbPre.x, ODT_4000nits);
  1292. rgbPost.y = segmented_spline_c9_fwd(rgbPre.y, ODT_4000nits);
  1293. rgbPost.z = segmented_spline_c9_fwd(rgbPre.z, ODT_4000nits);
  1294. return rgbPost;
  1295. }
  1296. #if SHADER_API_MOBILE || SHADER_API_GLES3 || SHADER_API_SWITCH
  1297. #pragma warning (enable : 3205) // conversion of larger type to smaller
  1298. #endif
  1299. #endif // __ACES__