PPWhiteBalance.bslinc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. mixin PPWhiteBalance
  2. {
  3. code
  4. {
  5. /**
  6. * Calculates correlated color temperature from chomaticity coordinates using the McCamy's formula.
  7. * Coordinates should be near the Planckian locus otherwise the returned temperature becomes meaningless.
  8. *
  9. * @param coords CIE 1931 x chomaticity coordinates.
  10. * @return Correlated color temperature in degrees Kelvin.
  11. */
  12. float CCT(float2 coords)
  13. {
  14. float n = (coords.x - 0.3320f) / (0.1858f - coords.y);
  15. float n2 = n * n;
  16. float n3 = n2 * n;
  17. return -449.0f * n3 + 3525.0f * n2 - 6823.3f * n + 5520.33f;
  18. }
  19. /**
  20. * Calculates chromaticity coordinates from a correlated color temperature. Uses the Planckian locus formula
  21. * which works for values in range [1000K, 15000K].
  22. *
  23. * @param T Correlated color temperature in degrees Kelvin.
  24. * @return CIE 1960 UCS chomaticity coordinates.
  25. */
  26. float2 PlanckianLocusChromaticity(float T)
  27. {
  28. float T2 = T * T;
  29. // Calculates CIE 1960 UCS coordinates
  30. float u = (0.860117757f + 1.54118254e-4f * T + 1.28641212e-7f * T2) / (1.0f + 8.42420235e-4f * T + 7.08145163e-7f * T2);
  31. float v = (0.317398726f + 4.22806245e-5f * T + 4.20481691e-8f * T2) / (1.0f - 2.89741816e-5f * T + 1.61456053e-7f * T2);
  32. return float2(u, v);
  33. }
  34. /**
  35. * Calculates chromaticity coordinates from a correlated color temperature. Uses the formula for series
  36. * D standard illuminants (D55, D65, D75, etc.). Valid for values in range [4000K, 25000K].
  37. *
  38. * @param T Correlated color temperature in degrees Kelvin.
  39. * @return CIE 1931 chomaticity coordinates.
  40. */
  41. float2 DSeriesIlluminantChromaticity(float T)
  42. {
  43. float x = T <= 7000.0f
  44. ? 0.244063f + (0.09911e3 + (2.9678e6 - 4.6070e9 / T) / T) / T
  45. : 0.237040f + (0.24748e3 + (1.9018e6 - 2.0064e9 / T) / T) / T;
  46. float y = -3.0f * x * x + 2.87f * x - 0.275f;
  47. return float2(x, y);
  48. }
  49. /**
  50. * Converts chomaticity coordinates from CIE 1960 uniform color space to CIE 1931 color space.
  51. *
  52. * @param uv Chromaticity coordinates in CIE 1960 UCS.
  53. * @return Chromaticity coordinates in CIE 1931.
  54. */
  55. float2 CIE1960ToCIE1931(float2 uv)
  56. {
  57. float x = (3 * uv.x) / (2 * uv.x - 8 * uv.y + 4);
  58. float y = (2 * uv.y) / (2 * uv.x - 8 * uv.y + 4);
  59. return float2(x, y);
  60. }
  61. /**
  62. * Adds the specified offset along the Planckian isothermal line and returns the chromaticity coordinates for the offset position.
  63. *
  64. * @param uv Chromaticity coordiantes in CIE 1960 UCS for the correlated color temperature along the Planckian locus.
  65. * @param offset Offset to be added along the isothermal. In range [-1, 1]. The actual offset in chromaticity
  66. * coordinates is scaled to |0.05| since values farther than that usually aren't useful.
  67. * @return CIE 1931 chomaticity coordinates.
  68. */
  69. float2 PlanckianIsothermalOffset(float2 uv, float offset)
  70. {
  71. // Rotate uv by 90 degrees and normalize it to get the isotherm line
  72. float2 isotherm = normalize(float2(-uv.y, uv.x));
  73. uv += isotherm * offset * 0.05f;
  74. return CIE1960ToCIE1931(uv);
  75. }
  76. /**
  77. * Converts from CIE 1931 xyY color space to XYZ color space.
  78. *
  79. * @param xyY Coordinates in xyY color space.
  80. * @return Coordinates in XYZ color space.
  81. */
  82. float3 xyYToXYZ(float3 xyY)
  83. {
  84. float divisor = max(xyY.y, 1e-10f);
  85. float3 XYZ;
  86. XYZ.x = (xyY.x * xyY.z) / divisor;
  87. XYZ.y = xyY.z;
  88. XYZ.z = ((1.0 - xyY.x - xyY.y) * xyY.z) / divisor;
  89. return XYZ;
  90. }
  91. /**
  92. * Converts from CIE 1931 XYZ color space to xyY color space.
  93. *
  94. * @param XYZ Coordinates in XYZ color space.
  95. * @return Coordinates in xyY color space.
  96. */
  97. float3 XYZToxyY(float3 XYZ)
  98. {
  99. float3 xyY;
  100. float divisor = XYZ.x + XYZ.y + XYZ.z;
  101. if (divisor == 0.0f)
  102. divisor = 1e-10f;
  103. xyY.x = XYZ.x / divisor;
  104. xyY.y = XYZ.y / divisor;
  105. xyY.z = XYZ.y;
  106. return xyY;
  107. }
  108. /**
  109. * Returns a matrix that transform XYZ tristimulus values for a given white point to
  110. * a new white point.
  111. *
  112. * @param orgWhite Chromaticity coordinates in CIE 1931 for the original white point.
  113. * @param newWhite Chromaticity coordinates in CIE 1931 for the new white point.
  114. * @return Matrix that transform from the original to new white point.
  115. */
  116. float3x3 ChromaticAdaptation(float2 orgWhite, float2 newWhite)
  117. {
  118. // Convert xyY to XYZ
  119. float3 orgWhite3 = xyYToXYZ(float3(orgWhite.xy, 1.0f));
  120. float3 newWhite3 = xyYToXYZ(float3(newWhite.xy, 1.0f));
  121. // Convert to cone response domain using Bradford's matrix
  122. const float3x3 coneResponse =
  123. {
  124. 0.8951f, 0.2664f, -0.1614f,
  125. -0.7502f, 1.7135f, 0.0367f,
  126. 0.0389f, -0.0685f, 1.0296f,
  127. };
  128. const float3x3 invConeResponse =
  129. {
  130. 0.9870f, -0.1471f, 0.1600f,
  131. 0.4323f, 0.5184f, 0.0493f,
  132. -0.0085f, 0.0400f, 0.9685f,
  133. };
  134. orgWhite3 = mul(coneResponse, orgWhite3);
  135. newWhite3 = mul(coneResponse, newWhite3);
  136. // Generate transformation matrix
  137. float3x3 adaptation =
  138. {
  139. newWhite3.x / orgWhite3.x, 0.0f, 0.0f,
  140. 0.0f, newWhite3.y / orgWhite3.y, 0.0f,
  141. 0.0f, 0.0f, newWhite3.z / orgWhite3.z
  142. };
  143. return mul(invConeResponse, mul(adaptation, coneResponse));
  144. }
  145. [internal]
  146. cbuffer WhiteBalanceInput
  147. {
  148. float gWhiteTemp;
  149. float gWhiteOffset;
  150. }
  151. /**
  152. * Applies color balancing to the provided color. The color is transformed from its original white point
  153. * (provided by gWhiteTemp and gWhiteOffset) to a D65 white point.
  154. *
  155. * @param color Color in linear sRGB/Rec.709 color space.
  156. * @return White balanced linear color.
  157. */
  158. float3 WhiteBalance(float3 color)
  159. {
  160. float2 orgPlanckianUV = PlanckianLocusChromaticity(gWhiteTemp);
  161. float2 orgWhiteXY;
  162. if(gWhiteTemp < 4000)
  163. {
  164. orgWhiteXY = PlanckianIsothermalOffset(orgPlanckianUV, gWhiteOffset);
  165. }
  166. else
  167. {
  168. orgWhiteXY = DSeriesIlluminantChromaticity(gWhiteTemp);
  169. float2 offsetXY = PlanckianIsothermalOffset(orgPlanckianUV, gWhiteOffset) - CIE1960ToCIE1931(orgPlanckianUV);
  170. orgWhiteXY += offsetXY;
  171. }
  172. float2 newWhiteXY = float2(0.3128f, 0.3290f); // D65 white point
  173. float3x3 adaptation = ChromaticAdaptation(orgWhiteXY, newWhiteXY);
  174. adaptation = mul(XYZTosRGBMatrix, mul(adaptation, sRGBToXYZMatrix));
  175. return mul(adaptation, color);
  176. }
  177. };
  178. };