1#version 450 core
2#extension GL_ARB_separate_shader_objects : enable
3#extension GL_ARB_shading_language_420pack : enable
4
5// includes
6
7#include "render/shaders/common/render_color_conversion_common.h"
8#include "render/shaders/common/render_post_process_common.h"
9#include "render/shaders/common/render_tonemap_common.h"
10
11// sets
12
13#include "render/shaders/common/render_post_process_layout_common.h"
14
15layout(set = 1, binding = 0) uniform texture2D uDepth;
16layout(set = 1, binding = 1) uniform texture2D uColor;
17layout(set = 1, binding = 2) uniform texture2D uVelocity;
18layout(set = 1, binding = 3) uniform texture2D uHistory;
19layout(set = 1, binding = 4) uniform sampler uSampler;
20
21// in / out
22
23layout(location = 0) in vec2 inUv;
24
25layout(location = 0) out vec4 outColor;
26
27// NOTE: cannot be used (remove if not used for any input)
28#define ENABLE_INPUT_ATTACHMENTS 0
29
30#define QUALITY_LOW 0
31#define QUALITY_MED 1
32#define QUALITY_HIGH 2
33
34float GetUnpackDepthBuffer(const vec2 uv)
35{
36#if (ENABLE_INPUT_ATTACHMENTS == 1)
37    return subpassLoad(uDepth).x;
38#else
39    return textureLod(sampler2D(uDepth, uSampler), uv, 0).x;
40#endif
41}
42
43vec2 GetUnpackVelocity(const vec2 uv, const vec2 invSize)
44{
45#if (ENABLE_INPUT_ATTACHMENTS == 1)
46    return subpassLoad(uVelocity).xy;
47#else
48    return textureLod(sampler2D(uVelocity, uSampler), uv, 0).xy * invSize;
49#endif
50}
51
52vec2 GetVelocity()
53{
54    const uint quality = uint(uPc.factor.x + 0.5);
55    vec2 velUv = inUv;
56    if (quality >= QUALITY_MED) {
57        const uint offsetCount = 5;
58        const ivec2 offsets[offsetCount] = {
59            ivec2(-1, -1),
60            ivec2(1, -1),
61            ivec2(0, 0),
62            ivec2(-1, 1),
63            ivec2(1, 1),
64        };
65
66        // NOTE: needs compile time constant offset and does not compile if e.g. passed to function
67        vec2 batch1 = vec2(1.0, textureLodOffset(sampler2D(uDepth, uSampler), inUv.xy, 0.0, offsets[0]).x);
68        vec2 batch2 = vec2(textureLodOffset(sampler2D(uDepth, uSampler), inUv.xy, 0.0, offsets[1]).x, textureLodOffset(sampler2D(uDepth, uSampler), inUv.xy, 0.0, offsets[2]).x);
69        ivec2 index = mix(ivec2(2, 0), ivec2(1, 2), lessThan(batch1, batch2));
70        batch1 = mix(batch1, batch2, lessThan(batch1, batch2));
71
72        vec2 batch3 = vec2(textureLodOffset(sampler2D(uDepth, uSampler), inUv.xy, 0.0, offsets[3]).x, textureLodOffset(sampler2D(uDepth, uSampler), inUv.xy, 0.0, offsets[4]).x);
73        index = mix(index, ivec2(3, 4), lessThan(batch1, batch3));
74        batch1 = mix(batch1, batch3, lessThan(batch1, batch3));
75
76        ivec2 offset = offsets[batch1.x < batch1.y?index.x:index.y];
77        float depth = batch1.x <batch1.y ? batch1.x :  batch1.y;
78
79        velUv += offset * (uPc.viewportSizeInvSize.zw);
80    }
81    // multiply velocity to correct uv offsets
82    return textureLod(sampler2D(uVelocity, uSampler), velUv, 0).xy * uPc.viewportSizeInvSize.zw;
83}
84
85// clip towards aabb center
86// e.g. "temporal reprojection anti-aliasing in inside"
87vec4 ClipAabb(const vec3 aabbMin, const vec3 aabbMax, const vec4 color, const vec4 history)
88{
89    const vec3 pClip = 0.5 * (aabbMax + aabbMin);
90    const vec3 eClip = 0.5 * (aabbMax - aabbMin);
91
92    const vec4 vClip = history - vec4(pClip, color.w);
93    const vec3 vUnit = vClip.xyz - eClip;
94    const vec3 aUnit = abs(vUnit);
95    const float maUnit = max(aUnit.x, max(aUnit.y, aUnit.z));
96    // if maUnit <= 1.0 the point is inside the aabb
97    const vec4 res = (maUnit > 1.0) ? (vec4(pClip, color.w) + vClip / maUnit) : color;
98    return res;
99}
100
101void main(void)
102{
103    // NOTE: could use closest depth 3x3 velocity
104    const vec2 velocity = GetVelocity();
105    const vec2 historyUv = inUv.xy - velocity;
106
107    const vec4 colorSample = textureLod(sampler2D(uColor, uSampler), inUv.xy, 0.0);
108    const float frameAlpha = colorSample.a;
109    vec4 currColor = colorSample;
110
111    // NOTE: add filtered option for less blurred history
112    vec4 history = textureLod(sampler2D(uHistory, uSampler), historyUv, 0.0);
113    // sample 3x3 grid
114    // 0 1 2
115    // 3 4 5
116    // 6 7 8
117
118    // Box filter for history
119    // diamond shape
120    vec3 bc1 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(0, -1)).rgb;
121    vec3 bc3 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(-1, 0)).rgb;
122    // center sample
123    vec3 bc4 = currColor.rgb;
124    vec3 min13 = min(min(bc1, bc3), bc4);
125    vec3 max13 = max(max(bc1, bc3), bc4);
126
127    vec3 bc5 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(1, 0)).rgb;
128    vec3 bc7 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(0, 1)).rgb;
129    vec3 min57 = min(bc5, bc7);
130    vec3 max57 = max(bc5, bc7);
131    vec3 boxMin = min(min13, min57);
132    vec3 boxMax = max(max13, max57);
133
134    //
135    vec3 bc0 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(-1, -1)).rgb;
136    vec3 bc2 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(1, -1)).rgb;
137    vec3 min02 = min(boxMin, min(bc0, bc2));
138    vec3 max02 = max(boxMax, max(bc0, bc2));
139
140    vec3 bc6 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(-1, 1)).rgb;
141    vec3 bc8 = textureLodOffset(sampler2D(uColor, uSampler), inUv.xy, 0.0, ivec2(1, 1)).rgb;
142    vec3 min68 = min(bc6, bc8);
143    vec3 max68 = max(bc6, bc8);
144
145    // corners
146    const vec3 boxMinCorner = min(min02, min68);
147    const vec3 boxMaxCorner = max(max02, max68);
148
149    // NOTE: add
150    // filter with tonemapping and yCoCG
151
152    boxMin = (boxMin + boxMinCorner) * 0.5;
153    boxMax = (boxMax + boxMaxCorner) * 0.5;
154
155    // NOTE: add option to use clipping
156    history.rgb = clamp(history.rgb, boxMin, boxMax);
157
158    // luma based tonemapping as suggested by Karis
159    const float historyLuma = CalcLuma(history.rgb);
160    const float colorLuma = CalcLuma(currColor.rgb);
161
162    history.rgb *= 1.0 / (1.0 + historyLuma);
163    currColor.rgb *= 1.0 / (1.0 + colorLuma);
164
165    const float taaAlpha = uPc.factor.a;
166    vec4 color = mix(history, currColor, taaAlpha);
167
168    // inverse tonemap
169    color.rgb *= 1.0 / (1.0 - CalcLuma(color.rgb));
170
171    // safety for removing negative values
172    outColor = max(color, vec4(0.0));
173}
174