|
@@ -5,17 +5,19 @@
|
|
|
|
|
|
|
|
// Calculates the motion vectors that will be used to sample from the previous frame
|
|
// Calculates the motion vectors that will be used to sample from the previous frame
|
|
|
|
|
|
|
|
-#include <AnKi/Shaders/Functions.glsl>
|
|
|
|
|
|
|
+#pragma anki hlsl
|
|
|
|
|
+
|
|
|
|
|
+#include <AnKi/Shaders/Functions.hlsl>
|
|
|
|
|
|
|
|
ANKI_SPECIALIZATION_CONSTANT_UVEC2(kFramebufferSize, 0u);
|
|
ANKI_SPECIALIZATION_CONSTANT_UVEC2(kFramebufferSize, 0u);
|
|
|
-const F32 kMaxRejectionDistance = 0.1; // In meters
|
|
|
|
|
-const F32 kMaxHistoryLength = 16.0;
|
|
|
|
|
|
|
+constexpr F32 kMaxRejectionDistance = 0.1; // In meters
|
|
|
|
|
+constexpr F32 kMaxHistoryLength = 16.0;
|
|
|
|
|
|
|
|
-layout(set = 0, binding = 0) uniform sampler u_linearAnyClampSampler;
|
|
|
|
|
-layout(set = 0, binding = 1) uniform texture2D u_currentDepthTex;
|
|
|
|
|
-layout(set = 0, binding = 2) uniform texture2D u_historyDepthTex;
|
|
|
|
|
-layout(set = 0, binding = 3) uniform texture2D u_velocityTex;
|
|
|
|
|
-layout(set = 0, binding = 4) uniform texture2D u_historyLengthTex;
|
|
|
|
|
|
|
+[[vk::binding(0)]] SamplerState g_linearAnyClampSampler;
|
|
|
|
|
+[[vk::binding(1)]] Texture2D g_currentDepthTex;
|
|
|
|
|
+[[vk::binding(2)]] Texture2D g_historyDepthTex;
|
|
|
|
|
+[[vk::binding(3)]] Texture2D g_velocityTex;
|
|
|
|
|
+[[vk::binding(4)]] Texture2D g_historyLengthTex;
|
|
|
|
|
|
|
|
struct Uniforms
|
|
struct Uniforms
|
|
|
{
|
|
{
|
|
@@ -24,49 +26,39 @@ struct Uniforms
|
|
|
Mat4 m_prevViewProjectionInvMat;
|
|
Mat4 m_prevViewProjectionInvMat;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-layout(set = 0, binding = 5, std140, row_major) uniform b_unis
|
|
|
|
|
-{
|
|
|
|
|
- Uniforms u_unis;
|
|
|
|
|
-};
|
|
|
|
|
|
|
+[[vk::binding(5)]] ConstantBuffer<Uniforms> g_unis;
|
|
|
|
|
|
|
|
#if defined(ANKI_COMPUTE_SHADER)
|
|
#if defined(ANKI_COMPUTE_SHADER)
|
|
|
-layout(set = 0, binding = 6) uniform writeonly image2D u_motionVectorsImage;
|
|
|
|
|
-layout(set = 0, binding = 7) uniform writeonly image2D u_historyLengthImage;
|
|
|
|
|
-
|
|
|
|
|
-const UVec2 kWorkgroupSize = UVec2(8, 8);
|
|
|
|
|
-layout(local_size_x = kWorkgroupSize.x, local_size_y = kWorkgroupSize.y, local_size_z = 1) in;
|
|
|
|
|
-#else
|
|
|
|
|
-layout(location = 0) in Vec2 in_uv;
|
|
|
|
|
-layout(location = 0) out Vec2 out_motionVectors;
|
|
|
|
|
-layout(location = 1) out F32 out_historyLength;
|
|
|
|
|
|
|
+[[vk::binding(6)]] RWTexture2D<Vec2> g_motionVectorsUav;
|
|
|
|
|
+[[vk::binding(7)]] RWTexture2D<F32> g_historyLengthUav;
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
Vec3 clipToWorld(Vec4 clip, Mat4 clipToWorldMat)
|
|
Vec3 clipToWorld(Vec4 clip, Mat4 clipToWorldMat)
|
|
|
{
|
|
{
|
|
|
- const Vec4 v4 = clipToWorldMat * clip;
|
|
|
|
|
|
|
+ const Vec4 v4 = mul(clipToWorldMat, clip);
|
|
|
return v4.xyz / v4.w;
|
|
return v4.xyz / v4.w;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Average the some depth values and unproject.
|
|
/// Average the some depth values and unproject.
|
|
|
-Vec3 getAverageWorldPosition(texture2D tex, Vec2 uv, Mat4 clipToWorldMat)
|
|
|
|
|
|
|
+Vec3 getAverageWorldPosition(Texture2D tex, Vec2 uv, Mat4 clipToWorldMat)
|
|
|
{
|
|
{
|
|
|
const Vec2 halfTexel = (1.0 / Vec2(kFramebufferSize)) / 2.0;
|
|
const Vec2 halfTexel = (1.0 / Vec2(kFramebufferSize)) / 2.0;
|
|
|
|
|
|
|
|
- Vec4 depths = textureGather(sampler2D(tex, u_linearAnyClampSampler), uv + halfTexel, 0);
|
|
|
|
|
- depths += textureGather(sampler2D(tex, u_linearAnyClampSampler), uv - halfTexel, 0);
|
|
|
|
|
|
|
+ Vec4 depths = tex.GatherRed(g_linearAnyClampSampler, uv + halfTexel);
|
|
|
|
|
+ depths += tex.GatherRed(g_linearAnyClampSampler, uv - halfTexel);
|
|
|
|
|
|
|
|
const F32 avgDepth = (depths.x + depths.y + depths.z + depths.w) / 8.0;
|
|
const F32 avgDepth = (depths.x + depths.y + depths.z + depths.w) / 8.0;
|
|
|
|
|
|
|
|
- return clipToWorld(Vec4(UV_TO_NDC(uv), avgDepth, 1.0), clipToWorldMat);
|
|
|
|
|
|
|
+ return clipToWorld(Vec4(uvToNdc(uv), avgDepth, 1.0), clipToWorldMat);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Get the depths of some neighbour texels, unproject and find the AABB in world space that encloses them.
|
|
/// Get the depths of some neighbour texels, unproject and find the AABB in world space that encloses them.
|
|
|
-void getMinMaxWorldPositions(texture2D tex, Vec2 uv, Mat4 clipToWorldMat, out Vec3 aabbMin, out Vec3 aabbMax)
|
|
|
|
|
|
|
+void getMinMaxWorldPositions(Texture2D tex, Vec2 uv, Mat4 clipToWorldMat, out Vec3 aabbMin, out Vec3 aabbMax)
|
|
|
{
|
|
{
|
|
|
const Vec2 halfTexel = (1.0 / Vec2(kFramebufferSize)) / 2.0;
|
|
const Vec2 halfTexel = (1.0 / Vec2(kFramebufferSize)) / 2.0;
|
|
|
|
|
|
|
|
- const Vec4 depths1 = textureGather(sampler2D(tex, u_linearAnyClampSampler), uv + halfTexel, 0);
|
|
|
|
|
- const Vec4 depths2 = textureGather(sampler2D(tex, u_linearAnyClampSampler), uv - halfTexel, 0);
|
|
|
|
|
|
|
+ const Vec4 depths1 = tex.GatherRed(g_linearAnyClampSampler, uv + halfTexel);
|
|
|
|
|
+ const Vec4 depths2 = tex.GatherRed(g_linearAnyClampSampler, uv - halfTexel);
|
|
|
|
|
|
|
|
const Vec4 minDepths4 = min(depths1, depths2);
|
|
const Vec4 minDepths4 = min(depths1, depths2);
|
|
|
const Vec4 maxDepths4 = max(depths1, depths2);
|
|
const Vec4 maxDepths4 = max(depths1, depths2);
|
|
@@ -77,8 +69,8 @@ void getMinMaxWorldPositions(texture2D tex, Vec2 uv, Mat4 clipToWorldMat, out Ve
|
|
|
const F32 minDepth = min(minDepths2.x, minDepths2.y);
|
|
const F32 minDepth = min(minDepths2.x, minDepths2.y);
|
|
|
const F32 maxDepth = max(maxDepths2.x, maxDepths2.y);
|
|
const F32 maxDepth = max(maxDepths2.x, maxDepths2.y);
|
|
|
|
|
|
|
|
- const Vec3 a = clipToWorld(Vec4(UV_TO_NDC(uv), minDepth, 1.0), clipToWorldMat);
|
|
|
|
|
- const Vec3 b = clipToWorld(Vec4(UV_TO_NDC(uv), maxDepth, 1.0), clipToWorldMat);
|
|
|
|
|
|
|
+ const Vec3 a = clipToWorld(Vec4(uvToNdc(uv), minDepth, 1.0), clipToWorldMat);
|
|
|
|
|
+ const Vec3 b = clipToWorld(Vec4(uvToNdc(uv), maxDepth, 1.0), clipToWorldMat);
|
|
|
|
|
|
|
|
aabbMin = min(a, b);
|
|
aabbMin = min(a, b);
|
|
|
aabbMax = max(a, b);
|
|
aabbMax = max(a, b);
|
|
@@ -88,15 +80,15 @@ F32 computeRejectionFactor(Vec2 uv, Vec2 historyUv)
|
|
|
{
|
|
{
|
|
|
Vec3 boxMin;
|
|
Vec3 boxMin;
|
|
|
Vec3 boxMax;
|
|
Vec3 boxMax;
|
|
|
- getMinMaxWorldPositions(u_currentDepthTex, uv, u_unis.m_viewProjectionInvMat, boxMin, boxMax);
|
|
|
|
|
|
|
+ getMinMaxWorldPositions(g_currentDepthTex, uv, g_unis.m_viewProjectionInvMat, boxMin, boxMax);
|
|
|
|
|
|
|
|
#if 0
|
|
#if 0
|
|
|
- const F32 historyDepth = textureLod(u_historyDepthTex, u_linearAnyClampSampler, historyUv, 0.0).r;
|
|
|
|
|
- const Vec3 historyWorldPos = clipToWorld(Vec4(UV_TO_NDC(historyUv), historyDepth, 1.0), u_unis.m_prevViewProjectionInvMat);
|
|
|
|
|
|
|
+ const F32 historyDepth = g_historyDepthTex.SampleLevel(g_linearAnyClampSampler, historyUv, 0.0).r;
|
|
|
|
|
+ const Vec3 historyWorldPos = clipToWorld(Vec4(uvToNdc(historyUv), historyDepth, 1.0), g_unis.m_prevViewProjectionInvMat);
|
|
|
#else
|
|
#else
|
|
|
// Average gives more rejection so less ghosting
|
|
// Average gives more rejection so less ghosting
|
|
|
const Vec3 historyWorldPos =
|
|
const Vec3 historyWorldPos =
|
|
|
- getAverageWorldPosition(u_historyDepthTex, historyUv, u_unis.m_prevViewProjectionInvMat);
|
|
|
|
|
|
|
+ getAverageWorldPosition(g_historyDepthTex, historyUv, g_unis.m_prevViewProjectionInvMat);
|
|
|
#endif
|
|
#endif
|
|
|
const Vec3 clampedHistoryWorldPos = clamp(historyWorldPos, boxMin, boxMax);
|
|
const Vec3 clampedHistoryWorldPos = clamp(historyWorldPos, boxMin, boxMax);
|
|
|
|
|
|
|
@@ -117,21 +109,29 @@ F32 computeRejectionFactor(Vec2 uv, Vec2 historyUv)
|
|
|
return rejection;
|
|
return rejection;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void main()
|
|
|
|
|
|
|
+#if defined(ANKI_COMPUTE_SHADER)
|
|
|
|
|
+[numthreads(8, 8, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID)
|
|
|
|
|
+#else
|
|
|
|
|
+struct FragOut
|
|
|
|
|
+{
|
|
|
|
|
+ Vec2 m_motionVectors : SV_TARGET0;
|
|
|
|
|
+ F32 m_historyLength : SV_TARGET1;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+FragOut main(Vec2 uv : TEXCOORD)
|
|
|
|
|
+#endif
|
|
|
{
|
|
{
|
|
|
#if defined(ANKI_COMPUTE_SHADER)
|
|
#if defined(ANKI_COMPUTE_SHADER)
|
|
|
- if(skipOutOfBoundsInvocations(kWorkgroupSize, kFramebufferSize))
|
|
|
|
|
|
|
+ if(skipOutOfBoundsInvocations(UVec2(8, 8), kFramebufferSize, svDispatchThreadId.xy))
|
|
|
{
|
|
{
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const Vec2 uv = (Vec2(gl_GlobalInvocationID.xy) + 0.5) / Vec2(kFramebufferSize);
|
|
|
|
|
-#else
|
|
|
|
|
- const Vec2 uv = in_uv;
|
|
|
|
|
|
|
+ const Vec2 uv = (Vec2(svDispatchThreadId.xy) + 0.5) / Vec2(kFramebufferSize);
|
|
|
#endif
|
|
#endif
|
|
|
- const F32 depth = textureLod(u_currentDepthTex, u_linearAnyClampSampler, uv, 0.0).r;
|
|
|
|
|
|
|
+ const F32 depth = g_currentDepthTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).r;
|
|
|
|
|
|
|
|
- const Vec2 velocity = textureLod(u_velocityTex, u_linearAnyClampSampler, uv, 0.0).rg;
|
|
|
|
|
|
|
+ const Vec2 velocity = g_velocityTex.SampleLevel(g_linearAnyClampSampler, uv, 0.0).rg;
|
|
|
|
|
|
|
|
Vec2 historyUv;
|
|
Vec2 historyUv;
|
|
|
if(velocity.x != 1.0)
|
|
if(velocity.x != 1.0)
|
|
@@ -140,8 +140,8 @@ void main()
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- const Vec4 v4 = u_unis.m_reprojectionMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
|
|
|
|
|
- historyUv = NDC_TO_UV(v4.xy / v4.w);
|
|
|
|
|
|
|
+ const Vec4 v4 = mul(g_unis.m_reprojectionMat, Vec4(uvToNdc(uv), depth, 1.0));
|
|
|
|
|
+ historyUv = ndcToUv(v4.xy / v4.w);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const F32 rejection = computeRejectionFactor(uv, historyUv);
|
|
const F32 rejection = computeRejectionFactor(uv, historyUv);
|
|
@@ -155,16 +155,18 @@ void main()
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- historyLength = textureLod(u_historyLengthTex, u_linearAnyClampSampler, historyUv, 0.0).r;
|
|
|
|
|
|
|
+ historyLength = g_historyLengthTex.SampleLevel(g_linearAnyClampSampler, historyUv, 0.0).r;
|
|
|
historyLength += 1.0 / kMaxHistoryLength;
|
|
historyLength += 1.0 / kMaxHistoryLength;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Write out
|
|
// Write out
|
|
|
#if defined(ANKI_COMPUTE_SHADER)
|
|
#if defined(ANKI_COMPUTE_SHADER)
|
|
|
- imageStore(u_motionVectorsImage, IVec2(gl_GlobalInvocationID.xy), Vec4(historyUv - uv, 0.0, 0.0));
|
|
|
|
|
- imageStore(u_historyLengthImage, IVec2(gl_GlobalInvocationID.xy), Vec4(historyLength, 0.0, 0.0, 0.0));
|
|
|
|
|
|
|
+ g_motionVectorsUav[svDispatchThreadId.xy] = historyUv - uv;
|
|
|
|
|
+ g_historyLengthUav[svDispatchThreadId.xy] = historyLength;
|
|
|
#else
|
|
#else
|
|
|
- out_motionVectors = historyUv - uv;
|
|
|
|
|
- out_historyLength = historyLength;
|
|
|
|
|
|
|
+ FragOut output;
|
|
|
|
|
+ output.m_motionVectors = historyUv - uv;
|
|
|
|
|
+ output.m_historyLength = historyLength;
|
|
|
|
|
+ return output;
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|