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

integrated animations and culling with shadowmap rendering (fixes #32)

parent 601584d4
......@@ -7,6 +7,8 @@ vert_shader:light_directional = shader/bin/fullscreen.vert.spv
frag_shader:light_directional = shader/bin/light_directional.frag.spv
vert_shader:shadow_model = shader/bin/shadow_model.vert.spv
vert_shader:shadow_model_animated = shader/bin/shadow_model_animated.vert.spv
vert_shader:shadow_model_animated_dqs = shader/bin/shadow_model_animated_dqs.vert.spv
frag_shader:shadow_model = shader/bin/shadow_model.frag.spv
vert_shader:model = shader/bin/model.vert.spv
......
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 tex_coords;
layout(location = 3) in ivec4 bone_ids;
layout(location = 4) in vec4 bone_weights;
layout(location = 0) out Vertex_data {
vec3 world_pos;
vec2 tex_coords;
} vertex_out;
layout(set=2, binding = 0, std140) uniform Bone_uniforms {
mat3x4 offset[64];
} bones;
layout(push_constant) uniform Per_model_uniforms {
mat4 model;
mat4 light_view_proj;
} model_uniforms;
out gl_PerVertex {
vec4 gl_Position;
};
void main() {
float unused_weight = 1.0 - dot(bone_weights, vec4(1.0));
vec3 p = (vec4(position, 1.0) * bones.offset[bone_ids[0]]) * bone_weights[0]
+ (vec4(position, 1.0) * bones.offset[bone_ids[1]]) * bone_weights[1]
+ (vec4(position, 1.0) * bones.offset[bone_ids[2]]) * bone_weights[2]
+ (vec4(position, 1.0) * bones.offset[bone_ids[3]]) * bone_weights[3]
+ position * unused_weight;
vec4 world_pos = model_uniforms.model * vec4(p, 1.0);
vertex_out.world_pos = world_pos.xyz;
gl_Position = model_uniforms.light_view_proj * world_pos;
vertex_out.tex_coords = tex_coords;
}
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 tex_coords;
layout(location = 3) in ivec4 bone_ids;
layout(location = 4) in vec4 bone_weights;
layout(location = 0) out Vertex_data {
vec3 world_pos;
vec2 tex_coords;
} vertex_out;
layout(set=2, binding = 0, std140) uniform Bone_uniforms {
mat3x4 offset[64];
} bones;
layout(push_constant) uniform Per_model_uniforms {
mat4 model;
mat4 light_view_proj;
} model_uniforms;
out gl_PerVertex {
vec4 gl_Position;
};
vec3 transform_position(vec3 p, mat3x4 dq) {
p *= dq[2].xyz;
return p +
2 * cross(dq[0].xyz, cross(dq[0].xyz, p) + dq[0].w*p) +
2 * (dq[0].w * dq[1].xyz - dq[1].w * dq[0].xyz +
cross(dq[0].xyz, dq[1].xyz));
}
void main() {
float unused_weight = 1.0 - dot(bone_weights, vec4(1.0));
mat3x4 identity_dqs = mat3x4(vec4(1,0,0,0), vec4(0,0,0,0), vec4(1,1,1,1));
mat3x4[] dq = mat3x4[](
bones.offset[bone_ids[0]],
bones.offset[bone_ids[1]],
bones.offset[bone_ids[2]],
bones.offset[bone_ids[3]]
);
// antipodality handling
for(uint i=1; i<=3; i++) {
if (dot(dq[0][0], dq[i][0]) < 0.0) {
dq[i][0] *= -1.0;
dq[i][1] *= -1.0;
}
}
mat3x4 bone = dq[0] * bone_weights[0]
+ dq[1] * bone_weights[1]
+ dq[2] * bone_weights[2]
+ dq[3] * bone_weights[3]
+ identity_dqs * unused_weight;
float dq_len = length(bone[0]);
bone[0] /= dq_len;
bone[1] /= dq_len;
vec3 p = transform_position(position, bone);
vec4 world_pos = model_uniforms.model * vec4(p, 1.0);
vertex_out.world_pos = world_pos.xyz;
gl_Position = model_uniforms.light_view_proj * world_pos;
vertex_out.tex_coords = tex_coords;
}
......@@ -7,7 +7,8 @@
"temperature": 4500,
"shadow_size": 24,
"near_plane": 1.0,
"far_plane": 80
"far_plane": 80,
"update_frequency": 1
},
"Shadowcaster": {
},
......
......@@ -27,6 +27,9 @@ namespace mirrage::renderer {
util::maybe<graphic::Texture_2D&> ambient_occlusion;
vk::UniqueDescriptorSetLayout animation_data_layout;
vk::DescriptorSet animation_data; //< might change each frame!
vk::UniqueDescriptorSetLayout shadowmaps_layout;
graphic::DescriptorSet shadowmaps;
......
......@@ -16,6 +16,7 @@ namespace mirrage::renderer {
using Component::Component;
void temperature(float kelvin);
auto shadowcaster(bool b) noexcept { _shadowcaster = b; }
void source_radius(util::Distance v) noexcept { _source_radius = v; }
void intensity(float v) noexcept { _intensity = v; }
void color(util::Rgb v) noexcept { _color = v; }
......@@ -24,6 +25,7 @@ namespace mirrage::renderer {
void shadow_near_plane(float v) noexcept { _shadow_near_plane = v; }
void shadow_far_plane(float v) noexcept { _shadow_far_plane = v; }
auto shadowcaster() const noexcept { return _shadowcaster; }
auto source_radius() const noexcept { return _source_radius; }
auto intensity() const noexcept { return _intensity; }
auto color() const noexcept { return _color; }
......@@ -31,14 +33,29 @@ namespace mirrage::renderer {
auto calc_shadowmap_view_proj(ecs::components::Transform_comp& transform) const -> glm::mat4;
auto needs_update() { return _shadow_last_update >= _shadow_update_frequency; }
auto on_update()
{
if(needs_update()) {
_shadow_last_update = 1;
return true;
} else {
_shadow_last_update++;
return false;
}
}
private:
util::Distance _source_radius;
float _intensity;
util::Rgb _color;
int _shadowmap_id = -1;
float _shadow_size = 128;
float _shadow_near_plane = 1;
float _shadow_far_plane = 128;
bool _shadowcaster = true;
int _shadowmap_id = -1;
float _shadow_size = 128;
float _shadow_near_plane = 1;
float _shadow_far_plane = 128;
int _shadow_update_frequency = 1;
int _shadow_last_update = 999;
};
extern auto temperature_to_color(float kelvin) -> util::Rgb;
......
......@@ -3,6 +3,10 @@
#include <mirrage/renderer/animation_comp.hpp>
#include <mirrage/renderer/deferred_renderer.hpp>
#include <mirrage/graphic/streamed_buffer.hpp>
#include <tsl/robin_map.h>
#include <gsl/gsl>
namespace mirrage::renderer::detail {
......@@ -51,10 +55,31 @@ namespace mirrage::renderer {
Deferred_renderer& _renderer;
ecs::Entity_manager& _ecs;
// data for animation/pose update
std::unordered_set<detail::Animation_key_cache_key> _unused_animation_keys;
Animation_key_cache _animation_key_cache;
// data for pose upload
struct Animation_upload_queue_entry {
const Model* model;
const Pose_comp* pose;
std::int32_t uniform_offset;
Animation_upload_queue_entry(const Model* model, Pose_comp& pose, std::int32_t uniform_offset)
: model(model), pose(&pose), uniform_offset(uniform_offset)
{
}
};
graphic::Streamed_buffer _animation_uniforms;
std::vector<graphic::DescriptorSet> _animation_desc_sets;
tsl::robin_map<ecs::Entity_handle, std::uint32_t> _animation_uniform_offsets;
std::vector<Animation_upload_queue_entry> _animation_uniform_queue;
void _update_animation(ecs::Entity_handle owner, Animation_comp& anim, Pose_comp&);
void _compute_poses(Frame_data&);
void _upload_poses(Frame_data&);
};
class Animation_pass_factory : public Render_pass_factory {
......
......@@ -32,28 +32,10 @@ namespace mirrage::renderer {
void draw(Frame_data&, graphic::Render_pass&);
private:
struct Animation_upload_queue_entry {
const Model* model;
const Pose_comp* pose;
std::int32_t uniform_offset;
Animation_upload_queue_entry(const Model* model, Pose_comp& pose, std::int32_t uniform_offset)
: model(model), pose(&pose), uniform_offset(uniform_offset)
{
}
};
ecs::Entity_manager& _ecs;
Deferred_renderer& _renderer;
vk::UniqueDescriptorSetLayout _animation_desc_layout;
graphic::Streamed_buffer _animation_uniforms;
std::vector<graphic::DescriptorSet> _animation_desc_sets;
util::iter_range<std::vector<Geometry>::iterator> _geometry_range;
util::iter_range<std::vector<Geometry>::iterator> _rigged_geometry_range;
std::unordered_map<ecs::Entity_handle, std::uint32_t> _animation_uniform_offsets;
std::vector<Animation_upload_queue_entry> _animation_uniform_queue;
};
} // namespace mirrage::renderer
......@@ -3,6 +3,7 @@
#include <mirrage/ecs/entity_handle.hpp>
#include <mirrage/utils/maybe.hpp>
#include <mirrage/utils/str_id.hpp>
#include <mirrage/utils/template_utils.hpp>
#include <mirrage/utils/units.hpp>
#include <glm/gtx/quaternion.hpp>
......@@ -10,6 +11,7 @@
#include <vulkan/vulkan.hpp>
#include <functional>
#include <variant>
namespace mirrage {
......@@ -18,6 +20,9 @@ namespace mirrage {
namespace mirrage::ecs {
class Entity_manager;
namespace components {
class Transform_comp;
}
} // namespace mirrage::ecs
namespace mirrage::graphic {
......@@ -27,6 +32,7 @@ namespace mirrage::graphic {
namespace mirrage::renderer {
class Deferred_renderer;
class Model;
class Directional_light_comp;
struct Camera_state;
struct Sub_mesh;
} // namespace mirrage::renderer
......@@ -36,14 +42,15 @@ namespace mirrage::renderer {
using Command_buffer_source = std::function<vk::CommandBuffer()>;
struct Geometry {
ecs::Entity_handle entity;
glm::vec3 position{0, 0, 0};
glm::quat orientation{1, 0, 0, 0};
glm::vec3 scale{1.f, 1.f, 1.f};
const Model* model;
util::Str_id substance_id;
std::uint32_t sub_mesh;
std::uint32_t culling_mask;
ecs::Entity_handle entity;
glm::vec3 position{0, 0, 0};
glm::quat orientation{1, 0, 0, 0};
glm::vec3 scale{1.f, 1.f, 1.f};
const Model* model;
util::Str_id substance_id;
std::uint32_t sub_mesh;
std::uint32_t culling_mask;
util::maybe<std::uint32_t> animation_uniform_offset;
Geometry() = default;
Geometry(ecs::Entity_handle entity,
......@@ -66,6 +73,26 @@ namespace mirrage::renderer {
}
};
struct Light {
using Transform_comp = mirrage::ecs::components::Transform_comp;
using Light_comp = std::variant<Directional_light_comp*>;
ecs::Entity_handle entity;
Transform_comp* transform;
Light_comp light;
std::uint32_t shadow_culling_mask;
Light() = default;
template <class L>
Light(ecs::Entity_handle entity,
Transform_comp& transform,
L& light,
std::uint32_t shadow_culling_mask)
: entity(entity), transform(&transform), light(&light), shadow_culling_mask(shadow_culling_mask)
{
}
};
class Frame_data {
public:
vk::CommandBuffer main_command_buffer;
......@@ -73,6 +100,9 @@ namespace mirrage::renderer {
std::size_t swapchain_image;
std::vector<Geometry> geometry_queue;
std::vector<Light> light_queue;
auto partition_geometry(std::uint32_t mask) -> util::vector_range<Geometry>;
};
class Render_pass {
......
......@@ -162,6 +162,7 @@ namespace mirrage::renderer {
_frame_data.global_uniform_set = *_global_uniform_descriptor_set;
_frame_data.swapchain_image = _factory->_aquire_next_image();
_frame_data.geometry_queue.clear();
_frame_data.light_queue.clear();
// draw subpasses
for(auto& pass : _passes) {
......
......@@ -20,7 +20,8 @@ namespace mirrage::renderer {
sf2::vmember("temperature", temperature),
sf2::vmember("shadow_size", comp._shadow_size),
sf2::vmember("near_plane", comp._shadow_near_plane),
sf2::vmember("far_plane", comp._shadow_far_plane));
sf2::vmember("far_plane", comp._shadow_far_plane),
sf2::vmember("update_frequency", comp._shadow_update_frequency));
comp._source_radius = src_radius * 1_m;
if(temperature >= 0.f) {
......@@ -35,7 +36,8 @@ namespace mirrage::renderer {
sf2::vmember("color", comp._color),
sf2::vmember("shadow_size", comp._shadow_size),
sf2::vmember("near_plane", comp._shadow_near_plane),
sf2::vmember("far_plane", comp._shadow_far_plane));
sf2::vmember("far_plane", comp._shadow_far_plane),
sf2::vmember("update_frequency", comp._shadow_update_frequency));
}
void Directional_light_comp::temperature(float kelvin) { _color = temperature_to_color(kelvin); }
......
......@@ -12,12 +12,54 @@ using mirrage::ecs::components::Transform_comp;
namespace mirrage::renderer {
Animation_pass::Animation_pass(Deferred_renderer& renderer, ecs::Entity_manager& entities)
: _renderer(renderer), _ecs(entities)
namespace {
constexpr auto initial_animation_capacity = 256 * 4 * 4 * 4;
auto animation_substance(util::Str_id substance_id, Skinning_type st)
{
switch(st) {
case Skinning_type::linear_blend_skinning: return substance_id;
case Skinning_type::dual_quaternion_skinning: return "dq_"_strid + substance_id;
}
return substance_id;
}
} // namespace
Animation_pass::Animation_pass(Deferred_renderer& r, ecs::Entity_manager& entities)
: _renderer(r)
, _ecs(entities)
, _animation_uniforms(r.device(), initial_animation_capacity, vk::BufferUsageFlagBits::eUniformBuffer)
{
_ecs.register_component_type<Pose_comp>();
_ecs.register_component_type<Animation_comp>();
_ecs.register_component_type<Simple_animation_controller_comp>();
MIRRAGE_INVARIANT(!r.gbuffer().animation_data_layout,
"More than one animation implementation active!");
r.gbuffer().animation_data_layout =
r.device().create_descriptor_set_layout(vk::DescriptorSetLayoutBinding{
0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex});
auto buffers = _animation_uniforms.buffer_count();
_animation_desc_sets = util::build_vector(buffers, [&](auto) {
return r.create_descriptor_set(*r.gbuffer().animation_data_layout, 1);
});
auto anim_desc_buffer_writes = util::build_vector(buffers, [&](auto i) {
return vk::DescriptorBufferInfo{_animation_uniforms.buffer(i), 0, initial_animation_capacity};
});
auto anim_desc_writes = util::build_vector(buffers, [&](auto i) {
return vk::WriteDescriptorSet{*_animation_desc_sets.at(i),
0,
0,
1,
vk::DescriptorType::eUniformBufferDynamic,
nullptr,
&anim_desc_buffer_writes[i]};
});
r.device().vk_device()->updateDescriptorSets(
std::uint32_t(anim_desc_writes.size()), anim_desc_writes.data(), 0, nullptr);
}
void Animation_pass::update(util::Time time)
......@@ -81,6 +123,14 @@ namespace mirrage::renderer {
}
void Animation_pass::draw(Frame_data& frame)
{
_compute_poses(frame);
// TODO: add Animation_listeners to intercept/replace computed poses before drawing
_upload_poses(frame);
}
void Animation_pass::_compute_poses(Frame_data& frame)
{
// mark all cached animations as unused
_unused_animation_keys.clear();
......@@ -111,6 +161,90 @@ namespace mirrage::renderer {
_animation_key_cache.erase(key);
}
}
void Animation_pass::_upload_poses(Frame_data& frame)
{
// upload skeleton pose
_animation_uniform_offsets.clear();
_animation_uniform_queue.clear();
auto required_size = std::int32_t(0);
auto alignment = std::int32_t(
_renderer.device().physical_device_properties().limits.minUniformBufferOffsetAlignment);
auto aligned_byte_size = [&](auto bone_count) {
auto size = bone_count * std::int32_t(sizeof(Final_bone_transform));
return size < alignment ? alignment : size + (alignment - size % alignment);
};
for(auto& geo : frame.geometry_queue) {
if(!geo.model->rigged())
break;
auto offset = gsl::narrow<std::uint32_t>(required_size);
auto entity = _ecs.get(geo.entity).get_or_throw("Invalid entity in render queue");
auto upload_required = entity.get<Shared_pose_comp>().process(true, [&](auto& sp) {
auto pose_offset = util::find_maybe(_animation_uniform_offsets, sp.pose_owner);
offset = pose_offset.get_or(offset);
_animation_uniform_offsets.emplace(geo.entity, pose_offset.get_or(offset));
entity = _ecs.get(sp.pose_owner).get_or_throw("Invalid entity in render queue");
entity.get<Pose_comp>().process([&](auto& pose) {
geo.substance_id = animation_substance(geo.substance_id, pose.skeleton().skinning_type());
});
if(pose_offset.is_some())
return false;
return true;
});
if(upload_required) {
entity.get<Pose_comp>().process([&](auto& pose) {
geo.substance_id = animation_substance(geo.substance_id, pose.skeleton().skinning_type());
auto [ex, success] = _animation_uniform_offsets.try_emplace(entity.handle(), offset);
offset = ex->second;
if(success) {
_animation_uniform_queue.emplace_back(geo.model, pose, required_size);
required_size += aligned_byte_size(geo.model->bone_count());
}
});
}
geo.animation_uniform_offset = offset;
}
if(_animation_uniforms.resize(required_size)) {
// recreate DescriptorSet if the buffer has been recreated
auto anim_desc_buffer_write = vk::DescriptorBufferInfo{
_animation_uniforms.write_buffer(), 0, vk::DeviceSize(required_size)};
auto anim_desc_writes = vk::WriteDescriptorSet{
*_animation_desc_sets.at(std::size_t(_animation_uniforms.write_buffer_index())),
0,
0,
1,
vk::DescriptorType::eUniformBufferDynamic,
nullptr,
&anim_desc_buffer_write};
_renderer.device().vk_device()->updateDescriptorSets(1, &anim_desc_writes, 0, nullptr);
}
for(auto& upload : _animation_uniform_queue) {
_animation_uniforms.update_objects<Final_bone_transform>(upload.uniform_offset, [&](auto out) {
auto geo_matrices = out.subspan(0, upload.model->bone_count());
upload.pose->skeleton().to_final_transforms(upload.pose->bone_transforms(), geo_matrices);
});
}
_animation_uniforms.flush(frame.main_command_buffer,
vk::PipelineStageFlagBits::eVertexShader,
vk::AccessFlagBits::eUniformRead | vk::AccessFlagBits::eShaderRead);
_renderer.gbuffer().animation_data =
*_animation_desc_sets.at(std::size_t(_animation_uniforms.read_buffer_index()));
}
void Animation_pass::_update_animation(ecs::Entity_handle owner,
Animation_comp& anim_comp,
......
......@@ -14,41 +14,12 @@ using mirrage::ecs::components::Transform_comp;
namespace mirrage::renderer {
namespace {
constexpr auto initial_animation_capacity = 256 * 4 * 4 * 4;
}
Deferred_geometry_subpass::Deferred_geometry_subpass(Deferred_renderer& r, ecs::Entity_manager& entities)
: _ecs(entities)
, _renderer(r)
, _animation_desc_layout(r.device().create_descriptor_set_layout(vk::DescriptorSetLayoutBinding{
0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex}))
, _animation_uniforms(r.device(), initial_animation_capacity, vk::BufferUsageFlagBits::eUniformBuffer)