Commit 138249c5 authored by Florian Oetke's avatar Florian Oetke
Browse files

basis for solid particle renderer and fixed some bugs in particle update process

parent c15ec121
{
"emitters": [{
"spawn": [
{"particles_per_second": 100, "time": 1},
{"particles_per_second": 0, "time": 200}
{"particles_per_second": 100, "stddev":0, "time": 1},
{"particles_per_second": 0, "stddev":0, "time": 3}
],
"spawn_loop": true,
......
......@@ -39,6 +39,9 @@ frag_shader:model = shader/model.frag.spv
frag_shader:model_emissive = shader/model_emissive.frag.spv
frag_shader:model_alphatest = shader/model_alphatest.frag.spv
vert_shader:particle = shader/particle.vert.spv
frag_shader:particle_solid = shader/particle_solid.frag.spv
vert_shader:ui = shader/ui.vert.spv
frag_shader:ui = shader/ui.frag.spv
......
......@@ -76,6 +76,10 @@ namespace mirrage::renderer {
glm::vec2 tex_coords;
Model_vertex() = default;
Model_vertex(glm::vec3 position, glm::vec3 normal, glm::vec2 tex_coords)
: position(position), normal(normal), tex_coords(tex_coords)
{
}
Model_vertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
: position(px, py, pz), normal(nx, ny, nz), tex_coords(u, v)
{
......@@ -234,6 +238,8 @@ namespace mirrage::renderer {
auto rigged() const noexcept { return _rigged; }
auto bone_count() const noexcept { return _bone_count; }
auto ready() const { return _mesh.ready(); }
private:
graphic::Mesh _mesh;
std::vector<Sub_mesh> _sub_meshes;
......
......@@ -19,6 +19,12 @@
namespace mirrage::renderer {
struct Particle {
glm::vec4 position; // xyz + uintBitsToFloat(last_feedback_buffer_index)
glm::vec4 velocity; // xyz + seed
glm::vec4 ttl; // ttl_left, ttl_initial, <empty>, <empty>
};
class Particle_script {
public:
explicit Particle_script(vk::UniquePipeline pipeline) : _pipeline(std::move(pipeline)) {}
......@@ -29,11 +35,11 @@ namespace mirrage::renderer {
vk::UniquePipeline _pipeline;
};
enum class Particle_blend_mode { solid, volume, transparent };
sf2_enumDef(Particle_blend_mode, solid, volume, transparent);
enum class Particle_blend_mode { solid, transparent };
sf2_enumDef(Particle_blend_mode, solid, transparent);
enum class Particle_geometry { billboard, ribbon, mesh };
sf2_enumDef(Particle_geometry, billboard, ribbon, mesh);
enum class Particle_geometry { billboard, mesh };
sf2_enumDef(Particle_geometry, billboard, mesh);
struct Particle_color {
float hue = 0.f;
......@@ -180,12 +186,18 @@ namespace mirrage::renderer {
auto valid() const noexcept { return _live_rev && *_live_rev == _rev; }
void set(const std::uint64_t* rev,
vk::Buffer,
vk::DescriptorSet,
std::int32_t offset,
std::int32_t count,
std::uint32_t feedback_idx);
void next_uniforms(vk::DescriptorSet s) { _next_uniforms = s; }
auto next_uniforms() const noexcept { return _next_uniforms; }
private:
vk::Buffer _buffer;
vk::DescriptorSet _uniforms;
vk::DescriptorSet _next_uniforms;
const std::uint64_t* _live_rev = nullptr;
std::uint64_t _rev = 0;
std::int32_t _offset = 0;
......@@ -211,7 +223,7 @@ namespace mirrage::renderer {
void absolute(bool b) noexcept { _absolute = b; }
auto absolute() const noexcept { return _absolute; }
void incr_time(float dt) { _time_accumulator += dt; }
void incr_time(float dt);
auto spawn(util::default_rand&) -> std::int32_t;
auto drawable() const noexcept { return _gpu_data && _gpu_data->valid(); }
......@@ -222,6 +234,10 @@ namespace mirrage::renderer {
return drawable() ? util::just(_gpu_data->_feedback_idx) : util::nothing;
}
auto particle_buffer() const noexcept { return drawable() ? _gpu_data->_buffer : vk::Buffer{}; }
auto particle_uniforms() const noexcept
{
return drawable() ? _gpu_data->_uniforms : vk::DescriptorSet{};
}
auto particles_to_spawn() const noexcept { return _particles_to_spawn; }
auto last_timestep() const noexcept { return _last_timestep; }
......
......@@ -35,6 +35,9 @@ namespace mirrage::renderer {
void configure_decal_pipeline(Deferred_renderer&, graphic::Pipeline_description&);
void configure_decal_subpass(Deferred_renderer&, graphic::Subpass_builder&);
void configure_particle_pipeline(Deferred_renderer&, graphic::Pipeline_description&);
void configure_particle_subpass(Deferred_renderer&, graphic::Subpass_builder&);
void update(util::Time dt);
void pre_draw(Frame_data&);
void draw(Frame_data&, graphic::Render_pass&);
......@@ -43,6 +46,8 @@ namespace mirrage::renderer {
ecs::Entity_manager& _ecs;
Deferred_renderer& _renderer;
Model _particle_billboard;
util::iter_range<std::vector<Geometry>::iterator> _geometry_range;
util::iter_range<std::vector<Geometry>::iterator> _rigged_geometry_range;
......
......@@ -81,10 +81,10 @@ namespace mirrage::renderer {
bool _first_frame = true;
void _submit_update(Frame_data&);
void _sort_particles(Frame_data&);
auto _alloc_feedback_buffer(Frame_data&)
-> std::tuple<gsl::span<Emitter_range>, gsl::span<std::uint32_t>>;
void _update_descriptor_set(Per_frame_data&, util::maybe<Per_frame_data&>);
void _dispatch_emits(Frame_data&, vk::CommandBuffer);
void _dispatch_updates(Frame_data&, vk::CommandBuffer, Per_frame_data&);
};
......
......@@ -116,6 +116,7 @@ namespace mirrage::renderer {
struct Particle_draw {
Particle_emitter* emitter;
Particle_system* system;
const Particle_type_config* type_cfg;
gsl::span<Particle_effector_config> effectors;
std::uint32_t culling_mask;
......@@ -124,7 +125,11 @@ namespace mirrage::renderer {
Particle_system& system,
gsl::span<Particle_effector_config> effectors,
std::uint32_t culling_mask)
: emitter(&emitter), system(&system), effectors(effectors), culling_mask(culling_mask)
: emitter(&emitter)
, system(&system)
, type_cfg(&*emitter.cfg().type)
, effectors(effectors)
, culling_mask(culling_mask)
{
}
};
......
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
#include "particle/data_structures.glsl"
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 tex_coords;
layout(location = 3) in vec4 particle_position;
layout(location = 4) in vec4 particle_velocity;
layout(location = 5) in vec4 particle_ttl;
layout(location = 0) out vec3 out_view_pos;
layout(location = 1) out vec3 out_normal;
layout(location = 2) out vec2 out_tex_coords;
layout(location = 3) out vec4 out_particle_velocity;
layout(location = 4) out vec4 out_particle_ttl;
layout(std140, set=2, binding = 0) uniform Particle_config {
Random_vec4 color; // hsva
Random_vec4 color_change;
Random_vec4 size;
Random_vec4 size_change;
Random_float sprite_rotation;
Random_float sprite_rotation_change;
float base_mass;
float density;
float drag;
float timestep;
uint particle_offset;
uint particle_count;
int padding;
int effector_count;
Effector effectors[];
} particle_config;
layout(push_constant) uniform Per_model_uniforms {
mat4 model_to_view;
vec4 light_color;
vec4 options;
} model_uniforms;
out gl_PerVertex {
vec4 gl_Position;
};
void main() {
uint seed = floatBitsToUint(particle_velocity.w);
float age = particle_ttl.y - particle_ttl.x;
vec3 p = position;
p *= rand_vec4(particle_config.size, seed,20).xyz + age*rand_vec4(particle_config.size_change, seed,24).xyz;
// TODO: rotation
vec4 view_pos = model_uniforms.model_to_view * vec4(p + particle_position.xyz, 1.0);
out_view_pos = view_pos.xyz / view_pos.w;
out_normal = (model_uniforms.model_to_view * vec4(normal, 0.0)).xyz;
out_tex_coords = tex_coords;
out_particle_velocity = particle_velocity;
out_particle_ttl = particle_ttl;
gl_Position = global_uniforms.proj_mat * view_pos;
}
#include "../random.glsl"
struct Random_vec4 {
vec4 mean_hsva;
vec4 stddev_hsva;
};
struct Random_float {
float mean;
float stddev;
// padded to vec4 if used as std140
};
vec2 normal_rand(uint seed, uint i, uint j) {
vec2 uv = vec2(floatConstruct(hash(uvec2(seed, i))), floatConstruct(hash(uvec2(seed, j))));
float x = 6.28318530718 * uv[1];
return sqrt(-2.0*log(uv[0])) * vec2(cos(x),sin(x));
}
float rand_float(Random_float range, float rand) {
return range.mean + range.stddev*rand;
}
struct Effector {
vec4 force_dir; // w=padding
vec4 position; // w=padding
float force;
float distance_decay;
float mass_scale; // used as a=mix(F/m, F, mass_scale)
float fixed_dir;
};
struct Particle {
vec4 position; // xyz + uintBitsToFloat(last_feedback_buffer_index)
vec4 velocity; // xyz + seed
vec4 ttl; // ttl_left, ttl_initial, <empty>, <empty>
};
#include "data_structures.glsl"
layout(std140, set=0, binding = 0) uniform Shared_uniforms {
// TODO ?
......@@ -57,11 +21,6 @@ layout(std430, set=0, binding = 2) writeonly buffer Particles_new {
Particle particles[];
} pout;
struct Emitter_particle_range {
int offset;
int count;
};
layout(std430, set=0, binding = 3) buffer Feedback_buffer {
Emitter_particle_range ranges[];
} feedback;
......@@ -69,8 +28,3 @@ layout(std430, set=0, binding = 3) buffer Feedback_buffer {
layout(std430, set=0, binding = 4) buffer Feedback_mapping {
uint new_feedback_index[];
} feedback_mapping;
vec3 quaternion_rotate(vec3 dir, vec4 q) {
return dir + 2.0 * cross(q.xyz, cross(q.xyz, dir) + q.w * dir);
}
#include "../random.glsl"
struct Random_vec4 {
vec4 mean;
vec4 stddev;
};
struct Random_float {
float mean;
float stddev;
// padded to vec4 if used as std140
};
vec2 normal_rand(uint seed, uint i, uint j) {
vec2 uv = vec2(floatConstruct(hash(uvec2(seed, i))), floatConstruct(hash(uvec2(seed, j))));
float x = 6.28318530718 * uv[1];
return sqrt(-2.0*log(uv[0])) * vec2(cos(x),sin(x));
}
float rand_float(Random_float range, float rand) {
return range.mean + range.stddev*rand;
}
vec4 rand_vec4(Random_vec4 range, uint seed, uint range_begin) {
vec4 rand = vec4(normal_rand(seed, range_begin, range_begin+1), normal_rand(seed, range_begin+2, range_begin+4));
return range.mean + range.stddev*rand;
}
struct Effector {
vec4 force_dir; // w=padding
vec4 position; // w=padding
float force;
float distance_decay;
float mass_scale; // used as a=mix(F/m, F, mass_scale)
float fixed_dir;
};
struct Particle {
vec4 position; // xyz + uintBitsToFloat(last_feedback_buffer_index)
vec4 velocity; // xyz + seed
vec4 ttl; // ttl_left, ttl_initial, <empty>, <empty>
// seed: 0=ttl, 1=velocity, 2+3=direction, TODO: position?
// 10-13=color, 14-17=color_change
// 20-23=size, 24-27=size_change
// 30=rotation, 31=rotation_change
};
struct Emitter_particle_range {
int offset;
int count;
};
vec3 quaternion_rotate(vec3 dir, vec4 q) {
return dir + 2.0 * cross(q.xyz, cross(q.xyz, dir) + q.w * dir);
}
......@@ -33,15 +33,23 @@ void main() {
float rand_vel = rand_float(emitter_cfg.velocity, r01[1]);
pout.particles[index].position.w = uintBitsToFloat(emitter_cfg.feedback_buffer_id);
pout.particles[index].velocity.w = seed;
pout.particles[index].velocity.w = uintBitsToFloat(seed);
pout.particles[index].ttl = vec4(rand_ttl, rand_ttl, 0, 0);
vec3 dir = vec3(0,1,0); // TODO: random dir
dir = quaternion_rotate(dir, emitter_cfg.rotation_quat);
pout.particles[index].position.xyz = emitter_cfg.position.xyz + dir;
pout.particles[index].velocity.xyz = dir*rand_vel + emitter_cfg.parent_velocity.xyz;
vec3 pos = emitter_cfg.position.xyz + dir;
vec3 velocity = dir*rand_vel + emitter_cfg.parent_velocity.xyz;
pos += velocity * emitter_cfg.timestep*(float(invId)/float(emitter_cfg.to_spawn));
// TODO: take euler step of timestep*(float(invId)/float(emitter_cfg.to_spawn))
// might distribute particles more evenly => test it first
pout.particles[index].position.xyz = pos;
pout.particles[index].velocity.xyz = velocity;
}
}
......@@ -20,8 +20,8 @@ layout(std140, set=1, binding = 0) uniform Config {
float drag;
float timestep;
uint particle_offset;
uint particle_count;
uint particle_read_offset;
uint particle_read_count;
int padding;
int effector_count;
......@@ -34,8 +34,8 @@ layout(std140, set=1, binding = 0) uniform Config {
void main() {
uint offset = gl_GlobalInvocationID.x;
if(offset < config.particle_count) {
uint index = offset + config.particle_offset;
if(offset < config.particle_read_count) {
uint index = offset + config.particle_read_offset;
vec2 ttl = pin.particles[index].ttl.xy;
ttl[0] -= config.timestep;
......
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
#include "global_uniforms.glsl"
#include "normal_encoding.glsl"
#include "particle/data_structures.glsl"
layout(early_fragment_tests) in;
layout(location = 0) in vec3 view_pos;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 tex_coords;
layout(location = 3) in vec4 out_particle_velocity;
layout(location = 4) in vec4 out_particle_ttl;
layout(location = 0) out vec4 depth_out;
layout(location = 1) out vec4 albedo_mat_id_out;
layout(location = 2) out vec4 mat_data_out;
layout(location = 3) out vec4 color_out;
layout(location = 4) out vec4 color_diffuse_out;
layout(set=1, binding = 0) uniform sampler2D albedo_sampler;
layout(set=1, binding = 1) uniform sampler2D normal_sampler;
layout(set=1, binding = 2) uniform sampler2D brdf_sampler;
layout(set=1, binding = 3) uniform sampler2D emission_sampler;
layout(std140, set=2, binding = 0) uniform Particle_config {
Random_vec4 color; // hsva
Random_vec4 color_change;
Random_vec4 size;
Random_vec4 size_change;
Random_float sprite_rotation;
Random_float sprite_rotation_change;
float base_mass;
float density;
float drag;
float timestep;
uint particle_offset;
uint particle_count;
int padding;
int effector_count;
Effector effectors[];
} particle_config;
layout(push_constant) uniform Per_model_uniforms {
mat4 model;
vec4 light_color;
vec4 options;
} model_uniforms;
const float PI = 3.14159265359;
vec3 decode_tangent_normal(vec2 tn);
vec3 tangent_space_to_world(vec3 N);
void main() {
vec4 albedo = texture(albedo_sampler, tex_coords);
vec3 N = tangent_space_to_world(decode_tangent_normal(texture(normal_sampler, tex_coords).rg));
vec4 brdf = texture(brdf_sampler, tex_coords);
float roughness = brdf.r;
float metallic = brdf.g;
roughness = mix(0.01, 0.99, roughness*roughness);
depth_out = vec4(-view_pos.z / global_uniforms.proj_planes.y, 0,0,1);
albedo_mat_id_out = vec4(albedo.rgb, 0.0);
mat_data_out = vec4(encode_normal(N), roughness, metallic);
float emissive_power = texture(emission_sampler, tex_coords).r;
color_out = vec4(albedo.rgb * emissive_power * albedo.a, 1.0);
color_diffuse_out = color_out;
}
vec3 decode_tangent_normal(vec2 tn) {
if(dot(tn,tn)<0.00001)
return vec3(0,0,1);
vec3 N = vec3(tn*2-1, 0);
N.z = sqrt(1 - dot(N.xy, N.xy));
return N;
}
vec3 tangent_space_to_world(vec3 N) {
vec3 VN = normalize(normal);
// calculate tangent
vec3 p_dx = dFdx(view_pos);
vec3 p_dy = dFdy(view_pos);
vec2 tc_dx = dFdx(tex_coords);
vec2 tc_dy = dFdy(tex_coords);
vec3 p_dy_N = cross(p_dy, VN);
vec3 p_dx_N = cross(VN, p_dx);
vec3 T = p_dy_N * tc_dx.x + p_dx_N * tc_dy.x;
vec3 B = p_dy_N * tc_dx.y + p_dx_N * tc_dy.y;
float inv_max = inversesqrt(max(dot(T,T), dot(B,B)));
mat3 TBN = mat3(T*inv_max, B*inv_max, VN);
return normalize(TBN * N);
}
......@@ -485,7 +485,11 @@ namespace mirrage::renderer {
, _compute_storage_buffer_layout(_device->create_descriptor_set_layout(vk::DescriptorSetLayoutBinding{
0, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute}))
, _compute_uniform_buffer_layout(_device->create_descriptor_set_layout(vk::DescriptorSetLayoutBinding{
0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eCompute}))
0,
vk::DescriptorType::eUniformBuffer,
1,
vk::ShaderStageFlagBits::eCompute | vk::ShaderStageFlagBits::eVertex
| vk::ShaderStageFlagBits::eFragment}))
, _asset_loaders(std::make_unique<Asset_loaders>(_assets,
*_device,
*_model_material_sampler,
......@@ -676,6 +680,10 @@ namespace mirrage::renderer {
"Anisotropic filtering is not supported by device!");
ret_val.features.samplerAnisotropy = true;
MIRRAGE_INVARIANT(supported_features.textureCompressionBC,
"BC texture compression is not supported by device!");
ret_val.features.textureCompressionBC = true;
for(auto& pass : _pass_factories) {
pass->configure_device(gpu, gqueue, ret_val);
}
......
......@@ -15,11 +15,13 @@ namespace mirrage::renderer {
void Particle_emitter_gpu_data::set(const std::uint64_t* rev,
vk::Buffer buffer,
vk::DescriptorSet uniforms,
std::int32_t offset,
std::int32_t count,
std::uint32_t feedback_idx)
{
_buffer = buffer;
_uniforms = uniforms;
_live_rev = rev;
_rev = *rev;
_offset = offset;
......@@ -27,6 +29,12 @@ namespace mirrage::renderer {
_feedback_idx = feedback_idx;
}
void Particle_emitter::incr_time(float dt)
{
_time_accumulator += dt;
_spawn_entry_timer += dt;
}
auto Particle_emitter::spawn(util::default_rand& rand) -> std::int32_t
{
if(_cfg->spawn.empty())
......@@ -39,7 +47,6 @@ namespace mirrage::renderer {
auto& entry = _cfg->spawn[_spawn_idx];
_spawn_entry_timer += _time_accumulator;
if(_spawn_entry_timer + _time_accumulator > entry.time) {
_time_accumulator = util::max(1.f / 60, entry.time - _spawn_entry_timer);
_spawn_idx++;
......
......@@ -2,6 +2,7 @@
#include <mirrage/renderer/animation_comp.hpp>
#include <mirrage/renderer/model_comp.hpp>
#include <mirrage/renderer/particle_system.hpp>
#include <mirrage/renderer/pass/deferred_pass.hpp>
#include <mirrage/ecs/components/transfor