Commit a2f32633 authored by Florian Oetke's avatar Florian Oetke
Browse files

depth of field effect

parent a442e76a
......@@ -6,6 +6,7 @@
#include <mirrage/renderer/pass/bloom_pass.hpp>
#include <mirrage/renderer/pass/debug_draw_pass.hpp>
#include <mirrage/renderer/pass/deferred_pass.hpp>
#include <mirrage/renderer/pass/depth_of_field_pass.hpp>
#include <mirrage/renderer/pass/frustum_culling_pass.hpp>
#include <mirrage/renderer/pass/gen_mipmap_pass.hpp>
#include <mirrage/renderer/pass/gi_pass.hpp>
......@@ -39,6 +40,7 @@ namespace mirrage {
renderer::make_pass_factory<renderer::Ssao_pass_factory>(),
renderer::make_pass_factory<renderer::Gi_pass_factory>(),
renderer::make_pass_factory<renderer::Taa_pass_factory>(),
renderer::make_pass_factory<renderer::Depth_of_field_pass_factory>(),
renderer::make_pass_factory<renderer::Bloom_pass_factory>(),
renderer::make_pass_factory<renderer::Tone_mapping_pass_factory>(),
renderer::make_pass_factory<renderer::Debug_draw_pass_factory>(),
......
......@@ -359,9 +359,14 @@ namespace mirrage {
14,
nk_vec2(100.f, 200)));
nk_layout_row_dynamic(ctx, 20, 1);
_camera.process<renderer::Camera_comp>([&](auto& cam) {
cam.dof_focus(nk_propertyf(ctx, "Focus Plane", 0.1f, cam.dof_focus(), 100.f, 1.f, 0.01f));
cam.dof_range(nk_propertyf(ctx, "Focus Range", 0.1f, cam.dof_range(), 10.f, 0.1f, 0.001f));
cam.dof_power(nk_propertyf(ctx, "DOF Power", 0.01f, cam.dof_power(), 1.f, 0.1f, 0.001f));
});
if(!_meta_system.nims().is_playing()) {
nk_layout_row_dynamic(ctx, 20, 1);
nk_label(ctx, "Directional Light", NK_TEXT_LEFT);
......
......@@ -46,6 +46,7 @@ add_library(mirrage_renderer STATIC
src/pass/deferred_geometry_subpass.cpp
src/pass/deferred_lighting_subpass.cpp
src/pass/deferred_pass.cpp
src/pass/depth_of_field_pass.cpp
src/pass/frustum_culling_pass.cpp
src/pass/gen_mipmap_pass.cpp
src/pass/gi_pass.cpp
......
......@@ -6,6 +6,10 @@ frag_shader:blit = shader/blit.frag.spv
vert_shader:debug_draw = shader/debug_draw.vert.spv
frag_shader:debug_draw = shader/debug_draw.frag.spv
vert_shader:depth_of_field = shader/fullscreen.vert.spv
frag_shader:depth_of_field_apply = shader/depth_of_field_apply.frag.spv
frag_shader:depth_of_field_coc = shader/depth_of_field_coc.frag.spv
frag_shader:depth_of_field_calc = shader/depth_of_field_calc.frag.spv
vert_shader:light_directional = shader/fullscreen.vert.spv
frag_shader:light_directional = shader/light_directional.frag.spv
......
......@@ -25,11 +25,22 @@ namespace mirrage::renderer {
auto far_plane() const noexcept { return _far; }
auto fov() const noexcept { return _fov; }
void dof_focus(float v) noexcept { _dof_focus = v; }
auto dof_focus() const noexcept { return _dof_focus; }
void dof_range(float v) noexcept { _dof_range = v; }
auto dof_range() const noexcept { return _dof_range; }
void dof_power(float v) noexcept { _dof_power = v; }
auto dof_power() const noexcept { return _dof_power; }
private:
util::Angle _fov;
float _near = 0.2f;
float _far = 1000.f;
float _priority = 0.f;
float _dof_focus = 0.f;
float _dof_range = 1.f;
float _dof_power = 0.f;
};
struct Camera_state {
......@@ -54,5 +65,9 @@ namespace mirrage::renderer {
float aspect_ratio;
util::Angle fov_vertical;
util::Angle fov_horizontal;
float dof_focus = 0.f;
float dof_range = 1.f;
float dof_power = 0.f;
};
} // namespace mirrage::renderer
......@@ -51,9 +51,10 @@ namespace mirrage::renderer {
float min_display_luminance = 2.f;
float max_display_luminance = 150.0f;
bool taa = true;
bool ssao = true;
bool bloom = true;
bool taa = true;
bool ssao = true;
bool bloom = true;
bool depth_of_field = true;
float background_intensity = 0.f;
......@@ -77,6 +78,7 @@ namespace mirrage::renderer {
taa,
ssao,
bloom,
depth_of_field,
shadows,
dynamic_lighting,
debug_geometry);
......
#pragma once
#include <mirrage/renderer/deferred_renderer.hpp>
#include <mirrage/graphic/render_pass.hpp>
namespace mirrage::renderer {
class Depth_of_field_pass_factory;
/**
* @brief Depth of field effect based on
* http://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html
*/
class Depth_of_field_pass : public Render_pass {
public:
using Factory = Depth_of_field_pass_factory;
Depth_of_field_pass(Deferred_renderer&,
graphic::Render_target_2D& src,
graphic::Render_target_2D& target);
void update(util::Time dt) override;
void draw(Frame_data&) override;
auto name() const noexcept -> const char* override { return "Depth of Fields"; }
private:
Deferred_renderer& _renderer;
graphic::Render_target_2D& _src;
graphic::Render_target_2D& _target;
vk::UniqueSampler _gbuffer_sampler;
graphic::Image_descriptor_set_layout _descriptor_set_layout;
graphic::DescriptorSet _coc_descriptor_set;
graphic::Framebuffer _coc_framebuffer;
graphic::Render_pass _coc_renderpass;
graphic::DescriptorSet _dof_descriptor_set;
graphic::Framebuffer _dof_framebuffer;
graphic::Render_pass _dof_renderpass;
graphic::DescriptorSet _apply_descriptor_set;
graphic::Framebuffer _apply_framebuffer;
graphic::Render_pass _apply_renderpass;
};
class Depth_of_field_pass_factory : public Render_pass_factory {
public:
auto id() const noexcept -> Render_pass_id override
{
return render_pass_id_of<Depth_of_field_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
void configure_device(vk::PhysicalDevice,
util::maybe<std::uint32_t>,
graphic::Device_create_info&) override;
};
} // namespace mirrage::renderer
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
layout(location = 0) in Vertex_data {
vec2 tex_coords;
} vertex_out;
layout(location = 0) out vec4 out_color;
layout(set=1, binding = 0) uniform sampler2D color_sampler;
layout(set=1, binding = 1) uniform sampler2D dof_sampler;
void main() {
vec4 dof = textureLod(dof_sampler, vertex_out.tex_coords, 0);
vec3 color = textureLod(color_sampler, vertex_out.tex_coords, 0).rgb;
out_color = vec4(mix(color, dof.rgb, smoothstep(0.1, 2.0, dof.a)), 1.0);
}
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
#include "random.glsl"
layout(location = 0) in Vertex_data {
vec2 tex_coords;
} vertex_out;
layout(location = 0) out vec4 out_color;
layout(set=1, binding = 0) uniform sampler2D color_sampler;
layout (constant_id = 0) const float MAX_RADIUS = 15;
layout (constant_id = 1) const float RAD_SCALE = 0.8;
layout(push_constant) uniform Push_constants {
// focus_distance, range, radius
vec4 parameters;
} pcs;
void main() {
vec4 center = textureLod(color_sampler, vertex_out.tex_coords, 0);
float center_coc = center.a;
float center_coc2 = abs(2.0 * center.a);
vec3 color = center.rgb;
float weight_sum = 1.0;
float coc_sum = abs(center_coc);
vec2 texel_size = 1.0 / textureSize(color_sampler, 0);
float radius = RAD_SCALE;
for (float ang = 0; radius<MAX_RADIUS; ang += 2.39996323) {
vec2 tc = vertex_out.tex_coords + vec2(cos(ang), sin(ang)) * texel_size * radius;
vec4 sample_data = textureLod(color_sampler, tc, 0);
float coc = abs(sample_data.a);
if(sample_data.a > center_coc)
sample_data.a = min(coc, center_coc2);
float m = smoothstep(radius-0.5, radius+0.5, coc);
color += mix(color/weight_sum, sample_data.rgb, m);
coc_sum += mix(coc_sum/weight_sum, coc, m);
weight_sum += 1.0;
radius += RAD_SCALE/radius;
}
out_color = vec4(color/weight_sum, coc_sum/weight_sum);
}
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
layout(location = 0) in Vertex_data {
vec2 tex_coords;
} vertex_out;
layout(location = 0) out vec4 out_color;
layout(set=1, binding = 0) uniform sampler2D color_sampler;
layout(set=1, binding = 1) uniform sampler2D depth_sampler;
layout (constant_id = 0) const float MAX_RADIUS = 15;
layout (constant_id = 1) const float RAD_SCALE = 0.5;
layout(push_constant) uniform Push_constants {
// focus_distance, range, radius
vec4 parameters;
mat4 depth_reprojection;
} pcs;
float calc_coc(float dist) {
const float focus_distance = pcs.parameters[0];
const float range = pcs.parameters[1];
const float radius = pcs.parameters[2];
return MAX_RADIUS * radius * clamp((dist - focus_distance) / dist / range, -1.0, 1.0);
}
void main() {
vec4 red = textureGather(color_sampler, vertex_out.tex_coords, 0);
vec4 green = textureGather(color_sampler, vertex_out.tex_coords, 1);
vec4 blue = textureGather(color_sampler, vertex_out.tex_coords, 2);
vec3 color = vec3(
max(red[0], max(red[1], max(red[2], red[3]))),
max(green[0], max(green[1], max(green[2], green[3]))),
max(blue[0], max(blue[1], max(blue[2], blue[3])))
);
vec4 depth_uv = pcs.depth_reprojection * vec4(vertex_out.tex_coords*2-1, 0.5, 1);
depth_uv.xy = (depth_uv.xy/depth_uv.w)*0.5+0.5;
vec4 depth = textureGather(depth_sampler, depth_uv.xy, 0);
vec4 dist = depth * global_uniforms.proj_planes[1];
float coc = calc_coc(dot(vec4(1.0), dist)/4.0);
out_color = vec4(color, coc);
}
......@@ -18,8 +18,12 @@ namespace mirrage::renderer {
{
auto fov = comp._fov / 1_deg;
state.read_virtual(
sf2::vmember("fov", fov), sf2::vmember("near", comp._near), sf2::vmember("far", comp._far));
state.read_virtual(sf2::vmember("fov", fov),
sf2::vmember("near", comp._near),
sf2::vmember("far", comp._far),
sf2::vmember("dof_focus", comp._dof_focus),
sf2::vmember("dof_range", comp._dof_range),
sf2::vmember("dof_power", comp._dof_power));
comp._fov = fov * 1_deg;
}
......@@ -27,8 +31,12 @@ namespace mirrage::renderer {
{
auto fov = comp._fov / 1_deg;
state.write_virtual(
sf2::vmember("fov", fov), sf2::vmember("near", comp._near), sf2::vmember("far", comp._far));
state.write_virtual(sf2::vmember("fov", fov),
sf2::vmember("near", comp._near),
sf2::vmember("far", comp._far),
sf2::vmember("dof_focus", comp._dof_focus),
sf2::vmember("dof_range", comp._dof_range),
sf2::vmember("dof_power", comp._dof_power));
}
auto Camera_comp::calc_projection(glm::vec4 viewport) const -> glm::mat4
......@@ -92,6 +100,9 @@ namespace mirrage::renderer {
, aspect_ratio((viewport.z - viewport.x) / (viewport.w - viewport.y))
, fov_vertical(cam.fov())
, fov_horizontal(2.f * std::atan(std::tan(fov_vertical / 2.f) * aspect_ratio))
, dof_focus(cam.dof_focus())
, dof_range(cam.dof_range())
, dof_power(cam.dof_power())
{
}
......
......@@ -247,6 +247,10 @@ namespace mirrage::renderer {
nk_checkbox_label(ctx, "Tonemapping", &bool_nk_wrapper);
renderer_settings.tonemapping = bool_nk_wrapper == 1;
bool_nk_wrapper = renderer_settings.depth_of_field ? 1 : 0;
nk_checkbox_label(ctx, "Depth of Field", &bool_nk_wrapper);
renderer_settings.depth_of_field = bool_nk_wrapper == 1;
nk_layout_row_dynamic(ctx, 20, 2);
......
......@@ -58,7 +58,8 @@ namespace mirrage::renderer {
{width, height},
1,
depth_format,
vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferSrc
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled
| vk::ImageUsageFlagBits::eInputAttachment | vk::ImageUsageFlagBits::eTransferSrc
| vk::ImageUsageFlagBits::eTransferDst,
vk::ImageAspectFlagBits::eColor)
, depth_buffer(
......
#include <mirrage/renderer/pass/depth_of_field_pass.hpp>
#include <mirrage/graphic/window.hpp>
namespace mirrage::renderer {
using namespace graphic;
namespace {
struct Push_constants {
glm::vec4 arguments{1, 1, 0, 0}; //< focus, range, power
glm::mat4 depth_reprojection = glm::mat4(1);
};
auto build_render_pass(Deferred_renderer& renderer,
graphic::Render_target_2D& target,
int mip_level,
const asset::AID& shader,
vk::DescriptorSetLayout desc_set_layout,
Framebuffer& out_framebuffer)
{
auto builder = renderer.device().create_render_pass_builder();
auto color = builder.add_attachment(
vk::AttachmentDescription{vk::AttachmentDescriptionFlags{},
renderer.gbuffer().color_format,
vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eDontCare,
vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare,
vk::AttachmentStoreOp::eDontCare,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eShaderReadOnlyOptimal});
auto pipeline = graphic::Pipeline_description{};
pipeline.input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
pipeline.multisample = vk::PipelineMultisampleStateCreateInfo{};
pipeline.color_blending = vk::PipelineColorBlendStateCreateInfo{};
pipeline.depth_stencil = vk::PipelineDepthStencilStateCreateInfo{};
pipeline.add_descriptor_set_layout(renderer.global_uniforms_layout());
pipeline.add_descriptor_set_layout(desc_set_layout);
pipeline.add_push_constant(
"pcs"_strid, sizeof(Push_constants), vk::ShaderStageFlagBits::eFragment);
auto& pass = builder.add_subpass(pipeline).color_attachment(color);
pass.stage("coc"_strid)
.shader(shader, graphic::Shader_stage::fragment)
.shader("vert_shader:depth_of_field"_aid, graphic::Shader_stage::vertex);
auto render_pass = builder.build();
out_framebuffer = builder.build_framebuffer(
Framebuffer_attachment_desc{target.view(mip_level), util::Rgba{}},
target.width(mip_level),
target.height(mip_level));
return render_pass;
}
} // namespace
Depth_of_field_pass::Depth_of_field_pass(Deferred_renderer& renderer,
graphic::Render_target_2D& src,
graphic::Render_target_2D& target)
: _renderer(renderer)
, _src(src)
, _target(target)
, _gbuffer_sampler(renderer.device().create_sampler(renderer.gbuffer().mip_levels,
vk::SamplerAddressMode::eClampToEdge,
vk::BorderColor::eIntOpaqueBlack,
vk::Filter::eLinear,
vk::SamplerMipmapMode::eNearest))
, _descriptor_set_layout(renderer.device(), *_gbuffer_sampler, 2)
, _coc_descriptor_set(_descriptor_set_layout.create_set(
renderer.descriptor_pool(), {src.view(0), renderer.gbuffer().depth.view(0)}))
, _coc_renderpass(build_render_pass(renderer,
src,
1,
"frag_shader:depth_of_field_coc"_aid,
*_descriptor_set_layout,
_coc_framebuffer))
, _dof_descriptor_set(_descriptor_set_layout.create_set(renderer.descriptor_pool(), {src.view(1)}))
, _dof_renderpass(build_render_pass(renderer,
target,
2,
"frag_shader:depth_of_field_calc"_aid,
*_descriptor_set_layout,
_dof_framebuffer))
, _apply_descriptor_set(
_descriptor_set_layout.create_set(renderer.descriptor_pool(), {src.view(0), target.view(2)}))
, _apply_renderpass(build_render_pass(renderer,
target,
0,
"frag_shader:depth_of_field_apply"_aid,
*_descriptor_set_layout,
_apply_framebuffer))
{
}
void Depth_of_field_pass::update(util::Time) {}
void Depth_of_field_pass::draw(Frame_data& frame)
{
auto pcs = Push_constants{};
_renderer.active_camera().process([&](auto& camera) {
pcs.arguments.r = camera.dof_focus;
pcs.arguments.g = camera.dof_range;
pcs.arguments.b = camera.dof_power;
pcs.depth_reprojection = camera.projection * glm::inverse(camera.pure_projection);
});
if(pcs.arguments.b <= 0.0001f || !_renderer.settings().depth_of_field) {
graphic::blit_texture(frame.main_command_buffer,
_src,
vk::ImageLayout::eShaderReadOnlyOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
_target,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eShaderReadOnlyOptimal);
return;
}
{
auto _ = _renderer.profiler().push("Calc CoC");
_coc_renderpass.execute(frame.main_command_buffer, _coc_framebuffer, [&] {
auto descriptor_sets =
std::array<vk::DescriptorSet, 2>{frame.global_uniform_set, *_coc_descriptor_set};
_coc_renderpass.bind_descriptor_sets(0, descriptor_sets);
_coc_renderpass.push_constant("pcs"_strid, pcs);
frame.main_command_buffer.draw(3, 1, 0, 0);
});
}
{
auto _ = _renderer.profiler().push("Calc DoF");
_dof_renderpass.execute(frame.main_command_buffer, _dof_framebuffer, [&] {
auto descriptor_sets =
std::array<vk::DescriptorSet, 2>{frame.global_uniform_set, *_dof_descriptor_set};
_dof_renderpass.bind_descriptor_sets(0, descriptor_sets);
_dof_renderpass.push_constant("pcs"_strid, pcs);
frame.main_command_buffer.draw(3, 1, 0, 0);
});
}
auto _ = _renderer.profiler().push("Apply");
_apply_renderpass.execute(frame.main_command_buffer, _apply_framebuffer, [&] {
_apply_renderpass.bind_descriptor_set(1, *_apply_descriptor_set);
_apply_renderpass.push_constant("pcs"_strid, pcs);
frame.main_command_buffer.draw(3, 1, 0, 0);
});
}
auto Depth_of_field_pass_factory::create_pass(Deferred_renderer& renderer,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass>
{
if(!renderer.settings().depth_of_field)
return {};
auto& src = !write_first_pp_buffer ? renderer.gbuffer().colorA : renderer.gbuffer().colorB;
auto& target = write_first_pp_buffer ? renderer.gbuffer().colorA : renderer.gbuffer().colorB;
write_first_pp_buffer = !write_first_pp_buffer;
return std::make_unique<Depth_of_field_pass>(renderer, src, target);
}
auto Depth_of_field_pass_factory::rank_device(vk::PhysicalDevice,
util::maybe<std::uint32_t>,
int current_score) -> int
{
return current_score;
}
void Depth_of_field_pass_factory::configure_device(vk::PhysicalDevice,
util::maybe<std::uint32_t>,
graphic::Device_create_info&)
{
}
} // namespace mirrage::renderer
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment