TemporalResolve.bslinc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. #include "$ENGINE$/PerCameraData.bslinc"
  2. #include "$ENGINE$/ColorSpace.bslinc"
  3. mixin TemporalResolve
  4. {
  5. mixin PerCameraData;
  6. mixin ColorSpace;
  7. code
  8. {
  9. ////////////////// CUSTOMIZATION PARAMETERS /////////////////////////////
  10. // When enabled, the system will sample a specific sample from a MS texture. UV coordinates are assumed
  11. // to be in pixel space in that case. When disabled sampleIdx parameter is ignored and UV coordinates
  12. // are assumed be in standard [0, 1] range.
  13. #ifndef MSAA
  14. #define MSAA 0
  15. #endif
  16. // Only relevant when MSAA is enabled. When disabled color textures are assumed to be non-MSAA. When
  17. // enabled all textures are assumed to be MSAA.
  18. #ifndef MSAA_COLOR
  19. #define MSAA_COLOR MSAA
  20. #endif
  21. // 0 - System will use the velocity of the current pixel
  22. // 1 - System will search 4 neighbor pixels in + pattern, and choose the velocity of the pixel nearest
  23. // to the camera
  24. // 2 - System will search 8 surrounding pixels and choose the velocity of the pixel nearest to the camera
  25. //
  26. // Searching the neighborhod instead of just using current velocity yields nicer edges for objects in
  27. // motion. See TEMPORAL_SEARCH_RADIUS in order to customize how far away to search.
  28. //
  29. // Only relevant if TEMPORAL_LOCAL_VELOCITY is enabled, since without it no per-object velocity
  30. // information is present and everything is blended based on camera movement.
  31. #ifndef TEMPORAL_SEARCH_NEAREST
  32. #define TEMPORAL_SEARCH_NEAREST 1
  33. #endif
  34. // Determine how far away to sample pixels when TEMPORAL_SEARCH_NEAREST is enabled.
  35. // 1 - Immediately adjacent pixels are searched
  36. // 2 - Pixels two away are searched (looks better than 1)
  37. // 3 - etc.
  38. #ifndef TEMPORAL_SEARCH_RADIUS
  39. #define TEMPORAL_SEARCH_RADIUS 2
  40. #endif
  41. // 0 - The system will only account for velocity due to camera movement (not due to individual objects)
  42. // 1 - The system will account both for velocity due to camera movement, as well as individual object
  43. // movement. Requires the user to provide a per-pixel velocity buffer.
  44. #ifndef TEMPORAL_LOCAL_VELOCITY
  45. #define TEMPORAL_LOCAL_VELOCITY 1
  46. #endif
  47. // When enabled, the resolve operation will be performed in YCoCg color space. This can yield better
  48. // results, requires less color samples and no value clipping.
  49. #ifndef TEMPORAL_YCOCG
  50. #define TEMPORAL_YCOCG 0
  51. #endif
  52. // When enabled, green color will be used instead of calculating luminosity. This will yield better
  53. // performance but can result in lower quality. Ignored when TEMPORAL_YCOCG is enabled, since luminosity
  54. // is already available as part of the YCoCg color space.
  55. #ifndef TEMPORAL_GREEN_AS_LUMA
  56. #define TEMPORAL_GREEN_AS_LUMA 0
  57. #endif
  58. // When enabled the input samples will be tonemapped using the provided exposure value. Once the final
  59. // value is resolved, it will be scaled back into original range. This ensures high frequency data from
  60. // HDR content is removed, as it would cause aliasing otherwise. We scale the result back into high range
  61. // so the high-quality tonemap shader can be ran on it.
  62. #ifndef TEMPORAL_TONEMAP
  63. #define TEMPORAL_TONEMAP 1
  64. #endif
  65. // When enabled an extra low-pass filter is ran when sampling scene color, for better quality.
  66. #ifndef TEMPORAL_LOWPASS
  67. #define TEMPORAL_LOWPASS 1
  68. #endif
  69. // When enabled, clamp/clip color neighborhood will be deduced using standard deviation of all the
  70. // neighborhood samples. When disabled a min/max operation is performed instead.
  71. #ifndef TEMPORAL_SMOOTH_NEIGHBORHOOD
  72. #define TEMPORAL_SMOOTH_NEIGHBORHOOD 1
  73. #endif
  74. // When enabled, neighborhood clipping will use an AABB intersection to clip the history value. When disabled
  75. // just a clamp will be used instead. Not relevant when TEMPORAL_YCOCG is enabled because it always uses a clamp.
  76. #ifndef TEMPORAL_CLIP_AABB
  77. #define TEMPORAL_CLIP_AABB 1
  78. #endif
  79. // Determines how is the history value blended with the current value.
  80. // 0 - The system will calculate the optimal blend value automatically
  81. // >0 - A fixed blend factor will be used, equal to the multiplicative inverse of the provided value.
  82. // (i.e. a value of 8 will result in blend factor of 1/8, meaning 12.5% of the history value will be used)
  83. #ifndef TEMPORAL_BLEND_FACTOR
  84. #define TEMPORAL_BLEND_FACTOR 0
  85. #endif
  86. // Determines how many frames should pixels deemed as "bad" (too different from current pixel) contribute to the
  87. // current frame.
  88. #ifndef TEMPORAL_BAD_RETENTION
  89. #define TEMPORAL_BAD_RETENTION 3
  90. #endif
  91. // Determines how many frames should pixels deemed as "good" (similar to the current pixel) contribute to the
  92. // current frame.
  93. #ifndef TEMPORAL_GOOD_RETENTION
  94. #define TEMPORAL_GOOD_RETENTION 10
  95. #endif
  96. ////////////////////////// HELPER MACROS /////////////////////////
  97. #if MSAA
  98. #define _TEX2D(n) Texture2DMS<float> n
  99. #if MSAA_COLOR
  100. #define _TEXCOLOR(n) Texture2DMS<float4> n
  101. #define _PTEXCOLOR(n) n
  102. #else
  103. #define _TEXCOLOR(n) Texture2D n, SamplerState n##SampState, float2 n##TexelSize
  104. #define _PTEXCOLOR(n) n, n##SampState, n##TexelSize
  105. #endif
  106. #define _PTEX2D(n) n
  107. #define _SAMPLE(n, uv) n.Load((int2)uv, sampleIdx)
  108. #define _SAMPLEOFF(n, uv, offset) n.Load((int2)(uv) + offset, sampleIdx)
  109. #if MSAA_COLOR
  110. #define _SAMPLECOL(n, uv, offset) _SAMPLEOFF(n, uv, offset)
  111. #else
  112. #define _SAMPLECOL(n, uv, offset) n.Sample(n##SampState, uv, offset)
  113. #endif
  114. #define _PIXSIZE(n) int2(1, 1)
  115. #else
  116. #define _TEX2D(n) Texture2D n, SamplerState n##SampState, float2 n##TexelSize
  117. #define _TEXCOLOR(n) _TEX2D(n)
  118. #define _PTEX2D(n) n, n##SampState, n##TexelSize
  119. #define _PTEXCOLOR(n) n, n##SampState, n##TexelSize
  120. #define _SAMPLE(n, uv) n.Sample(n##SampState, uv)
  121. #define _SAMPLEOFF(n, uv, offset) n.Sample(n##SampState, uv, offset)
  122. #define _SAMPLECOL(n, uv, offset) _SAMPLEOFF(n, uv, offset)
  123. #define _PIXSIZE(n) n##TexelSize
  124. #endif
  125. ///////////////////////// HELPER FUNCTIONS ////////////////////////
  126. float3 findNearest3x3(_TEX2D(sceneDepth), float2 uv, int sampleIdx)
  127. {
  128. int r = TEMPORAL_SEARCH_RADIUS;
  129. float3 dmin = float3(0, 0, 1);
  130. [unroll]
  131. for(int y = -r; y <= r; y += r)
  132. {
  133. [unroll]
  134. for(int x = -r; x <= r; x += r)
  135. {
  136. float depth = _SAMPLEOFF(sceneDepth, uv, int2(x, y)).x;
  137. dmin = depth < dmin.z ? float3(x, y, depth) : dmin;
  138. }
  139. }
  140. return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
  141. }
  142. float3 findNearestCross(_TEX2D(sceneDepth), float2 uv, int sampleIdx)
  143. {
  144. int r = TEMPORAL_SEARCH_RADIUS;
  145. float3 dmin = float3(0, 0, 1);
  146. {
  147. float depth = _SAMPLE(sceneDepth, uv).x;
  148. dmin = depth < dmin.z ? float3(0, 0, depth) : dmin;
  149. }
  150. {
  151. float depth = _SAMPLEOFF(sceneDepth, uv, int2(-r, 0)).x;
  152. dmin = depth < dmin.z ? float3(-r, 0, depth) : dmin;
  153. }
  154. {
  155. float depth = _SAMPLEOFF(sceneDepth, uv, int2(r, 0)).x;
  156. dmin = depth < dmin.z ? float3(r, 0, depth) : dmin;
  157. }
  158. {
  159. float depth = _SAMPLEOFF(sceneDepth, uv, int2(0, -r)).x;
  160. dmin = depth < dmin.z ? float3(0, -r, depth) : dmin;
  161. }
  162. {
  163. float depth = _SAMPLEOFF(sceneDepth, uv, int2(0, r)).x;
  164. dmin = depth < dmin.z ? float3(0, r, depth) : dmin;
  165. }
  166. return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
  167. }
  168. float clipAABB(float3 boxMin, float3 boxMax, float3 history, float3 current)
  169. {
  170. // Note: Is this necessary? Will "current" always be in the box?
  171. boxMin = min(current, boxMin);
  172. boxMax = max(current, boxMax);
  173. float3 center = (boxMax + boxMin) * 0.5f;
  174. float3 extents = boxMax - center;
  175. float3 origin = history - center; // Relative to box
  176. float3 dir = current - history;
  177. float3 rDir = rcp(dir);
  178. float3 tNeg = (extents - origin) * rDir;
  179. float3 tPos = (-extents - origin) * rDir;
  180. return saturate(max(max(min(tNeg.x, tPos.x), min(tNeg.y, tPos.y)), min(tNeg.z, tPos.z)));
  181. }
  182. // Encodes velocity into a format suitable for storing in a 16-bit SNORM texture.
  183. // Velocity range of [-2, 2] is supported (full NDC).
  184. float2 encodeVelocity16SNORM(float2 velocity)
  185. {
  186. return velocity * 0.5f;
  187. }
  188. // Decodes velocity from an encoded 16-bit SNORM format. See encodeVelocity16SNORM().
  189. // Velocity range of [-2, 2] is supported (full NDC).
  190. float2 decodeVelocity16SNORM(float2 val)
  191. {
  192. return val * 2.0f;
  193. }
  194. ////////////////////// HELPER TONEMAP/COLOR SPACE DEFINES /////////////////////
  195. // Automatically scale HDR values based on luminance, if enabled
  196. #if TEMPORAL_TONEMAP
  197. #if TEMPORAL_YCOCG
  198. #define _TONEMAP_COLOR(v) HDRScaleY(v, exposureScale)
  199. #elif TEMPORAL_GREEN_AS_LUMA
  200. #define _TONEMAP_COLOR(v) HDRScaleG(v, exposureScale)
  201. #else
  202. #define _TONEMAP_COLOR(v) HDRScaleRGB(v, exposureScale)
  203. #endif
  204. #else // TEMPORAL_TONEMAP
  205. #define _TONEMAP_COLOR(v) v
  206. #endif // TEMPORAL_TONEMAP
  207. // Converts scene color to YCoCg space (if enabled) and applies tonemapping
  208. float4 convertColor(
  209. float4 color
  210. #if TEMPORAL_TONEMAP
  211. , float exposureScale
  212. #endif // TEMPORAL_TONEMAP
  213. )
  214. {
  215. #if TEMPORAL_YCOCG
  216. color.rgb = _TONEMAP_COLOR(RGBToYCoCg(color.rgb));
  217. #else
  218. color.rgb = _TONEMAP_COLOR(color.rgb);
  219. #endif
  220. return color;
  221. }
  222. #if TEMPORAL_TONEMAP
  223. #define _SAMPLE_COLOR(n, uv, offset) convertColor(_SAMPLECOL(n, uv, offset), exposureScale)
  224. #else
  225. #define _SAMPLE_COLOR(n, uv, offset) convertColor(_SAMPLECOL(n, uv, offset))
  226. #endif
  227. ///////////////////////////// MAIN /////////////////////////////////
  228. [internal]
  229. cbuffer TemporalInput
  230. {
  231. float gSampleWeights[9];
  232. float gSampleWeightsLowpass[9];
  233. }
  234. float4 temporalResolve(
  235. _TEX2D(sceneDepth),
  236. _TEXCOLOR(sceneColor),
  237. _TEXCOLOR(prevColor),
  238. #if TEMPORAL_LOCAL_VELOCITY
  239. _TEX2D(velocityBuffer),
  240. #endif // TEMPORAL_LOCAL_VELOCITY
  241. #if TEMPORAL_TONEMAP
  242. float exposureScale,
  243. #endif // TEMPORAL_TONEMAP
  244. float2 uv,
  245. float2 ndcPos, // Can be derived from UV, but we usually have it for free, so pass it directly
  246. int sampleIdx)
  247. {
  248. ///////////// DETERMINE PER-PIXEL VELOCITY & CURRENT DEPTH ///////////////////
  249. float curDepth;
  250. float2 velocity;
  251. #if TEMPORAL_LOCAL_VELOCITY
  252. #if TEMPORAL_SEARCH_NEAREST == 1
  253. float3 nearest = findNearestCross(_PTEX2D(sceneDepth), uv, sampleIdx);
  254. velocity = _SAMPLE(velocityBuffer, nearest.xy);
  255. curDepth = nearest.z;
  256. #elif TEMPORAL_SEARCH_NEAREST == 2
  257. float3 nearest = findNearest3x3(_PTEX2D(sceneDepth), uv, sampleIdx);
  258. velocity = _SAMPLE(velocityBuffer, nearest.xy);
  259. curDepth = nearest.z;
  260. #else // TEMPORAL_SEARCH_NEAREST
  261. velocity = _SAMPLE(velocityBuffer, uv);
  262. curDepth = _SAMPLE(sceneDepth, uv).x;
  263. #endif // TEMPORAL_SEARCH_NEAREST
  264. #else // TEMPORAL_LOCAL_VELOCITY
  265. velocity = 0;
  266. curDepth = _SAMPLE(sceneDepth, uv).x;
  267. #endif // TEMPORAL_LOCAL_VELOCITY
  268. ///////////////////// DETERMINE PREV. FRAME UV //////////////////////////////
  269. float2 prevNdcPos;
  270. bool hasLocalVelocity = (abs(velocity.x) + abs(velocity.y)) > 0;
  271. if(hasLocalVelocity)
  272. {
  273. velocity = decodeVelocity16SNORM(velocity);
  274. prevNdcPos = float2(ndcPos - velocity);
  275. }
  276. else
  277. {
  278. // Assumes velocity due to camera movement
  279. float4 currentNDC = float4(ndcPos, curDepth, 1);
  280. float4 prevClip = mul(gNDCToPrevNDC, currentNDC);
  281. prevNdcPos = prevClip.xy / prevClip.w;
  282. }
  283. #if MSAA && MSAA_COLOR
  284. float2 prevUV = NDCToScreen(prevNdcPos);
  285. #else
  286. float2 prevUV = NDCToUV(prevNdcPos);
  287. #endif
  288. /////////////// GET FILTERED COLOR VALUE AND NEIGHBORHOOD MIN/MAX /////////////
  289. #if MSAA && !MSAA_COLOR
  290. float2 uvColor = uv * sceneColorTexelSize;
  291. #else
  292. float2 uvColor = uv;
  293. #endif
  294. #if TEMPORAL_YCOCG
  295. // YCOCG only requires a + pattern for good quality
  296. float4 neighbor[5];
  297. neighbor[0] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1, 0));
  298. neighbor[1] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, -1));
  299. neighbor[2] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, 0));
  300. neighbor[3] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1, 0));
  301. neighbor[4] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, 1));
  302. float4 filtered = 0;
  303. [unroll]
  304. for(uint i = 0; i < 5; ++i)
  305. filtered += neighbor[i] * gSampleWeights[i];
  306. float4 filteredLow = filtered;
  307. float4 neighborMin = min(min(min(neighbor[0], neighbor[1]), min(neighbor[2], neighbor[3])),
  308. neighbor[4]);
  309. float4 neighborMax = max(max(max(neighbor[0], neighbor[1]), max(neighbor[2], neighbor[3])),
  310. neighbor[4]);
  311. #else // TEMPORAL_YCOCG
  312. float4 neighbor[9];
  313. neighbor[0] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1, -1));
  314. neighbor[1] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, -1));
  315. neighbor[2] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1, -1));
  316. neighbor[3] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1, 0));
  317. neighbor[4] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, 0));
  318. neighbor[5] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1, 0));
  319. neighbor[6] = _SAMPLE_COLOR(sceneColor, uvColor, int2(-1, 1));
  320. neighbor[7] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 0, 1));
  321. neighbor[8] = _SAMPLE_COLOR(sceneColor, uvColor, int2( 1, 1));
  322. float4 filtered = 0;
  323. [unroll]
  324. for(uint i = 0; i < 9; ++i)
  325. filtered += neighbor[i] * gSampleWeights[i];
  326. #if TEMPORAL_LOWPASS
  327. float4 filteredLow = 0;
  328. [unroll]
  329. for(uint i = 0; i < 9; ++i)
  330. filteredLow += neighbor[i] * gSampleWeightsLowpass[i];
  331. #else
  332. float4 filteredLow = filtered;
  333. #endif // TEMPORAL_LOWPASS
  334. #if TEMPORAL_SMOOTH_NEIGHBORHOOD
  335. // Calculate standard deviation and determine neighborhood min/max based on it
  336. float4 mean = 0;
  337. [unroll]
  338. for(uint i = 0; i < 9; ++i)
  339. mean += neighbor[i];
  340. mean /= 9.0f;
  341. float4 meanSqrd = 0;
  342. [unroll]
  343. for(uint i = 0; i < 9; ++i)
  344. meanSqrd += neighbor[i] * neighbor[i];
  345. meanSqrd /= 9.0f;
  346. float4 stdDev = sqrt(abs(meanSqrd - mean * mean));
  347. float4 neighborMin = mean - stdDev;
  348. float4 neighborMax = mean + stdDev;
  349. #else // TEMPORAL_SMOOTH_NEIGHBORHOOD
  350. float4 neighborMin = min(min(
  351. min(min(neighbor[0], neighbor[1]), min(neighbor[2], neighbor[3])),
  352. min(min(neighbor[4], neighbor[5]), min(neighbor[6], neighbor[7]))),
  353. neighbor[8]);
  354. float4 neighborMax = max(max(
  355. max(max(neighbor[0], neighbor[1]), max(neighbor[2], neighbor[3])),
  356. max(max(neighbor[4], neighbor[5]), max(neighbor[6], neighbor[7]))),
  357. neighbor[8]);
  358. #endif // TEMPORAL_SMOOTH_NEIGHBORHOOD
  359. #endif // TEMPORAL_YCOCG
  360. /////////////////// GET PREVIOUS FRAME COLOR ///////////////////////
  361. float4 prevColorVal = _SAMPLE_COLOR(prevColor, prevUV, int2(0, 0));
  362. ///////////////////// CLAMP TO NEIGHBORHOOD ////////////////////////
  363. // Clamping to neighborhood ensures we don't blend with values that are too
  364. // different, which can happen when history data becomes invalid.
  365. #if TEMPORAL_YCOCG
  366. prevColorVal = clamp(prevColorVal, neighborMin, neighborMax);
  367. #else // TEMPORAL_YCOCG
  368. // Uses low-pass to reduce flickering
  369. #if TEMPORAL_CLIP_AABB
  370. float clipT = clipAABB(neighborMin.rgb, neighborMax.rgb, prevColorVal.rgb, filteredLow.rgb);
  371. prevColorVal = prevColorVal + clipT * (filteredLow - prevColorVal);
  372. #else // TEMPORAL_CLIP_AABB
  373. prevColorVal = clamp(prevColorVal, neighborMin, neighborMax);
  374. #endif // TEMPORAL_CLIP_AABB
  375. #endif // TEMPORAL_YCOCG
  376. //////////////// BLEND BETWEEN CURRENT AND HISTORY //////////////////
  377. // Find out how much impact should the previous frame's color have
  378. #if TEMPORAL_BLEND_FACTOR // Fixed blend factor
  379. float blendAmount = 1.0f / TEMPORAL_BLEND_FACTOR;
  380. float4 output = lerp(prevColorVal, filtered, blendAmount);
  381. #else // TEMPORAL_BLEND_FACTOR
  382. #if TEMPORAL_YCOCG
  383. float lumaCurrent = filtered.r;
  384. float lumaHistory = prevColorVal.r;
  385. #else // TEMPORAL_YCOCG
  386. #if TEMPORAL_GREEN_AS_LUMA
  387. float lumaCurrent = filtered.g;
  388. float lumaHistory = prevColorVal.g;
  389. #else // TEMPORAL_GREEN_AS_LUMA
  390. float lumaCurrent = LuminanceRGB(filtered);
  391. float lumaHistory = LuminanceRGB(prevColorVal);
  392. #endif // TEMPORAL_GREEN_AS_LUMA
  393. #endif // TEMPORAL_YCOCG
  394. // Based on T. Lottes: https://www.youtube.com/watch?v=WzpLWzGvFK4&t=18m
  395. float blendWeight = 1.0f - abs(lumaCurrent - lumaHistory) / max(max(lumaCurrent, lumaHistory), 0.001f);
  396. float weightBad = 1.0f - 1.0f / TEMPORAL_BAD_RETENTION;
  397. float weightGood = 1.0f - 1.0f / TEMPORAL_GOOD_RETENTION;
  398. float blendAmount = lerp(weightBad, weightGood, blendWeight * blendWeight);
  399. float4 output = lerp(filtered, prevColorVal, blendAmount);
  400. #endif // TEMPORAL_BLEND_FACTOR
  401. //////// UNDO TONEMAP & MOVE BACK TO RGB SPACE //////////////////////
  402. #if TEMPORAL_TONEMAP
  403. #if TEMPORAL_YCOCG
  404. output.rgb = HDRScaleYInv(output.rgb, exposureScale);
  405. #elif TEMPORAL_GREEN_AS_LUMA
  406. output.rgb = HDRScaleGInv(output.rgb, exposureScale);
  407. #else
  408. output.rgb = HDRScaleRGBInv(output.rgb, exposureScale);
  409. #endif
  410. #endif // TEMPORAL_TONEMAP
  411. #if TEMPORAL_YCOCG
  412. output.rgb = YCoCgToRGB(output.rgb);
  413. #endif // TEMPORAL_YCOCG
  414. // Note: Potential improvements:
  415. // - Add a sharpen step
  416. // - Use filtering when sampling history
  417. // - Properly handle borders when sampling neighbors
  418. // - Better blend amount calculation? (Needs experimentation)
  419. return output;
  420. }
  421. #undef _TEX2D
  422. #undef _PTEX2D
  423. #undef _SAMPLE
  424. #undef _PIXSIZE
  425. #undef _TONEMAP_COLOR
  426. #undef _TONEMAP_COLOR_INV
  427. #undef _RESOLVE_COLOR
  428. };
  429. };