Graphics: Add gamma correction

This commit is contained in:
SirLynix
2023-09-10 14:34:56 +02:00
committed by Jérôme Leclercq
parent 041be74b9d
commit d40b8af68d
12 changed files with 269 additions and 10 deletions

View File

@@ -257,6 +257,9 @@ namespace Nz
if (passFlags.Test(FramePipelineExtraPass::DepthPrepass))
viewerData.depthPrepass = std::make_unique<DepthPipelinePass>(*this, m_elementRegistry, viewerInstance, depthPassIndex, "Depth pre-pass");
if (passFlags.Test(FramePipelineExtraPass::GammaCorrection))
viewerData.gammaCorrectionPass = std::make_unique<PostProcessPipelinePass>(*this, "Gamma correction", "PostProcess.GammaCorrection");
m_transferSet.insert(&viewerInstance->GetViewerInstance());
m_rebuildFrameGraph = true;
@@ -396,6 +399,9 @@ namespace Nz
if (viewerData.depthPrepass)
viewerData.depthPrepass->Prepare(renderFrame, frustum, visibleRenderables, depthVisibilityHash);
if (viewerData.gammaCorrectionPass)
viewerData.gammaCorrectionPass->Prepare(renderFrame);
viewerData.forwardPass->Prepare(renderFrame, frustum, visibleRenderables, m_visibleLights, visibilityHash);
if (viewerData.debugDrawPass)
@@ -611,7 +617,6 @@ namespace Nz
PixelFormat::RGBA8
});
viewerData.depthStencilAttachment = frameGraph.AddAttachment({
"Depth-stencil buffer",
Graphics::Instance()->GetPreferredDepthStencilFormat()
@@ -627,14 +632,25 @@ namespace Nz
lightData->shadowData->RegisterPassInputs(forwardPass);
}
viewerData.finalColorAttachment = viewerData.forwardColorAttachment;
if (viewerData.gammaCorrectionPass)
{
std::size_t postGammaColorAttachment = frameGraph.AddAttachment({
"Gamma-corrected output",
PixelFormat::RGBA8
});
viewerData.gammaCorrectionPass->RegisterToFrameGraph(frameGraph, viewerData.finalColorAttachment, postGammaColorAttachment);
viewerData.finalColorAttachment = postGammaColorAttachment;
}
if (viewerData.debugDrawPass)
{
viewerData.debugColorAttachment = frameGraph.AddAttachmentProxy("Debug draw output", viewerData.forwardColorAttachment);
viewerData.debugDrawPass->RegisterToFrameGraph(frameGraph, viewerData.forwardColorAttachment, viewerData.debugColorAttachment);
viewerData.debugColorAttachment = frameGraph.AddAttachmentProxy("Debug draw output", viewerData.finalColorAttachment);
viewerData.debugDrawPass->RegisterToFrameGraph(frameGraph, viewerData.finalColorAttachment, viewerData.debugColorAttachment);
viewerData.finalColorAttachment = viewerData.debugColorAttachment;
}
else
viewerData.finalColorAttachment = viewerData.forwardColorAttachment;
}
using ViewerPair = std::pair<const RenderTarget*, const ViewerData*>;

View File

