TemporalResolve.bslinc 19 KB

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