This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
@@ -0,0 +1,61 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rendering/subpasses/forward_subpass.h"
#include "common/utils.h"
#include "common/vk_common.h"
#include "rendering/render_context.h"
#include "scene_graph/components/camera.h"
#include "scene_graph/components/image.h"
#include "scene_graph/components/material.h"
#include "scene_graph/components/mesh.h"
#include "scene_graph/components/pbr_material.h"
#include "scene_graph/components/sub_mesh.h"
#include "scene_graph/components/texture.h"
#include "scene_graph/node.h"
#include "scene_graph/scene.h"
namespace vkb
{
ForwardSubpass::ForwardSubpass(RenderContext &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source, sg::Scene &scene_, sg::Camera &camera) :
GeometrySubpass{render_context, std::move(vertex_source), std::move(fragment_source), scene_, camera}
{
}
void ForwardSubpass::prepare()
{
auto &device = get_render_context().get_device();
for (auto &mesh : meshes)
{
for (auto &sub_mesh : mesh->get_submeshes())
{
auto &variant = sub_mesh->get_mut_shader_variant();
auto &vert_module = device.get_resource_cache().request_shader_module(VK_SHADER_STAGE_VERTEX_BIT, get_vertex_shader(), variant);
auto &frag_module = device.get_resource_cache().request_shader_module(VK_SHADER_STAGE_FRAGMENT_BIT, get_fragment_shader(), variant);
}
}
}
void ForwardSubpass::draw(vkb::core::CommandBufferC &command_buffer)
{
allocate_lights<ForwardLights>(scene.get_components<sg::Light>(), MAX_FORWARD_LIGHT_COUNT);
command_buffer.bind_lighting(get_lighting_state(), 0, 4);
GeometrySubpass::draw(command_buffer);
}
} // namespace vkb
@@ -0,0 +1,70 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "buffer_pool.h"
#include "rendering/subpasses/geometry_subpass.h"
// This value is per type of light that we feed into the shader
#define MAX_FORWARD_LIGHT_COUNT 8
namespace vkb
{
namespace sg
{
class Scene;
class Node;
class Mesh;
class SubMesh;
class Camera;
} // namespace sg
struct alignas(16) ForwardLights
{
vkb::rendering::Light directional_lights[MAX_FORWARD_LIGHT_COUNT];
vkb::rendering::Light point_lights[MAX_FORWARD_LIGHT_COUNT];
vkb::rendering::Light spot_lights[MAX_FORWARD_LIGHT_COUNT];
};
/**
* @brief This subpass is responsible for rendering a Scene
*/
class ForwardSubpass : public GeometrySubpass
{
public:
/**
* @brief Constructs a subpass designed for forward rendering
* @param render_context Render context
* @param vertex_shader Vertex shader source
* @param fragment_shader Fragment shader source
* @param scene Scene to render on this subpass
* @param camera Camera used to look at the scene
*/
ForwardSubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Scene &scene, sg::Camera &camera);
virtual ~ForwardSubpass() = default;
virtual void prepare() override;
/**
* @brief Record draw commands
*/
virtual void draw(vkb::core::CommandBufferC &command_buffer) override;
};
} // namespace vkb
@@ -0,0 +1,322 @@
/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rendering/subpasses/geometry_subpass.h"
#include "common/utils.h"
#include "common/vk_common.h"
#include "rendering/render_context.h"
#include "scene_graph/components/camera.h"
#include "scene_graph/components/image.h"
#include "scene_graph/components/material.h"
#include "scene_graph/components/mesh.h"
#include "scene_graph/components/pbr_material.h"
#include "scene_graph/components/texture.h"
#include "scene_graph/node.h"
#include "scene_graph/scene.h"
namespace vkb
{
GeometrySubpass::GeometrySubpass(RenderContext &render_context, ShaderSource &&vertex_source, ShaderSource &&fragment_source, sg::Scene &scene_, sg::Camera &camera) :
Subpass{render_context, std::move(vertex_source), std::move(fragment_source)},
meshes{scene_.get_components<sg::Mesh>()},
camera{camera},
scene{scene_}
{
}
void GeometrySubpass::prepare()
{
// Build all shader variance upfront
auto &device = get_render_context().get_device();
for (auto &mesh : meshes)
{
for (auto &sub_mesh : mesh->get_submeshes())
{
auto &variant = sub_mesh->get_shader_variant();
auto &vert_module = device.get_resource_cache().request_shader_module(VK_SHADER_STAGE_VERTEX_BIT, get_vertex_shader(), variant);
auto &frag_module = device.get_resource_cache().request_shader_module(VK_SHADER_STAGE_FRAGMENT_BIT, get_fragment_shader(), variant);
}
}
}
void GeometrySubpass::get_sorted_nodes(std::multimap<float, std::pair<sg::Node *, sg::SubMesh *>> &opaque_nodes, std::multimap<float, std::pair<sg::Node *, sg::SubMesh *>> &transparent_nodes)
{
auto camera_transform = camera.get_node()->get_transform().get_world_matrix();
for (auto &mesh : meshes)
{
for (auto &node : mesh->get_nodes())
{
auto node_transform = node->get_transform().get_world_matrix();
const sg::AABB &mesh_bounds = mesh->get_bounds();
sg::AABB world_bounds{mesh_bounds.get_min(), mesh_bounds.get_max()};
world_bounds.transform(node_transform);
float distance = glm::length(glm::vec3(camera_transform[3]) - world_bounds.get_center());
for (auto &sub_mesh : mesh->get_submeshes())
{
if (sub_mesh->get_material()->alpha_mode == sg::AlphaMode::Blend)
{
transparent_nodes.emplace(distance, std::make_pair(node, sub_mesh));
}
else
{
opaque_nodes.emplace(distance, std::make_pair(node, sub_mesh));
}
}
}
}
}
void GeometrySubpass::draw(vkb::core::CommandBufferC &command_buffer)
{
std::multimap<float, std::pair<sg::Node *, sg::SubMesh *>> opaque_nodes;
std::multimap<float, std::pair<sg::Node *, sg::SubMesh *>> transparent_nodes;
get_sorted_nodes(opaque_nodes, transparent_nodes);
// Draw opaque objects in front-to-back order
{
ScopedDebugLabel opaque_debug_label{command_buffer, "Opaque objects"};
for (auto node_it = opaque_nodes.begin(); node_it != opaque_nodes.end(); node_it++)
{
update_uniform(command_buffer, *node_it->second.first, thread_index);
// Invert the front face if the mesh was flipped
const auto &scale = node_it->second.first->get_transform().get_scale();
bool flipped = scale.x * scale.y * scale.z < 0;
VkFrontFace front_face = flipped ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE;
draw_submesh(command_buffer, *node_it->second.second, front_face);
}
}
// Enable alpha blending
ColorBlendAttachmentState color_blend_attachment{};
color_blend_attachment.blend_enable = VK_TRUE;
color_blend_attachment.src_color_blend_factor = VK_BLEND_FACTOR_SRC_ALPHA;
color_blend_attachment.dst_color_blend_factor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment.src_alpha_blend_factor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
ColorBlendState color_blend_state{};
color_blend_state.attachments.resize(get_output_attachments().size());
for (auto &it : color_blend_state.attachments)
{
it = color_blend_attachment;
}
command_buffer.set_color_blend_state(color_blend_state);
command_buffer.set_depth_stencil_state(get_depth_stencil_state());
// Draw transparent objects in back-to-front order
{
ScopedDebugLabel transparent_debug_label{command_buffer, "Transparent objects"};
for (auto node_it = transparent_nodes.rbegin(); node_it != transparent_nodes.rend(); node_it++)
{
update_uniform(command_buffer, *node_it->second.first, thread_index);
draw_submesh(command_buffer, *node_it->second.second);
}
}
}
void GeometrySubpass::update_uniform(vkb::core::CommandBufferC &command_buffer, sg::Node &node, size_t thread_index)
{
GlobalUniform global_uniform;
global_uniform.camera_view_proj = camera.get_pre_rotation() * vkb::rendering::vulkan_style_projection(camera.get_projection()) * camera.get_view();
auto &render_frame = get_render_context().get_active_frame();
auto &transform = node.get_transform();
auto allocation = render_frame.allocate_buffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, sizeof(GlobalUniform), thread_index);
global_uniform.model = transform.get_world_matrix();
global_uniform.camera_position = glm::vec3(glm::inverse(camera.get_view())[3]);
allocation.update(global_uniform);
command_buffer.bind_buffer(allocation.get_buffer(), allocation.get_offset(), allocation.get_size(), 0, 1, 0);
}
void GeometrySubpass::draw_submesh(vkb::core::CommandBufferC &command_buffer, sg::SubMesh &sub_mesh, VkFrontFace front_face)
{
auto &device = command_buffer.get_device();
ScopedDebugLabel submesh_debug_label{command_buffer, sub_mesh.get_name().c_str()};
prepare_pipeline_state(command_buffer, front_face, sub_mesh.get_material()->double_sided);
MultisampleState multisample_state{};
multisample_state.rasterization_samples = get_sample_count();
command_buffer.set_multisample_state(multisample_state);
auto &vert_shader_module = device.get_resource_cache().request_shader_module(VK_SHADER_STAGE_VERTEX_BIT, get_vertex_shader(), sub_mesh.get_shader_variant());
auto &frag_shader_module = device.get_resource_cache().request_shader_module(VK_SHADER_STAGE_FRAGMENT_BIT, get_fragment_shader(), sub_mesh.get_shader_variant());
std::vector<ShaderModule *> shader_modules{&vert_shader_module, &frag_shader_module};
auto &pipeline_layout = prepare_pipeline_layout(command_buffer, shader_modules);
command_buffer.bind_pipeline_layout(pipeline_layout);
if (pipeline_layout.get_push_constant_range_stage(sizeof(PBRMaterialUniform)) != 0)
{
prepare_push_constants(command_buffer, sub_mesh);
}
DescriptorSetLayout &descriptor_set_layout = pipeline_layout.get_descriptor_set_layout(0);
for (auto &texture : sub_mesh.get_material()->textures)
{
if (auto layout_binding = descriptor_set_layout.get_layout_binding(texture.first))
{
command_buffer.bind_image(texture.second->get_image()->get_vk_image_view(),
texture.second->get_sampler()->vk_sampler,
0, layout_binding->binding, 0);
}
}
auto vertex_input_resources = pipeline_layout.get_resources(ShaderResourceType::Input, VK_SHADER_STAGE_VERTEX_BIT);
VertexInputState vertex_input_state;
for (auto &input_resource : vertex_input_resources)
{
sg::VertexAttribute attribute;
if (!sub_mesh.get_attribute(input_resource.name, attribute))
{
continue;
}
VkVertexInputAttributeDescription vertex_attribute{};
vertex_attribute.binding = input_resource.location;
vertex_attribute.format = attribute.format;
vertex_attribute.location = input_resource.location;
vertex_attribute.offset = attribute.offset;
vertex_input_state.attributes.push_back(vertex_attribute);
VkVertexInputBindingDescription vertex_binding{};
vertex_binding.binding = input_resource.location;
vertex_binding.stride = attribute.stride;
vertex_input_state.bindings.push_back(vertex_binding);
}
command_buffer.set_vertex_input_state(vertex_input_state);
// Find submesh vertex buffers matching the shader input attribute names
for (auto &input_resource : vertex_input_resources)
{
const auto &buffer_iter = sub_mesh.vertex_buffers.find(input_resource.name);
if (buffer_iter != sub_mesh.vertex_buffers.end())
{
std::vector<std::reference_wrapper<const vkb::core::BufferC>> buffers;
buffers.emplace_back(std::ref(buffer_iter->second));
// Bind vertex buffers only for the attribute locations defined
command_buffer.bind_vertex_buffers(input_resource.location, std::move(buffers), {0});
}
}
draw_submesh_command(command_buffer, sub_mesh);
}
void GeometrySubpass::prepare_pipeline_state(vkb::core::CommandBufferC &command_buffer,
VkFrontFace front_face,
bool double_sided_material)
{
RasterizationState rasterization_state = base_rasterization_state;
rasterization_state.front_face = front_face;
if (double_sided_material)
{
rasterization_state.cull_mode = VK_CULL_MODE_NONE;
}
command_buffer.set_rasterization_state(rasterization_state);
MultisampleState multisample_state{};
multisample_state.rasterization_samples = get_sample_count();
command_buffer.set_multisample_state(multisample_state);
}
PipelineLayout &GeometrySubpass::prepare_pipeline_layout(vkb::core::CommandBufferC &command_buffer,
const std::vector<ShaderModule *> &shader_modules)
{
// Sets any specified resource modes
for (auto &shader_module : shader_modules)
{
for (auto &resource_mode : get_resource_mode_map())
{
shader_module->set_resource_mode(resource_mode.first, resource_mode.second);
}
}
return command_buffer.get_device().get_resource_cache().request_pipeline_layout(shader_modules);
}
void GeometrySubpass::prepare_push_constants(vkb::core::CommandBufferC &command_buffer, sg::SubMesh &sub_mesh)
{
auto pbr_material = dynamic_cast<const sg::PBRMaterial *>(sub_mesh.get_material());
PBRMaterialUniform pbr_material_uniform{};
pbr_material_uniform.base_color_factor = pbr_material->base_color_factor;
pbr_material_uniform.metallic_factor = pbr_material->metallic_factor;
pbr_material_uniform.roughness_factor = pbr_material->roughness_factor;
auto data = to_bytes(pbr_material_uniform);
if (!data.empty())
{
command_buffer.push_constants(data);
}
}
void GeometrySubpass::draw_submesh_command(vkb::core::CommandBufferC &command_buffer, sg::SubMesh &sub_mesh)
{
// Draw submesh indexed if indices exists
if (sub_mesh.vertex_indices != 0)
{
// Bind index buffer of submesh
command_buffer.bind_index_buffer(*sub_mesh.index_buffer, sub_mesh.index_offset, sub_mesh.index_type);
// Draw submesh using indexed data
command_buffer.draw_indexed(sub_mesh.vertex_indices, 1, 0, 0, 0);
}
else
{
// Draw submesh using vertices only
command_buffer.draw(sub_mesh.vertices_count, 1, 0, 0);
}
}
void GeometrySubpass::set_thread_index(uint32_t index)
{
thread_index = index;
}
} // namespace vkb
@@ -0,0 +1,130 @@
/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/error.h"
#include "common/glm_common.h"
#include "rendering/subpass.h"
namespace vkb
{
namespace core
{
template <vkb::BindingType bindingType>
class CommandBuffer;
using CommandBufferC = CommandBuffer<vkb::BindingType::C>;
} // namespace core
namespace sg
{
class Scene;
class Node;
class Mesh;
class SubMesh;
class Camera;
} // namespace sg
/**
* @brief Global uniform structure for base shader
*/
struct alignas(16) GlobalUniform
{
glm::mat4 model;
glm::mat4 camera_view_proj;
glm::vec3 camera_position;
};
/**
* @brief PBR material uniform for base shader
*/
struct PBRMaterialUniform
{
glm::vec4 base_color_factor;
float metallic_factor;
float roughness_factor;
};
/**
* @brief This subpass is responsible for rendering a Scene
*/
class GeometrySubpass : public vkb::rendering::SubpassC
{
public:
/**
* @brief Constructs a subpass for the geometry pass of Deferred rendering
* @param render_context Render context
* @param vertex_shader Vertex shader source
* @param fragment_shader Fragment shader source
* @param scene Scene to render on this subpass
* @param camera Camera used to look at the scene
*/
GeometrySubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Scene &scene, sg::Camera &camera);
virtual ~GeometrySubpass() = default;
virtual void prepare() override;
/**
* @brief Record draw commands
*/
virtual void draw(vkb::core::CommandBufferC &command_buffer) override;
/**
* @brief Thread index to use for allocating resources
*/
void set_thread_index(uint32_t index);
protected:
virtual void update_uniform(vkb::core::CommandBufferC &command_buffer, sg::Node &node, size_t thread_index);
void draw_submesh(vkb::core::CommandBufferC &command_buffer, sg::SubMesh &sub_mesh, VkFrontFace front_face = VK_FRONT_FACE_COUNTER_CLOCKWISE);
virtual void prepare_pipeline_state(vkb::core::CommandBufferC &command_buffer, VkFrontFace front_face, bool double_sided_material);
virtual PipelineLayout &prepare_pipeline_layout(vkb::core::CommandBufferC &command_buffer,
const std::vector<ShaderModule *> &shader_modules);
virtual void prepare_push_constants(vkb::core::CommandBufferC &command_buffer, sg::SubMesh &sub_mesh);
virtual void draw_submesh_command(vkb::core::CommandBufferC &command_buffer, sg::SubMesh &sub_mesh);
/**
* @brief Sorts objects based on distance from camera and classifies them
* into opaque and transparent in the arrays provided
*/
void get_sorted_nodes(std::multimap<float, std::pair<sg::Node *, sg::SubMesh *>> &opaque_nodes,
std::multimap<float, std::pair<sg::Node *, sg::SubMesh *>> &transparent_nodes);
sg::Camera &camera;
std::vector<sg::Mesh *> meshes;
sg::Scene &scene;
uint32_t thread_index{0};
vkb::RasterizationState base_rasterization_state{};
};
} // namespace vkb
@@ -0,0 +1,53 @@
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "rendering/subpasses/forward_subpass.h"
#include <rendering/hpp_render_context.h>
namespace vkb
{
namespace rendering
{
namespace subpasses
{
/**
* @brief facade class around vkb::ForwardSubpass, providing a vulkan.hpp-based interface
*
* See vkb::ForwardSubpass for documentation
*/
class HPPForwardSubpass : public vkb::ForwardSubpass
{
public:
HPPForwardSubpass(vkb::rendering::HPPRenderContext &render_context,
vkb::ShaderSource &&vertex_shader,
vkb::ShaderSource &&fragment_shader,
vkb::scene_graph::HPPScene &scene,
vkb::sg::Camera &camera) :
vkb::ForwardSubpass(reinterpret_cast<vkb::RenderContext &>(render_context),
std::forward<ShaderSource>(vertex_shader),
std::forward<ShaderSource>(fragment_shader),
reinterpret_cast<vkb::sg::Scene &>(scene),
camera)
{}
};
} // namespace subpasses
} // namespace rendering
} // namespace vkb
@@ -0,0 +1,101 @@
/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lighting_subpass.h"
#include "buffer_pool.h"
#include "rendering/render_context.h"
#include "scene_graph/components/camera.h"
#include "scene_graph/scene.h"
namespace vkb
{
LightingSubpass::LightingSubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Camera &cam, sg::Scene &scene_) :
Subpass{render_context, std::move(vertex_shader), std::move(fragment_shader)},
camera{cam},
scene{scene_}
{
}
void LightingSubpass::prepare()
{
// Build all shaders upfront
auto &resource_cache = get_render_context().get_device().get_resource_cache();
resource_cache.request_shader_module(VK_SHADER_STAGE_VERTEX_BIT, get_vertex_shader(), lighting_variant);
resource_cache.request_shader_module(VK_SHADER_STAGE_FRAGMENT_BIT, get_fragment_shader(), lighting_variant);
}
void LightingSubpass::draw(vkb::core::CommandBufferC &command_buffer)
{
allocate_lights<DeferredLights>(scene.get_components<sg::Light>(), MAX_DEFERRED_LIGHT_COUNT);
command_buffer.bind_lighting(get_lighting_state(), 0, 4);
// Get shaders from cache
auto &resource_cache = command_buffer.get_device().get_resource_cache();
auto &vert_shader_module = resource_cache.request_shader_module(VK_SHADER_STAGE_VERTEX_BIT, get_vertex_shader(), lighting_variant);
auto &frag_shader_module = resource_cache.request_shader_module(VK_SHADER_STAGE_FRAGMENT_BIT, get_fragment_shader(), lighting_variant);
std::vector<ShaderModule *> shader_modules{&vert_shader_module, &frag_shader_module};
// Create pipeline layout and bind it
auto &pipeline_layout = resource_cache.request_pipeline_layout(shader_modules);
command_buffer.bind_pipeline_layout(pipeline_layout);
// we know, that the lighting subpass does not have any vertex stage input -> reset the vertex input state
assert(pipeline_layout.get_resources(ShaderResourceType::Input, VK_SHADER_STAGE_VERTEX_BIT).empty());
command_buffer.set_vertex_input_state({});
// Get image views of the attachments
auto &render_target = get_render_context().get_active_frame().get_render_target();
auto &target_views = render_target.get_views();
assert(3 < target_views.size());
// Bind depth, albedo, and normal as input attachments
auto &depth_view = target_views[1];
command_buffer.bind_input(depth_view, 0, 0, 0);
auto &albedo_view = target_views[2];
command_buffer.bind_input(albedo_view, 0, 1, 0);
auto &normal_view = target_views[3];
command_buffer.bind_input(normal_view, 0, 2, 0);
// Set cull mode to front as full screen triangle is clock-wise
RasterizationState rasterization_state;
rasterization_state.cull_mode = VK_CULL_MODE_FRONT_BIT;
command_buffer.set_rasterization_state(rasterization_state);
// Populate uniform values
LightUniform light_uniform;
// Inverse resolution
light_uniform.inv_resolution.x = 1.0f / render_target.get_extent().width;
light_uniform.inv_resolution.y = 1.0f / render_target.get_extent().height;
// Inverse view projection
light_uniform.inv_view_proj = glm::inverse(vkb::rendering::vulkan_style_projection(camera.get_projection()) * camera.get_view());
// Allocate a buffer using the buffer pool from the active frame to store uniform values and bind it
auto &render_frame = get_render_context().get_active_frame();
auto allocation = render_frame.allocate_buffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, sizeof(LightUniform));
allocation.update(light_uniform);
command_buffer.bind_buffer(allocation.get_buffer(), allocation.get_offset(), allocation.get_size(), 0, 3, 0);
// Draw full screen triangle triangle
command_buffer.draw(3, 1, 0, 0);
}
} // namespace vkb
@@ -0,0 +1,82 @@
/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "buffer_pool.h"
#include "rendering/subpass.h"
#include "common/glm_common.h"
// This value is per type of light that we feed into the shader
#define MAX_DEFERRED_LIGHT_COUNT 48
namespace vkb
{
namespace core
{
template <vkb::BindingType bindingType>
class CommandBuffer;
using CommandBufferC = CommandBuffer<vkb::BindingType::C>;
} // namespace core
namespace sg
{
class Camera;
class Light;
class Scene;
} // namespace sg
/**
* @brief Light uniform structure for lighting shader
* Inverse view projection matrix and inverse resolution vector are used
* in lighting pass to reconstruct position from depth and frag coord
*/
struct alignas(16) LightUniform
{
glm::mat4 inv_view_proj;
glm::vec2 inv_resolution;
};
struct alignas(16) DeferredLights
{
vkb::rendering::Light directional_lights[MAX_DEFERRED_LIGHT_COUNT];
vkb::rendering::Light point_lights[MAX_DEFERRED_LIGHT_COUNT];
vkb::rendering::Light spot_lights[MAX_DEFERRED_LIGHT_COUNT];
};
/**
* @brief Lighting pass of Deferred Rendering
*/
class LightingSubpass : public vkb::rendering::SubpassC
{
public:
LightingSubpass(RenderContext &render_context, ShaderSource &&vertex_shader, ShaderSource &&fragment_shader, sg::Camera &camera, sg::Scene &scene);
virtual void prepare() override;
void draw(vkb::core::CommandBufferC &command_buffer) override;
private:
sg::Camera &camera;
sg::Scene &scene;
ShaderVariant lighting_variant;
};
} // namespace vkb