Skip to content

Commit 29eebc8

Browse files
committed
Simple temporal accumulation with moving average
1 parent d06c8c9 commit 29eebc8

File tree

9 files changed

+141
-30
lines changed

9 files changed

+141
-30
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ set(TESTS_SOURCE_FILES
128128
bvh.cpp
129129
gltf.cpp
130130
intersection.cpp
131+
math.cpp
131132
pt_format.cpp
132133
stream.cpp
133134
vector_set.cpp)

src/common/math.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include <cmath>
4+
5+
namespace nlrs
6+
{
7+
inline float fract(const float x)
8+
{
9+
if (x >= 0.0f)
10+
{
11+
return x - std::floor(x);
12+
}
13+
else
14+
{
15+
return x - std::ceil(x);
16+
}
17+
}
18+
} // namespace nlrs

src/common/r_sequence.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include "math.hpp"
4+
5+
#include <glm/glm.hpp>
6+
7+
#include <cmath>
8+
9+
namespace nlrs
10+
{
11+
inline glm::vec2 r2Sequence(const std::uint32_t n, const std::uint32_t sequenceLength)
12+
{
13+
// https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
14+
constexpr float G = 1.32471795f;
15+
constexpr float A1 = 1.0f / G;
16+
constexpr float A2 = 1.0f / (G * G);
17+
18+
const float i = static_cast<float>(n % sequenceLength);
19+
const float x = fract(0.5f + A1 * i);
20+
const float y = fract(0.5f + A2 * i);
21+
return glm::vec2(x, y);
22+
}
23+
} // namespace nlrs

src/pt/deferred_renderer.cpp

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "window.hpp"
99

1010
#include <common/assert.hpp>
11+
#include <common/r_sequence.hpp>
1112

1213
#include <algorithm>
1314
#include <array>
@@ -107,7 +108,8 @@ DeferredRenderer::DeferredRenderer(
107108
mLightingPass(),
108109
mResolvePass(),
109110
mGbufferPassDurationsNs(),
110-
mLightingPassDurationsNs()
111+
mLightingPassDurationsNs(),
112+
mFrameCount(0)
111113
{
112114
{
113115
const std::array<WGPUTextureFormat, 1> depthFormats{
@@ -240,6 +242,7 @@ DeferredRenderer::DeferredRenderer(DeferredRenderer&& other)
240242
mResolvePass = std::move(other.mResolvePass);
241243
mGbufferPassDurationsNs = std::move(other.mGbufferPassDurationsNs);
242244
mLightingPassDurationsNs = std::move(other.mLightingPassDurationsNs);
245+
mFrameCount = other.mFrameCount;
243246
}
244247
}
245248

@@ -270,6 +273,7 @@ DeferredRenderer& DeferredRenderer::operator=(DeferredRenderer&& other)
270273
mResolvePass = std::move(other.mResolvePass);
271274
mGbufferPassDurationsNs = std::move(other.mGbufferPassDurationsNs);
272275
mLightingPassDurationsNs = std::move(other.mLightingPassDurationsNs);
276+
mFrameCount = other.mFrameCount;
273277
}
274278
return *this;
275279
}
@@ -293,13 +297,25 @@ void DeferredRenderer::render(
293297
return wgpuDeviceCreateCommandEncoder(gpuContext.device, &cmdEncoderDesc);
294298
}();
295299

300+
const Extent2f framebufferSize = Extent2f(renderDesc.framebufferSize);
301+
const std::uint32_t frameCount = mFrameCount++;
302+
const glm::mat4 jitterMat = [framebufferSize, frameCount]() -> glm::mat4 {
303+
glm::mat4 jitterMat = glm::mat4(1.0f);
304+
const glm::vec2 j = r2Sequence(frameCount, 1 << 20);
305+
jitterMat[3][0] = (j.x - 0.5f) / framebufferSize.x;
306+
jitterMat[3][1] = (j.y - 0.5f) / framebufferSize.y;
307+
return jitterMat;
308+
}();
309+
310+
// GBuffer pass
311+
296312
wgpuCommandEncoderWriteTimestamp(
297313
encoder,
298314
mQuerySet,
299315
offsetof(TimestampsLayout, gbufferPassStart) / TimestampsLayout::MEMBER_SIZE);
300316
mGbufferPass.render(
301317
gpuContext,
302-
renderDesc.viewReverseZProjectionMatrix,
318+
jitterMat * renderDesc.viewReverseZProjectionMatrix,
303319
encoder,
304320
mDepthTextureView,
305321
mAlbedoTextureView,
@@ -309,27 +325,31 @@ void DeferredRenderer::render(
309325
mQuerySet,
310326
offsetof(TimestampsLayout, gbufferPassEnd) / TimestampsLayout::MEMBER_SIZE);
311327

328+
// Lighting pass
329+
312330
wgpuCommandEncoderWriteTimestamp(
313331
encoder,
314332
mQuerySet,
315333
offsetof(TimestampsLayout, lightingPassStart) / TimestampsLayout::MEMBER_SIZE);
316-
const Extent2f framebufferSize = Extent2f(renderDesc.framebufferSize);
317334
{
318335
const glm::mat4 inverseViewProjectionMat =
319-
glm::inverse(renderDesc.viewReverseZProjectionMatrix);
336+
glm::inverse(jitterMat * renderDesc.viewReverseZProjectionMatrix);
320337
mLightingPass.render(
321338
gpuContext,
322339
encoder,
323340
inverseViewProjectionMat,
324341
renderDesc.cameraPosition,
325342
framebufferSize,
326-
renderDesc.sky);
343+
renderDesc.sky,
344+
frameCount);
327345
}
328346
wgpuCommandEncoderWriteTimestamp(
329347
encoder,
330348
mQuerySet,
331349
offsetof(TimestampsLayout, lightingPassEnd) / TimestampsLayout::MEMBER_SIZE);
332350

351+
// Resolve pass
352+
333353
wgpuCommandEncoderWriteTimestamp(
334354
encoder,
335355
mQuerySet,
@@ -341,13 +361,16 @@ void DeferredRenderer::render(
341361
renderDesc.targetTextureView,
342362
framebufferSize,
343363
renderDesc.exposure,
364+
frameCount,
344365
gui);
345366
}
346367
wgpuCommandEncoderWriteTimestamp(
347368
encoder,
348369
mQuerySet,
349370
offsetof(TimestampsLayout, resolvePassEnd) / TimestampsLayout::MEMBER_SIZE);
350371

372+
// Resolve timestamp queries
373+
351374
wgpuCommandEncoderResolveQuerySet(
352375
encoder, mQuerySet, 0, TimestampsLayout::QUERY_COUNT, mQueryBuffer.ptr(), 0);
353376
wgpuCommandEncoderCopyBufferToBuffer(
@@ -676,15 +699,15 @@ DeferredRenderer::GbufferPass::GbufferPass(
676699
gpuContext.device,
677700
"Uniform buffer",
678701
{GpuBufferUsage::Uniform, GpuBufferUsage::CopyDst},
679-
sizeof(glm::mat4)),
702+
sizeof(Uniforms)),
680703
mUniformBindGroup(),
681704
mSamplerBindGroup(),
682705
mPipeline(nullptr)
683706
{
684707
const GpuBindGroupLayout uniformBindGroupLayout{
685708
gpuContext.device,
686709
"Uniform bind group layout",
687-
mUniformBuffer.bindGroupLayoutEntry(0, WGPUShaderStage_Vertex, sizeof(glm::mat4))};
710+
mUniformBuffer.bindGroupLayoutEntry(0, WGPUShaderStage_Vertex, sizeof(Uniforms))};
688711

