Skip to content

Commit

Permalink
Implement UI controls for linear and ACES filmic tonemapping
Browse files Browse the repository at this point in the history
  • Loading branch information
Nelarius committed Dec 3, 2023
1 parent 1814510 commit 37bfce8
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 16 deletions.
34 changes: 29 additions & 5 deletions src/pt/main.cpp
Expand Up @@ -101,15 +101,22 @@ int main(int argc, char** argv)
}();

{
nlrs::Extent2i curFramebufferSize = window.resolution();
float vfovDegrees = 70.0f;
int numSamplesPerPixel = 128;
int numBounces = 4;
nlrs::Extent2i curFramebufferSize = window.resolution();
// camera
float vfovDegrees = 70.0f;
// sampling
int numSamplesPerPixel = 128;
int numBounces = 4;
// sky
float sunZenithDegrees = 30.0f;
float sunAzimuthDegrees = 0.0f;
float skyTurbidity = 1.0f;
std::array<float, 3> skyAlbedo = {1.0f, 1.0f, 1.0f};
auto lastTime = std::chrono::steady_clock::now();
// tonemapping
int exposureStops = 4;
int tonemapFn = 1;
// timestep
auto lastTime = std::chrono::steady_clock::now();
while (!glfwWindowShouldClose(window.ptr()))
{
const auto currentTime = std::chrono::steady_clock::now();
Expand Down Expand Up @@ -193,6 +200,16 @@ int main(int argc, char** argv)
ImGui::SliderFloat("camera vfov", &vfovDegrees, 10.0f, 120.0f);
cameraController.vfov() = nlrs::Angle::degrees(vfovDegrees);

ImGui::Separator();
ImGui::Text("Post processing");

ImGui::SliderInt("exposure stops", &exposureStops, 0, 10);
ImGui::Text("tonemap fn");
ImGui::SameLine();
ImGui::RadioButton("linear", &tonemapFn, 0);
ImGui::SameLine();
ImGui::RadioButton("filmic", &tonemapFn, 1);

ImGui::Separator();
ImGui::Text("Camera");
{
Expand Down Expand Up @@ -240,6 +257,13 @@ int main(int argc, char** argv)
},
};
renderer.setRenderParameters(renderParams);

const nlrs::PostProcessingParameters postProcessingParams{
static_cast<std::uint32_t>(exposureStops),
static_cast<nlrs::Tonemapping>(tonemapFn),
};
renderer.setPostProcessingParameters(postProcessingParams);

renderer.render(gpuContext, gui);
}

Expand Down
37 changes: 33 additions & 4 deletions src/pt/raytracer.wgsl
Expand Up @@ -25,7 +25,8 @@ fn vsMain(in: VertexInput) -> VertexOutput {

// render params bind group
@group(1) @binding(0) var<uniform> renderParams: RenderParams;
@group(1) @binding(1) var<storage, read_write> skyState: SkyState;
@group(1) @binding(1) var<uniform> postProcessingParams: PostProcessingParams;
@group(1) @binding(2) var<storage, read_write> skyState: SkyState;

// scene bind group
// TODO: these are `read` only buffers. How can I create a buffer layout type which allows this?
Expand Down Expand Up @@ -67,7 +68,12 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
}

let estimator = imageBuffer[idx] / f32(accumulatedSampleCount);
let rgb = expose(estimator, 0.17f);

let stops = f32(postProcessingParams.stops);
let exposure = 1f / pow(2f, stops);

let tonemapFn = postProcessingParams.tonemapFn;
let rgb = expose(tonemapFn, exposure * estimator);

return vec4f(rgb, 1f);
}
Expand Down Expand Up @@ -110,6 +116,11 @@ struct SamplingState {
accumulatedSampleCount: u32,
}

struct PostProcessingParams {
stops: u32,
tonemapFn: u32,
}

struct SkyState {
params: array<f32, 27>,
radiances: array<f32, 3>,
Expand Down Expand Up @@ -243,8 +254,26 @@ fn radiance(theta: f32, gamma: f32, channel: u32) -> f32 {
}

@must_use
fn expose(v: vec3f, exposure: f32) -> vec3f {
return vec3(2.0f) / (vec3(1.0f) + exp(-exposure * v)) - vec3(1.0f);
fn expose(tonemapFn: u32, x: vec3f) -> vec3f {
switch tonemapFn {
case 1u: {
return acesFilmic(x);
}

default: {
return x;
}
}
}

@must_use
fn acesFilmic(x: vec3f) -> vec3f {
let a = 2.51f;
let b = 0.03f;
let c = 2.43f;
let d = 0.59f;
let e = 0.14f;
return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
}

fn evalImplicitLambertian(hit: Intersection, rngState: ptr<function, u32>) -> Scatter {
Expand Down
27 changes: 23 additions & 4 deletions src/pt/renderer.cpp
Expand Up @@ -201,6 +201,11 @@ Renderer::Renderer(
"render params buffer",
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform,
sizeof(RenderParamsLayout)),
postProcessingParamsBuffer(
gpuContext.device,
"post processing params buffer",
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform,
sizeof(PostProcessingParameters)),
skyStateBuffer(
gpuContext.device,
"sky state buffer",
Expand Down Expand Up @@ -244,6 +249,7 @@ Renderer::Renderer(
sizeof(TimestampsLayout)),
renderPipeline(nullptr),
currentRenderParams(rendererDesc.renderParams),
currentPostProcessingParams(),
frameCount(0),
accumulatedSampleCount(0),
timestampBufferMapContext{&timestampBuffer, &drawDurationsNs, &renderPassDurationsNs}
Expand Down Expand Up @@ -452,9 +458,10 @@ Renderer::Renderer(

// renderParams group layout

const std::array<WGPUBindGroupLayoutEntry, 2> renderParamsBindGroupLayoutEntries{
const std::array<WGPUBindGroupLayoutEntry, 3> renderParamsBindGroupLayoutEntries{
renderParamsBuffer.bindGroupLayoutEntry(0, WGPUShaderStage_Fragment),
skyStateBuffer.bindGroupLayoutEntry(1, WGPUShaderStage_Fragment),
postProcessingParamsBuffer.bindGroupLayoutEntry(1, WGPUShaderStage_Fragment),
skyStateBuffer.bindGroupLayoutEntry(2, WGPUShaderStage_Fragment),
};

const WGPUBindGroupLayoutDescriptor renderParamsBindGroupLayoutDesc{
Expand Down Expand Up @@ -534,9 +541,10 @@ Renderer::Renderer(

// renderParams bind group

const std::array<WGPUBindGroupEntry, 2> renderParamsBindGroupEntries{
const std::array<WGPUBindGroupEntry, 3> renderParamsBindGroupEntries{
renderParamsBuffer.bindGroupEntry(0),
skyStateBuffer.bindGroupEntry(1),
postProcessingParamsBuffer.bindGroupEntry(1),
skyStateBuffer.bindGroupEntry(2),
};

const WGPUBindGroupDescriptor renderParamsBindGroupDesc{
Expand Down Expand Up @@ -662,6 +670,11 @@ void Renderer::setRenderParameters(const RenderParameters& renderParams)
}
}

void Renderer::setPostProcessingParameters(const PostProcessingParameters& postProcessingParameters)
{
currentPostProcessingParams = postProcessingParameters;
}

void Renderer::render(const GpuContext& gpuContext, Gui& gui)
{
const WGPUTextureView nextTexture = wgpuSwapChainGetCurrentTextureView(gpuContext.swapChain);
Expand All @@ -687,6 +700,12 @@ void Renderer::render(const GpuContext& gpuContext, Gui& gui)
sizeof(RenderParamsLayout));
accumulatedSampleCount = std::min(
accumulatedSampleCount + 1, currentRenderParams.samplingParams.numSamplesPerPixel);
wgpuQueueWriteBuffer(
gpuContext.queue,
postProcessingParamsBuffer.handle(),
0,
&currentPostProcessingParams,
sizeof(PostProcessingParameters));
const SkyStateLayout skyStateLayout{currentRenderParams.sky};
wgpuQueueWriteBuffer(
gpuContext.queue, skyStateBuffer.handle(), 0, &skyStateLayout, sizeof(SkyStateLayout));
Expand Down
23 changes: 20 additions & 3 deletions src/pt/renderer.hpp
Expand Up @@ -47,6 +47,20 @@ struct RenderParameters
bool operator==(const RenderParameters&) const noexcept = default;
};

enum class Tonemapping : std::uint32_t
{
Linear = 0,
Filmic,
};

struct PostProcessingParameters
{
// Exposure is calculated as 1 / (2 ^ stops), stops >= 0. Increasing a stop by one halves the
// exposure.
std::uint32_t stops = 0;
Tonemapping tonemapping = Tonemapping::Filmic;
};

struct PositionAttribute
{
glm::vec3 p0; // offset: 0, size: 12
Expand Down Expand Up @@ -97,6 +111,7 @@ struct Renderer
GpuBuffer uniformsBuffer;
WGPUBindGroup uniformsBindGroup;
GpuBuffer renderParamsBuffer;
GpuBuffer postProcessingParamsBuffer;
GpuBuffer skyStateBuffer;
WGPUBindGroup renderParamsBindGroup;
GpuBuffer bvhNodeBuffer;
Expand All @@ -112,9 +127,10 @@ struct Renderer
GpuBuffer timestampBuffer;
WGPURenderPipeline renderPipeline;

RenderParameters currentRenderParams;
std::uint32_t frameCount;
std::uint32_t accumulatedSampleCount;
RenderParameters currentRenderParams;
PostProcessingParameters currentPostProcessingParams;
std::uint32_t frameCount;
std::uint32_t accumulatedSampleCount;

std::deque<std::uint64_t> drawDurationsNs;
std::deque<std::uint64_t> renderPassDurationsNs;
Expand All @@ -132,6 +148,7 @@ struct Renderer
~Renderer();

void setRenderParameters(const RenderParameters&);
void setPostProcessingParameters(const PostProcessingParameters&);
void render(const GpuContext&, Gui&);

float averageDrawDurationMs() const;
Expand Down

0 comments on commit 37bfce8

Please sign in to comment.