Archived
1
Fork 0

Seperate exposure pass from the rest of post processing

This prevents gamma-weirdness with SMAA
This commit is contained in:
Joshua Goins 2022-04-04 12:15:15 -04:00
parent b5655efe0e
commit ecea7f990e
9 changed files with 313 additions and 225 deletions

View file

@ -53,4 +53,6 @@ add_shaders(TARGET Renderer
shaders/dof.vert.glsl
shaders/dof.frag.glsl
shaders/histogram.comp.glsl
shaders/histogram-average.comp.glsl)
shaders/histogram-average.comp.glsl
shaders/expose.frag.glsl
shaders/expose.vert.glsl)

View file

@ -132,6 +132,7 @@ namespace prism {
GFXPipeline* sky_pipeline = nullptr;
// post
GFXPipeline* expose_pipeline = nullptr;
GFXPipeline* post_pipeline = nullptr;
// brdf

View file

@ -34,6 +34,10 @@ public:
GFXFramebuffer* edge_framebuffer = nullptr;
GFXFramebuffer* blend_framebuffer = nullptr;
// intermediary
GFXTexture* mid_texture = nullptr;
GFXFramebuffer* mid_framebuffer = nullptr;
// imgui
GFXBuffer* vertex_buffer[RT_MAX_FRAMES_IN_FLIGHT] = {};
int current_vertex_size[RT_MAX_FRAMES_IN_FLIGHT] = {};

View file

@ -197,14 +197,6 @@ void renderer::render(GFXCommandBuffer* commandbuffer, Scene* scene, RenderTarge
commandbuffer->pop_group();
if (render_options.enable_aa) {
commandbuffer->push_group("SMAA");
smaa_pass->render(commandbuffer, target);
commandbuffer->pop_group();
}
if (render_options.enable_depth_of_field && dof_pass != nullptr)
dof_pass->render(commandbuffer, *scene);
@ -213,30 +205,37 @@ void renderer::render(GFXCommandBuffer* commandbuffer, Scene* scene, RenderTarge
commandbuffer->end_render_pass();
// begin auto exposure
{
commandbuffer->set_compute_pipeline(histogram_pipeline);
commandbuffer->bind_texture(target.offscreenColorTexture, 0);
commandbuffer->bind_shader_buffer(histogram_buffer, 0, 1, sizeof(uint32_t) * 256);
commandbuffer->bind_shader_buffer(histogram_buffer, 0, 1,
sizeof(uint32_t) * 256);
const float lum_range = render_options.max_luminance - render_options.min_luminance;
const float lum_range =
render_options.max_luminance - render_options.min_luminance;
prism::float4 params = prism::float4(render_options.min_luminance,
1.0f / lum_range,
prism::float4 params =
prism::float4(render_options.min_luminance, 1.0f / lum_range,
static_cast<float>(render_extent.width),
static_cast<float>(render_extent.height));
commandbuffer->set_push_constant(&params, sizeof(prism::float4));
commandbuffer->dispatch(static_cast<uint32_t>(std::ceil(static_cast<float>(render_extent.width) / 16.0f)),
static_cast<uint32_t>(std::ceil(static_cast<float>(render_extent.height) / 16.0f)), 1);
commandbuffer->dispatch(
static_cast<uint32_t>(
std::ceil(static_cast<float>(render_extent.width) / 16.0f)),
static_cast<uint32_t>(
std::ceil(static_cast<float>(render_extent.height) / 16.0f)),
1);
commandbuffer->set_compute_pipeline(histogram_average_pipeline);
commandbuffer->bind_shader_buffer(histogram_buffer, 0, 1, sizeof(uint32_t) * 256);
commandbuffer->bind_shader_buffer(histogram_buffer, 0, 1,
sizeof(uint32_t) * 256);
params = prism::float4(render_options.min_luminance,
lum_range,
params = prism::float4(
render_options.min_luminance, lum_range,
std::clamp(1.0f - std::exp(-(1.0f / 60.0f) * 1.1f), 0.0f, 1.0f),
static_cast<float>(render_extent.width * render_extent.height));
@ -245,44 +244,8 @@ void renderer::render(GFXCommandBuffer* commandbuffer, Scene* scene, RenderTarge
commandbuffer->bind_texture(average_luminance_texture, 0);
commandbuffer->dispatch(1, 1, 1);
// continue post processing
beginInfo.framebuffer = nullptr;
beginInfo.render_pass = nullptr;
commandbuffer->set_render_pass(beginInfo);
Viewport viewport = {};
viewport.width = static_cast<float>(render_extent.width);
viewport.height = static_cast<float>(render_extent.height);
commandbuffer->set_viewport(viewport);
commandbuffer->set_graphics_pipeline(post_pipeline);
if (render_options.enable_depth_of_field)
commandbuffer->bind_texture(dof_pass->normal_field, 1);
else
commandbuffer->bind_texture(target.offscreenColorTexture, 1);
if (render_options.enable_aa) {
commandbuffer->bind_texture(target.blend_texture, 3);
} else {
commandbuffer->bind_texture(dummy_texture, 3);
}
if(auto texture = get_requested_texture(PassTextureType::SelectionSobel))
commandbuffer->bind_texture(texture, 5);
else
commandbuffer->bind_texture(dummy_texture, 5);
commandbuffer->bind_texture(average_luminance_texture, 6);
if(render_options.enable_depth_of_field)
commandbuffer->bind_texture(dof_pass->far_field, 7);
else
commandbuffer->bind_texture(dummy_texture, 7);
PostPushConstants pc;
pc.options.x = render_options.enable_aa;
pc.options.z = render_options.exposure;
@ -296,11 +259,83 @@ void renderer::render(GFXCommandBuffer* commandbuffer, Scene* scene, RenderTarge
const auto [width, height] = render_extent;
pc.viewport = prism::float4(1.0f / static_cast<float>(width), 1.0f / static_cast<float>(height), static_cast<float>(width), static_cast<float>(height));
// continue post processing
// first we want to manually "expose" the scene, otherwise AA wouldn't work properly
{
beginInfo.framebuffer = target.mid_framebuffer;
beginInfo.render_pass = unorm_render_pass;
commandbuffer->set_render_pass(beginInfo);
Viewport viewport = {};
viewport.width = static_cast<float>(render_extent.width);
viewport.height = static_cast<float>(render_extent.height);
commandbuffer->set_viewport(viewport);
commandbuffer->set_graphics_pipeline(expose_pipeline);
commandbuffer->set_push_constant(&pc, sizeof(PostPushConstants));
if (render_options.enable_depth_of_field)
commandbuffer->bind_texture(dof_pass->normal_field, 1);
else
commandbuffer->bind_texture(target.offscreenColorTexture, 1);
commandbuffer->bind_texture(average_luminance_texture, 6);
if(render_options.enable_depth_of_field)
commandbuffer->bind_texture(dof_pass->far_field, 7);
else
commandbuffer->bind_texture(dummy_texture, 7);
commandbuffer->draw(0, 4, 0, 1);
commandbuffer->end_render_pass();
}
if (render_options.enable_aa) {
commandbuffer->push_group("SMAA");
smaa_pass->render(commandbuffer, target);
commandbuffer->pop_group();
}
// now we overlay sobel, and perform smaa
{
beginInfo.framebuffer = nullptr;
beginInfo.render_pass = nullptr;
commandbuffer->set_render_pass(beginInfo);
Viewport viewport = {};
viewport.width = static_cast<float>(render_extent.width);
viewport.height = static_cast<float>(render_extent.height);
commandbuffer->set_viewport(viewport);
commandbuffer->set_graphics_pipeline(post_pipeline);
commandbuffer->bind_texture(target.mid_texture, 1);
if (render_options.enable_aa) {
commandbuffer->bind_texture(target.blend_texture, 3);
} else {
commandbuffer->bind_texture(dummy_texture, 3);
}
if(auto texture = get_requested_texture(PassTextureType::SelectionSobel))
commandbuffer->bind_texture(texture, 5);
else
commandbuffer->bind_texture(dummy_texture, 5);
commandbuffer->set_push_constant(&pc, sizeof(PostPushConstants));
commandbuffer->draw(0, 4, 0, 1);
commandbuffer->pop_group();
}
commandbuffer->push_group("Extra Passes");
@ -550,12 +585,15 @@ void renderer::create_dummy_texture() {
void renderer::create_render_target_resources(RenderTarget& target) {
const auto extent = target.get_render_extent();
// offscreen
{
GFXTextureCreateInfo textureInfo = {};
textureInfo.label = "Offscreen Color";
textureInfo.width = extent.width;
textureInfo.height = extent.height;
textureInfo.format = GFXPixelFormat::RGBA_32F;
textureInfo.usage = GFXTextureUsage::Attachment | GFXTextureUsage::Sampled | GFXTextureUsage::Storage;
textureInfo.usage = GFXTextureUsage::Attachment | GFXTextureUsage::Sampled |
GFXTextureUsage::Storage;
textureInfo.samplingMode = SamplingMode::ClampToEdge;
target.offscreenColorTexture = gfx->create_texture(textureInfo);
@ -572,29 +610,25 @@ void renderer::create_render_target_resources(RenderTarget& target) {
framebufferInfo.attachments.push_back(target.offscreenDepthTexture);
target.offscreenFramebuffer = gfx->create_framebuffer(framebufferInfo);
}
if(post_pipeline == nullptr) {
GFXGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.label = "Post";
// mid
{
GFXTextureCreateInfo textureInfo = {};
textureInfo.label = "Mid Color";
textureInfo.width = extent.width;
textureInfo.height = extent.height;
textureInfo.format = GFXPixelFormat::R8G8B8A8_UNORM;
textureInfo.usage = GFXTextureUsage::Attachment | GFXTextureUsage::Sampled;
textureInfo.samplingMode = SamplingMode::ClampToEdge;
pipelineInfo.shaders.vertex_src = ShaderSource(prism::path("post.vert"));
pipelineInfo.shaders.fragment_src = ShaderSource(prism::path("post.frag"));
target.mid_texture = gfx->create_texture(textureInfo);
pipelineInfo.shader_input.bindings = {
{4, GFXBindingType::PushConstant},
{1, GFXBindingType::Texture},
{2, GFXBindingType::Texture},
{3, GFXBindingType::Texture},
{5, GFXBindingType::Texture},
{6, GFXBindingType::Texture},
{7, GFXBindingType::Texture}
};
GFXFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.render_pass = unorm_render_pass;
framebufferInfo.attachments.push_back(target.mid_texture);
pipelineInfo.shader_input.push_constants = {
{sizeof(PostPushConstants), 0}
};
post_pipeline = gfx->create_graphics_pipeline(pipelineInfo);
target.mid_framebuffer = gfx->create_framebuffer(framebufferInfo);
}
target.sceneBuffer = gfx->create_buffer(nullptr, sizeof(SceneInformation), true, GFXBufferUsage::Storage);
@ -609,12 +643,9 @@ void renderer::create_post_pipelines() {
pipelineInfo.shader_input.bindings = {
{4, GFXBindingType::PushConstant},
{1, GFXBindingType::Texture},
{2, GFXBindingType::Texture},
{3, GFXBindingType::Texture},
{5, GFXBindingType::Texture},
{6, GFXBindingType::Texture},
{7, GFXBindingType::Texture}
{1, GFXBindingType::Texture}, // colorSampler
{3, GFXBindingType::Texture}, // blendSampler
{5, GFXBindingType::Texture} // sobelSampler
};
pipelineInfo.shader_input.push_constants = {
@ -622,6 +653,20 @@ void renderer::create_post_pipelines() {
};
post_pipeline = gfx->create_graphics_pipeline(pipelineInfo);
pipelineInfo.label = "Expose";
pipelineInfo.shaders.vertex_src = ShaderSource(prism::path("expose.vert"));
pipelineInfo.shaders.fragment_src = ShaderSource(prism::path("expose.frag"));
pipelineInfo.render_pass = unorm_render_pass;
pipelineInfo.shader_input.bindings = {
{4, GFXBindingType::PushConstant},
{1, GFXBindingType::Texture}, // colorSampler
{6, GFXBindingType::Texture}, // averageLuminanceSampler
{7, GFXBindingType::Texture} // farFieldSampler
};
expose_pipeline = gfx->create_graphics_pipeline(pipelineInfo);
}
void renderer::create_sky_pipeline() {

View file

@ -68,7 +68,7 @@ void SMAAPass::render(GFXCommandBuffer* command_buffer, RenderTarget& target) {
command_buffer->set_graphics_pipeline(edge_pipeline);
command_buffer->set_push_constant(&pc, sizeof(PushConstant));
command_buffer->bind_texture(target.offscreenColorTexture, 0); // color
command_buffer->bind_texture(target.mid_texture, 0); // color
command_buffer->bind_texture(target.offscreenDepthTexture, 1); // depth
command_buffer->draw(0, 3, 0, 1);

View file

@ -10,7 +10,8 @@ layout(binding = 0) uniform sampler2D imageSampler;
layout(binding = 1) uniform sampler2D depthSampler;
void main() {
vec2 edge = SMAALumaEdgeDetectionPS(inUV, inOffset, imageSampler, depthSampler);
//vec2 edge = SMAALumaEdgeDetectionPS(inUV, inOffset, imageSampler, depthSampler);
vec2 edge = SMAADepthEdgeDetectionPS(inUV, inOffset, depthSampler);
outColor = vec4(edge, 0.0, 1.0);
}

View file

@ -0,0 +1,119 @@
layout(push_constant) uniform PushConstant {
vec4 viewport;
vec4 options;
vec4 transform_ops;
};
#include "common.nocompile.glsl"
layout (location = 0) in vec2 inUV;
layout(location = 1) in vec4 inOffset;
layout (location = 0) out vec4 outColor;
layout (binding = 1) uniform sampler2D colorSampler;
layout (binding = 6) uniform sampler2D averageLuminanceSampler;
layout (binding = 7) uniform sampler2D farFieldSampler;
// adapted from https://bruop.github.io/exposure/
vec3 convertRGB2XYZ(vec3 _rgb)
{
// Reference:
// RGB/XYZ Matrices
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
vec3 xyz;
xyz.x = dot(vec3(0.4124564, 0.3575761, 0.1804375), _rgb);
xyz.y = dot(vec3(0.2126729, 0.7151522, 0.0721750), _rgb);
xyz.z = dot(vec3(0.0193339, 0.1191920, 0.9503041), _rgb);
return xyz;
}
vec3 convertXYZ2RGB(vec3 _xyz)
{
vec3 rgb;
rgb.x = dot(vec3( 3.2404542, -1.5371385, -0.4985314), _xyz);
rgb.y = dot(vec3(-0.9692660, 1.8760108, 0.0415560), _xyz);
rgb.z = dot(vec3( 0.0556434, -0.2040259, 1.0572252), _xyz);
return rgb;
}
vec3 convertXYZ2Yxy(vec3 _xyz)
{
// Reference:
// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_xyY.html
float inv = 1.0/dot(_xyz, vec3(1.0, 1.0, 1.0) );
return vec3(_xyz.y, _xyz.x*inv, _xyz.y*inv);
}
vec3 convertYxy2XYZ(vec3 _Yxy)
{
// Reference:
// http://www.brucelindbloom.com/index.html?Eqn_xyY_to_XYZ.html
vec3 xyz;
xyz.x = _Yxy.x*_Yxy.y/_Yxy.z;
xyz.y = _Yxy.x;
xyz.z = _Yxy.x*(1.0 - _Yxy.y - _Yxy.z)/_Yxy.z;
return xyz;
}
vec3 convertRGB2Yxy(vec3 _rgb)
{
return convertXYZ2Yxy(convertRGB2XYZ(_rgb) );
}
vec3 convertYxy2RGB(vec3 _Yxy)
{
return convertXYZ2RGB(convertYxy2XYZ(_Yxy) );
}
float reinhard2(float _x, float _whiteSqr)
{
return (_x * (1.0 + _x/_whiteSqr) ) / (1.0 + _x);
}
void main() {
// passthrough
if(options.w == 1) {
outColor = texture(colorSampler, inUV);
return;
}
bool enable_dof = options.w == 2;
vec3 sceneColor = vec3(0);
if(enable_dof) {
sceneColor = texture(farFieldSampler, inUV).rgb;
sceneColor += texture(colorSampler, inUV).rgb;
} else {
sceneColor = texture(colorSampler, inUV).rgb;
}
vec3 hdrColor = sceneColor; // fading removed
float avg_lum = texture(averageLuminanceSampler, inUV).r;
vec3 transformed_color = hdrColor;
if(transform_ops.y == 1) {
transformed_color = vec3(1.0) - exp(-transformed_color * options.z);
} else if(transform_ops.y == 2) {
vec3 Yxy = convertRGB2Yxy(transformed_color);
// hard-coded for now
float whitePoint = 4.0;
float lp = Yxy.x / (9.6 * avg_lum + 0.0001);
// Replace this line with other tone mapping functions
// Here we applying the curve to the luminance exclusively
Yxy.x = reinhard2(lp, whitePoint);
transformed_color = convertYxy2RGB(Yxy);
}
if(transform_ops.x == 1) {
transformed_color = from_linear_to_srgb(transformed_color);
}
outColor = vec4(transformed_color, 1.0);
}

View file

@ -0,0 +1,14 @@
layout(push_constant) uniform readonlPushConstant {
vec4 viewport;
float fade;
vec4 transform_ops;
};
layout (location = 0) out vec2 outUV;
layout(location = 1) out vec4 outOffset;
void main() {
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
outUV.y = 1.0 - outUV.y;
}

View file

@ -20,11 +20,8 @@ layout(location = 1) in vec4 inOffset;
layout (location = 0) out vec4 outColor;
layout (binding = 1) uniform sampler2D colorSampler;
layout (binding = 2) uniform sampler2D backSampler;
layout (binding = 3) uniform sampler2D blendSampler;
layout (binding = 5) uniform sampler2D sobelSampler;
layout (binding = 6) uniform sampler2D averageLuminanceSampler;
layout (binding = 7) uniform sampler2D farFieldSampler;
float calculate_sobel() {
float x = 1.0 / viewport.z;
@ -46,81 +43,13 @@ float calculate_sobel() {
return sqrt((horizEdge.rgb * horizEdge.rgb) + (vertEdge.rgb * vertEdge.rgb)).r;
}
// adapted from https://bruop.github.io/exposure/
vec3 convertRGB2XYZ(vec3 _rgb)
{
// Reference:
// RGB/XYZ Matrices
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
vec3 xyz;
xyz.x = dot(vec3(0.4124564, 0.3575761, 0.1804375), _rgb);
xyz.y = dot(vec3(0.2126729, 0.7151522, 0.0721750), _rgb);
xyz.z = dot(vec3(0.0193339, 0.1191920, 0.9503041), _rgb);
return xyz;
}
vec3 convertXYZ2RGB(vec3 _xyz)
{
vec3 rgb;
rgb.x = dot(vec3( 3.2404542, -1.5371385, -0.4985314), _xyz);
rgb.y = dot(vec3(-0.9692660, 1.8760108, 0.0415560), _xyz);
rgb.z = dot(vec3( 0.0556434, -0.2040259, 1.0572252), _xyz);
return rgb;
}
vec3 convertXYZ2Yxy(vec3 _xyz)
{
// Reference:
// http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_xyY.html
float inv = 1.0/dot(_xyz, vec3(1.0, 1.0, 1.0) );
return vec3(_xyz.y, _xyz.x*inv, _xyz.y*inv);
}
vec3 convertYxy2XYZ(vec3 _Yxy)
{
// Reference:
// http://www.brucelindbloom.com/index.html?Eqn_xyY_to_XYZ.html
vec3 xyz;
xyz.x = _Yxy.x*_Yxy.y/_Yxy.z;
xyz.y = _Yxy.x;
xyz.z = _Yxy.x*(1.0 - _Yxy.y - _Yxy.z)/_Yxy.z;
return xyz;
}
vec3 convertRGB2Yxy(vec3 _rgb)
{
return convertXYZ2Yxy(convertRGB2XYZ(_rgb) );
}
vec3 convertYxy2RGB(vec3 _Yxy)
{
return convertXYZ2RGB(convertYxy2XYZ(_Yxy) );
}
float reinhard2(float _x, float _whiteSqr)
{
return (_x * (1.0 + _x/_whiteSqr) ) / (1.0 + _x);
}
void main() {
// passthrough
if(options.w == 1) {
outColor = texture(colorSampler, inUV);
return;
}
bool enable_dof = options.w == 2;
vec3 sceneColor = vec3(0);
if(enable_dof) {
sceneColor = texture(farFieldSampler, inUV).rgb;
sceneColor += texture(colorSampler, inUV).rgb;
} else {
if(options.x == 1) // enable AA
sceneColor = SMAANeighborhoodBlendingPS(inUV, inOffset, colorSampler, blendSampler).rgb;
else
sceneColor = texture(colorSampler, inUV).rgb;
}
float sobel = 0.0;
if(textureSize(sobelSampler, 0).x > 1)
@ -128,32 +57,5 @@ void main() {
vec3 sobelColor = vec3(0, 1, 1);
vec3 hdrColor = sceneColor; // fading removed
float avg_lum = texture(averageLuminanceSampler, inUV).r;
vec3 transformed_color = hdrColor;
if(transform_ops.y == 1) {
transformed_color = vec3(1.0) - exp(-transformed_color * options.z);
} else if(transform_ops.y == 2) {
vec3 Yxy = convertRGB2Yxy(transformed_color);
// hard-coded for now
float whitePoint = 4.0;
float lp = Yxy.x / (9.6 * avg_lum + 0.0001);
// Replace this line with other tone mapping functions
// Here we applying the curve to the luminance exclusively
Yxy.x = reinhard2(lp, whitePoint);
transformed_color = convertYxy2RGB(Yxy);
}
if(transform_ops.x == 1) {
transformed_color = from_linear_to_srgb(transformed_color);
}
outColor = vec4(transformed_color + (sobelColor * sobel), 1.0);
outColor = vec4(sceneColor + (sobelColor * sobel), 1.0);
}