Antkeeper  0.0.1
bloom-pass.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
22 #include <engine/gl/pipeline.hpp>
28 #include <engine/gl/texture.hpp>
31 #include <algorithm>
32 #include <cmath>
33 
34 namespace render {
35 
37  pass(pipeline, nullptr)
38 {
39  // Load downsample shader template
40  auto downsample_shader_template = resource_manager->load<gl::shader_template>("bloom-downsample.glsl");
41 
42  // Build downsample shader program with Karis averaging
43  m_downsample_karis_shader = downsample_shader_template->build
44  (
45  {
46  {"KARIS_AVERAGE", std::string()}
47  }
48  );
49 
50  // Build downsample shader program without Karis averaging
51  m_downsample_shader = downsample_shader_template->build();
52 
53  // Load upsample shader template
54  auto upsample_shader_template = resource_manager->load<gl::shader_template>("bloom-upsample.glsl");
55 
56  // Build upsample shader program
57  m_upsample_shader = upsample_shader_template->build();
58 
59  // Construct framebuffer texture sampler
60  m_sampler = std::make_shared<gl::sampler>
61  (
67  );
68 
69  // Allocate empty vertex array
70  m_vertex_array = std::make_unique<gl::vertex_array>();
71 }
72 
74 {
75  // Execute command buffer
76  for (const auto& command: m_command_buffer)
77  {
78  command();
79  }
80 }
81 
82 void bloom_pass::set_source_texture(std::shared_ptr<gl::texture_2d> texture)
83 {
84  if (m_source_texture != texture)
85  {
86  m_source_texture = texture;
87 
88  rebuild_mip_chain();
89  correct_filter_radius();
90  rebuild_command_buffer();
91  }
92 }
93 
95 {
96  m_mip_chain_length = length;
97  rebuild_mip_chain();
98  rebuild_command_buffer();
99 }
100 
102 {
103  m_filter_radius = radius;
104  correct_filter_radius();
105 }
106 
107 void bloom_pass::rebuild_mip_chain()
108 {
109  if (m_source_texture && m_mip_chain_length)
110  {
111  // Rebuild target image
112  m_target_image = std::make_shared<gl::image_2d>
113  (
115  m_source_texture->get_image_view()->get_image()->get_dimensions()[0],
116  m_source_texture->get_image_view()->get_image()->get_dimensions()[1],
117  m_mip_chain_length
118  );
119 
120  m_target_textures.resize(m_mip_chain_length);
121  m_target_framebuffers.resize(m_mip_chain_length);
122  for (unsigned int i = 0; i < m_mip_chain_length; ++i)
123  {
124  // Rebuild mip texture
125  m_target_textures[i] = std::make_shared<gl::texture_2d>
126  (
127  std::make_shared<gl::image_view_2d>
128  (
129  m_target_image,
130  m_target_image->get_format(),
131  i,
132  1
133  ),
134  m_sampler
135  );
136 
137  // Rebuild mip framebuffer
138  const gl::framebuffer_attachment attachments[1] =
139  {{
141  m_target_textures[i]->get_image_view(),
142  0
143  }};
144  m_target_framebuffers[i] = std::make_shared<gl::framebuffer>
145  (
146  attachments,
147  m_target_image->get_dimensions()[0] >> i,
148  m_target_image->get_dimensions()[1] >> i
149  );
150  }
151  }
152  else
153  {
154  m_target_image = nullptr;
155  m_target_textures.clear();
156  m_target_framebuffers.clear();
157  }
158 }
159 
160 void bloom_pass::correct_filter_radius()
161 {
162  // Get aspect ratio of target image
163  float aspect_ratio = 1.0f;
164  if (m_target_image)
165  {
166  aspect_ratio = static_cast<float>(m_target_image->get_dimensions()[1]) /
167  static_cast<float>(m_target_image->get_dimensions()[0]);
168  }
169 
170  // Correct filter radius according to target image aspect ratio
171  m_corrected_filter_radius = {m_filter_radius * aspect_ratio, m_filter_radius};
172 }
173 
174 void bloom_pass::rebuild_command_buffer()
175 {
176  m_command_buffer.clear();
177 
178  if (!m_source_texture ||
179  !m_mip_chain_length ||
180  !m_downsample_karis_shader ||
181  !m_downsample_shader ||
182  !m_upsample_shader)
183  {
184  return;
185  }
186 
187  // Setup downsample state
188  m_command_buffer.emplace_back
189  (
190  [&]()
191  {
193  m_pipeline->bind_vertex_array(m_vertex_array.get());
197  }
198  );
199 
200  // Downsample first mip with Karis average
201  if (auto source_texture_var = m_downsample_karis_shader->variable("source_texture"))
202  {
203  m_command_buffer.emplace_back
204  (
205  [&, source_texture_var]()
206  {
207  m_pipeline->bind_shader_program(m_downsample_karis_shader.get());
208  m_pipeline->bind_framebuffer(m_target_framebuffers[0].get());
209 
210  const auto& target_dimensions = m_target_image->get_dimensions();
211  const gl::viewport viewport[1] = {{0.0f, 0.0f, static_cast<float>(target_dimensions[0]), static_cast<float>(target_dimensions[1])}};
212  m_pipeline->set_viewport(0, viewport);
213 
214  source_texture_var->update(*m_source_texture);
215 
216  // Draw fullscreen triangle
217  m_pipeline->draw(3, 1, 0, 0);
218  }
219  );
220  }
221 
222  // Downsample remaining mips
223  if (m_mip_chain_length > 1)
224  {
225  if (auto source_texture_var = m_downsample_shader->variable("source_texture"))
226  {
227  m_command_buffer.emplace_back([&](){m_pipeline->bind_shader_program(m_downsample_shader.get());});
228 
229  for (int i = 1; i < static_cast<int>(m_mip_chain_length); ++i)
230  {
231  m_command_buffer.emplace_back
232  (
233  [&, source_texture_var, i]()
234  {
235  m_pipeline->bind_framebuffer(m_target_framebuffers[i].get());
236 
237  const auto& target_dimensions = m_target_image->get_dimensions();
238  const gl::viewport viewport[1] = {{0.0f, 0.0f, static_cast<float>(target_dimensions[0] >> i), static_cast<float>(target_dimensions[1] >> i)}};
239  m_pipeline->set_viewport(0, viewport);
240 
241  // Use previous downsample texture as downsample source
242  source_texture_var->update(*m_target_textures[i - 1]);
243 
244  // Draw fullscreen triangle
245  m_pipeline->draw(3, 1, 0, 0);
246  }
247  );
248  }
249  }
250  }
251 
252  // Setup upsample state
253  m_command_buffer.emplace_back
254  (
255  [&]()
256  {
257  // Enable additive blending
258  m_pipeline->set_color_blend_enabled(true);
259  m_pipeline->set_color_blend_equation
260  ({
267  });
268 
269  // Bind upsample shader
270  m_pipeline->bind_shader_program(m_upsample_shader.get());
271  }
272  );
273 
274  // Update upsample filter radius
275  if (auto filter_radius_var = m_upsample_shader->variable("filter_radius"))
276  {
277  m_command_buffer.emplace_back([&, filter_radius_var](){filter_radius_var->update(m_corrected_filter_radius);});
278  }
279 
280  // Upsample
281  if (auto source_texture_var = m_upsample_shader->variable("source_texture"))
282  {
283  for (int i = static_cast<int>(m_mip_chain_length) - 1; i > 0; --i)
284  {
285  const int j = i - 1;
286 
287  m_command_buffer.emplace_back
288  (
289  [&, source_texture_var, i, j]()
290  {
291  m_pipeline->bind_framebuffer(m_target_framebuffers[j].get());
292 
293  const auto& target_dimensions = m_target_image->get_dimensions();
294  const gl::viewport viewport[1] = {{0.0f, 0.0f, static_cast<float>(target_dimensions[0] >> j), static_cast<float>(target_dimensions[1] >> j)}};
295  m_pipeline->set_viewport(0, viewport);
296 
297  source_texture_var->update(*m_target_textures[i]);
298 
299  // Draw fullscreen triangle
300  m_pipeline->draw(3, 1, 0, 0);
301  }
302  );
303  }
304  }
305 }
306 
307 } // namespace render
Graphics pipeline interface.
Definition: pipeline.hpp:48
void set_primitive_topology(primitive_topology topology)
Sets the primitive topology to use for drawing.
Definition: pipeline.cpp:356
void bind_shader_program(const gl::shader_program *shader_program)
Sets the vertex input.
Definition: pipeline.cpp:292
void bind_framebuffer(const gl::framebuffer *framebuffer)
Sets the vertex input.
Definition: pipeline.cpp:275
void set_color_blend_enabled(bool enabled)
Controls whether blending is enabled for the corresponding color attachment.
Definition: pipeline.cpp:953
void set_cull_mode(cull_mode mode)
Sets the triangle culling mode.
Definition: pipeline.cpp:503
void set_viewport(std::uint32_t first_viewport, std::span< const viewport > viewports)
Sets one or more viewports.
Definition: pipeline.cpp:381
void draw(std::uint32_t vertex_count, std::uint32_t instance_count, std::uint32_t first_vertex, std::uint32_t first_instance)
Draws primitives.
Definition: pipeline.cpp:1028
void set_depth_test_enabled(bool enabled)
Controls whether depth testing is enabled.
Definition: pipeline.cpp:649
void bind_vertex_array(const vertex_array *array)
Binds a vertex array.
Definition: pipeline.cpp:309
Template used to for generating one or more shader variants from a single source.
void set_mip_chain_length(unsigned int length)
Sets the mip chain length.
Definition: bloom-pass.cpp:94
void set_filter_radius(float radius)
Sets the upsample filter radius.
Definition: bloom-pass.cpp:101
void render(render::context &ctx) override
Renders a bloom texture.
Definition: bloom-pass.cpp:73
void set_source_texture(std::shared_ptr< gl::texture_2d > texture)
Sets the bloom source texture.
Definition: bloom-pass.cpp:82
bloom_pass(gl::pipeline *pipeline, resource_manager *resource_manager)
Constructs a bloom pass.
Definition: bloom-pass.cpp:36
Render pass.
Definition: pass.hpp:34
gl::pipeline * m_pipeline
Definition: pass.hpp:63
Manages the loading, caching, and saving of resources.
std::shared_ptr< T > load(const std::filesystem::path &path)
Loads and caches a resource.
Commands which operate on entity::id components.
Definition: commands.cpp:28
@ clamp_to_edge
Clamp to edge wrap mode.
@ color_attachment_bit
Framebuffer color attachment.
@ back
Back-facing triangles are discarded.
@ linear
Linear filtering.
@ linear
Linear filtering.
@ triangle_list
Separate triangle primitives.
T length(const quaternion< T > &q)
Calculates the length of a quaternion.
Definition: quaternion.hpp:602
constexpr matrix< T, N, M >::column_vector_type & get(matrix< T, N, M > &m) noexcept
Extracts the Ith column from a matrix.
High-level rendering.
Viewport position, dimensions, and depth range.
Definition: viewport.hpp:31
Context of a renderer.
Definition: context.hpp:40