@@ -0,0 +1,107 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/PostProcessPipelinePass.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/FrameGraph.hpp>
#include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
PostProcessPipelinePass::PostProcessPipelinePass(FramePipeline& owner, std::string passName, std::string shaderName) :
m_passName(std::move(passName)),
m_shader(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, std::move(shaderName)),
m_pipeline(owner)
{
RenderPipelineLayoutInfo layoutInfo;
layoutInfo.bindings.assign({
{
0, 0, 1,
ShaderBindingType::Sampler,
nzsl::ShaderStageType::Fragment
}
});
std::shared_ptr<RenderDevice> renderDevice = Graphics::Instance()->GetRenderDevice();
m_renderPipelineLayout = renderDevice->InstantiateRenderPipelineLayout(std::move(layoutInfo));
if (!m_renderPipelineLayout)
throw std::runtime_error("failed to instantiate postprocess RenderPipelineLayout");
m_onShaderUpdated.Connect(m_shader.OnShaderUpdated, [this](UberShader*)
{
BuildPipeline();
});
BuildPipeline();
}
void PostProcessPipelinePass::Prepare(RenderFrame& renderFrame)
{
if (m_nextRenderPipeline)
{
if (m_renderPipeline)
renderFrame.PushForRelease(std::move(m_renderPipeline));
m_renderPipeline = std::move(m_nextRenderPipeline);
m_rebuildFramePass = true;
}
}
FramePass& PostProcessPipelinePass::RegisterToFrameGraph(FrameGraph& frameGraph, std::size_t inputColorBufferIndex, std::size_t outputColorBufferIndex)
{
FramePass& postProcess = frameGraph.AddPass(m_passName);
postProcess.AddInput(inputColorBufferIndex);
postProcess.AddOutput(outputColorBufferIndex);
postProcess.SetExecutionCallback([&]()
{
return (m_rebuildFramePass) ? FramePassExecution::UpdateAndExecute : FramePassExecution::Execute;
});
postProcess.SetCommandCallback([this, inputColorBufferIndex](CommandBufferBuilder& builder, const FramePassEnvironment& env)
{
if (m_shaderBinding)
env.renderFrame.PushForRelease(std::move(m_shaderBinding));
auto& samplerCache = Graphics::Instance()->GetSamplerCache();
const auto& sourceTexture = env.frameGraph.GetAttachmentTexture(inputColorBufferIndex);
const auto& sampler = samplerCache.Get({});
m_shaderBinding = m_renderPipelineLayout->AllocateShaderBinding(0);
m_shaderBinding->Update({
{
0,
Nz::ShaderBinding::SampledTextureBinding {
sourceTexture.get(), sampler.get()
}
}
});
builder.SetScissor(env.renderRect);
builder.SetViewport(env.renderRect);
builder.BindRenderPipeline(*m_renderPipeline);
builder.BindRenderShaderBinding(0, *m_shaderBinding);
builder.Draw(3);
m_rebuildFramePass = false;
});
return postProcess;
}
void PostProcessPipelinePass::BuildPipeline()
{
std::shared_ptr<RenderDevice> renderDevice = Graphics::Instance()->GetRenderDevice();
RenderPipelineInfo pipelineInfo;
pipelineInfo.pipelineLayout = m_renderPipelineLayout;
pipelineInfo.shaderModules.push_back(m_shader.Get({}));
m_nextRenderPipeline = renderDevice->InstantiateRenderPipeline(pipelineInfo);
}
}

View File

@@ -0,0 +1,36 @@
[nzsl_version("1.0")]
module Math.Color;
// from https://en.wikipedia.org/wiki/SRGB
option ApproximatesRGB: bool = false;
[export]
fn LinearTosRGB(color: vec3[f32]) -> vec3[f32]
{
const if (!ApproximatesRGB)
{
return select(
color > (0.0031308).rrr,
1.055 * pow(color, (1.0 / 2.4).rrr) - (0.055).rrr,
12.92 * color
);
}
else
return pow(color, (1.0 / 2.2).rrr);
}
[export]
fn sRGBToLinear(color: vec3[f32]) -> vec3[f32]
{
const if (!ApproximatesRGB)
{
return select(
color > (0.04045).rrr,
pow((color + (0.055).rrr) / 1.055, (2.4).rrr),
color / 12.92
);
}
else
return pow(color, (2.2).rrr);
}

View File

@@ -0,0 +1,26 @@
[nzsl_version("1.0")]
module PostProcess.GammaCorrection;
import VertOut, VertexShader from Engine.FullscreenVertex;
import LinearTosRGB from Math.Color;
external
{
[binding(0)] colorTexture: sampler2D[f32]
}
struct FragOut
{
[location(0)] color: vec4[f32]
}
[entry(frag)]
fn main(input: VertOut) -> FragOut
{
let color = colorTexture.Sample(input.uv);
color.rgb = LinearTosRGB(color.rgb);
let output: FragOut;
output.color = color;
return output;
}

View File

@@ -122,7 +122,7 @@ namespace Nz
{
TextureParams texParams;
texParams.renderDevice = Graphics::Instance()->GetRenderDevice();
texParams.loadFormat = PixelFormat::RGBA8; //< TODO: Re-enable gamma correction
texParams.loadFormat = PixelFormat::RGBA8_SRGB;
auto CreateMaterialFromTexture = [&](std::shared_ptr<Texture> texture)
{