689712
mUniformBindGroup = GpuBindGroup{
690713
gpuContext.device,
@@ -990,12 +1013,8 @@ void DeferredRenderer::GbufferPass::render(
9901013
const WGPUTextureView albedoTextureView,
9911014
const WGPUTextureView normalTextureView)
9921015
{
993-
wgpuQueueWriteBuffer(
994-
gpuContext.queue,
995-
mUniformBuffer.ptr(),
996-
0,
997-
&viewReverseZProjectionMatrix[0][0],
998-
sizeof(glm::mat4));
1016+
const Uniforms uniforms{viewReverseZProjectionMatrix};
1017+
wgpuQueueWriteBuffer(gpuContext.queue, mUniformBuffer.ptr(), 0, &uniforms, sizeof(Uniforms));
9991018

10001019
const WGPURenderPassEncoder renderPassEncoder = [cmdEncoder,
10011020
depthTextureView,
@@ -1644,7 +1663,6 @@ DeferredRenderer::LightingPass::LightingPass(LightingPass&& other) noexcept
16441663
mSampleBindGroup = std::move(other.mSampleBindGroup);
16451664
mPipeline = other.mPipeline;
16461665
other.mPipeline = nullptr;
1647-
mFrameCount = other.mFrameCount;
16481666
}
16491667
}
16501668

@@ -1670,7 +1688,6 @@ DeferredRenderer::LightingPass& DeferredRenderer::LightingPass::operator=(
16701688
computePipelineSafeRelease(mPipeline);
16711689
mPipeline = other.mPipeline;
16721690
other.mPipeline = nullptr;
1673-
mFrameCount = other.mFrameCount;
16741691
}
16751692
return *this;
16761693
}
@@ -1681,7 +1698,8 @@ void DeferredRenderer::LightingPass::render(
16811698
const glm::mat4& inverseViewReverseZProjectionMatrix,
16821699
const glm::vec3& cameraPosition,
16831700
const Extent2f& fbsize,
1684-
const Sky& sky)
1701+
const Sky& sky,
1702+
const std::uint32_t frameCount)
16851703
{
16861704
if (mCurrentSky != sky)
16871705
{
@@ -1696,7 +1714,7 @@ void DeferredRenderer::LightingPass::render(
16961714
inverseViewReverseZProjectionMatrix,
16971715
glm::vec4(cameraPosition, 1.f),
16981716
glm::vec2(fbsize.x, fbsize.y),
1699-
mFrameCount++,
1717+
frameCount,
17001718
0};
17011719
wgpuQueueWriteBuffer(
17021720
gpuContext.queue, mUniformBuffer.ptr(), 0, &uniforms, sizeof(Uniforms));
@@ -1953,10 +1971,11 @@ void DeferredRenderer::ResolvePass::render(
19531971
WGPUTextureView targetTextureView,
19541972
const Extent2f& fbsize,
19551973
const float exposure,
1974+
const std::uint32_t frameCount,
19561975
Gui& gui)
19571976
{
19581977
{
1959-
const Uniforms uniforms{glm::vec2(fbsize.x, fbsize.y), exposure, 0.f};
1978+
const Uniforms uniforms{glm::vec2(fbsize.x, fbsize.y), exposure, frameCount};
19601979
wgpuQueueWriteBuffer(
19611980
gpuContext.queue, mUniformBuffer.ptr(), 0, &uniforms, sizeof(Uniforms));
19621981
}

src/pt/deferred_renderer.hpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ class DeferredRenderer
106106
GpuBindGroup mSamplerBindGroup{};
107107
WGPURenderPipeline mPipeline = nullptr;
108108

109+
struct Uniforms
110+
{
111+
glm::mat4 viewProjectionMat;
112+
};
113+
109114
public:
110115
GbufferPass() = default;
111116
GbufferPass(const GpuContext&, const DeferredRendererDescriptor&);
@@ -184,8 +189,6 @@ class DeferredRenderer
184189
GpuBindGroup mSampleBindGroup = GpuBindGroup{};
185190
WGPUComputePipeline mPipeline = nullptr;
186191

187-
std::uint32_t mFrameCount = 0;
188-
189192
struct Uniforms
190193
{
191194
glm::mat4 inverseViewReverseZProjectionMat;
@@ -221,7 +224,8 @@ class DeferredRenderer
221224
const glm::mat4& inverseViewProjection,
222225
const glm::vec3& cameraPosition,
223226
const Extent2f& framebufferSize,
224-
const Sky& sky);
227+
const Sky& sky,
228+
std::uint32_t frameCount);
225229
void resize(
226230
const GpuContext&,
227231
WGPUTextureView albedoTextureView,
@@ -241,9 +245,9 @@ class DeferredRenderer
241245

242246
struct Uniforms
243247
{
244-
glm::vec2 framebufferSize;
245-
float exposure;
246-
float _padding;
248+
glm::vec2 framebufferSize;
249+
float exposure;
250+
std::uint32_t frameCount;
247251
};
248252

249253
public:
@@ -266,6 +270,7 @@ class DeferredRenderer
266270
WGPUTextureView targetTextureView,
267271
const Extent2f& framebufferSize,
268272
float exposure,
273+
std::uint32_t frameCount,
269274
Gui& gui);
270275
};
271276

@@ -286,5 +291,6 @@ class DeferredRenderer
286291
std::deque<std::uint64_t> mGbufferPassDurationsNs;
287292
std::deque<std::uint64_t> mLightingPassDurationsNs;
288293
std::deque<std::uint64_t> mResolvePassDurationsNs;
294+
std::uint32_t mFrameCount;
289295
};
290296
} // namespace nlrs

src/pt/deferred_renderer_gbuffer_pass.wgsl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
@group(0) @binding(0) var<uniform> viewProjectionMat: mat4x4f;
1+
struct Uniforms {
2+
viewReverseZProjectionMat: mat4x4f
3+
}
4+
5+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
26

37
struct VertexInput {
48
@location(0) position: vec4f,
@@ -15,7 +19,7 @@ struct VertexOutput {
1519
@vertex
1620
fn vsMain(in: VertexInput) -> VertexOutput {
1721
var out: VertexOutput;
18-
out.position = viewProjectionMat * in.position;
22+
out.position = uniforms.viewReverseZProjectionMat * in.position;
1923
out.normal = in.normal;
2024
out.texCoord = in.texCoord;
2125
return out;

src/pt/deferred_renderer_resolve_pass.wgsl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fn vsMain(in: VertexInput) -> VertexOutput {
2020
struct Uniforms {
2121
framebufferSize: vec2f,
2222
exposure: f32,
23+
frameCount: u32,
2324
}
2425

2526
// TODO: consider merging these into one bind group
@@ -35,7 +36,17 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
3536
let textureIdx = vec2u(floor(uv * uniforms.framebufferSize));
3637
let sampleBufferIdx = textureIdx.y * u32(uniforms.framebufferSize.x) + textureIdx.x;
3738
let sample: array<f32, 3> = sampleBuffer[sampleBufferIdx];
38-
let color = vec3f(sample[0], sample[1], sample[2]);
39+
let currentColor = vec3f(sample[0], sample[1], sample[2]);
40+
var color = vec3f(0f);
41+
if uniforms.frameCount == 0u {
42+
accumulationBuffer[sampleBufferIdx] = sample;
43+
color = currentColor;
44+
} else {
45+
let previousSample: array<f32, 3> = accumulationBuffer[sampleBufferIdx];
46+
let previousColor = vec3f(previousSample[0], previousSample[1], previousSample[2]);
47+
color = 0.1 * currentColor + 0.9 * previousColor;
48+
accumulationBuffer[sampleBufferIdx] = array<f32, 3>(color.r, color.g, color.b);
49+
}
3950

4051
let rgb = acesFilmic(uniforms.exposure * color);
4152
let srgb = pow(rgb, vec3(1f / 2.2f));

src/pt/shader_source.hpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,11 @@ fn animatedBlueNoise(coord: vec2u, frameIdx: u32, totalSampleCount: u32) -> vec2
623623
}
624624
)";
625625

626-
const char* const DEFERRED_RENDERER_GBUFFER_PASS_SOURCE = R"(@group(0) @binding(0) var<uniform> viewProjectionMat: mat4x4f;
626+
const char* const DEFERRED_RENDERER_GBUFFER_PASS_SOURCE = R"(struct Uniforms {
627+
viewReverseZProjectionMat: mat4x4f
628+
}
629+
630+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
627631
628632
struct VertexInput {
629633
@location(0) position: vec4f,
@@ -640,7 +644,7 @@ struct VertexOutput {
640644
@vertex
641645
fn vsMain(in: VertexInput) -> VertexOutput {
642646
var out: VertexOutput;
643-
out.position = viewProjectionMat * in.position;
647+
out.position = uniforms.viewReverseZProjectionMat * in.position;
644648
out.normal = in.normal;
645649
out.texCoord = in.texCoord;
646650
return out;
@@ -1291,6 +1295,7 @@ fn vsMain(in: VertexInput) -> VertexOutput {
12911295
struct Uniforms {
12921296
framebufferSize: vec2f,
12931297
exposure: f32,
1298+
frameCount: u32,
12941299
}
12951300
12961301
// TODO: consider merging these into one bind group
@@ -1306,7 +1311,17 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
13061311
let textureIdx = vec2u(floor(uv * uniforms.framebufferSize));
13071312
let sampleBufferIdx = textureIdx.y * u32(uniforms.framebufferSize.x) + textureIdx.x;
13081313
let sample: array<f32, 3> = sampleBuffer[sampleBufferIdx];
1309-
let color = vec3f(sample[0], sample[1], sample[2]);
1314+
let currentColor = vec3f(sample[0], sample[1], sample[2]);
1315+
var color = vec3f(0f);
1316+
if uniforms.frameCount == 0u {
1317+
accumulationBuffer[sampleBufferIdx] = sample;
1318+
color = currentColor;
1319+
} else {
1320+
let previousSample: array<f32, 3> = accumulationBuffer[sampleBufferIdx];
1321+
let previousColor = vec3f(previousSample[0], previousSample[1], previousSample[2]);
1322+
color = 0.1 * currentColor + 0.9 * previousColor;
1323+
accumulationBuffer[sampleBufferIdx] = array<f32, 3>(color.r, color.g, color.b);
1324+
}
13101325
13111326
let rgb = acesFilmic(uniforms.exposure * color);
13121327
let srgb = pow(rgb, vec3(1f / 2.2f));

0 commit comments

Comments
 (0)