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

keyframe animations for particles (size and color)

parent c66aca6b
{
"emitters": [{
"spawn": [
{"particles_per_second": 100, "stddev":0, "time": 1},
{"particles_per_second": 0, "stddev":0, "time": 3}
{"particles_per_second": 2000, "stddev":0, "time": 1},
{"particles_per_second": 0, "stddev":0, "time": 4}
],
"spawn_loop": true,
"ttl": {"mean": 2, "stddev": 0.5},
"ttl": {"mean": 4, "stddev": 0.5},
"velocity": {"mean": 2, "stddev": 0.5},
"velocity": {"mean": 1, "stddev": 0.5},
"emit_script_id": "comp_shader:particle_spawn_sphere",
......
{
"keyframes": [
{
"size": {"mean": {"x": 0.2, "y":0.2, "z":0.2}},
"time": 0,
"color": {"mean":{"hue":0}, "stddev":{"hue":0.5}},
"size": {"mean": {"x": 0.08, "y":0.08, "z":0.08}, "stddev":{"x": 0.04, "y":0.04, "z":0.04}},
"drag": 0.1
},
{
"time": 3,
"color": {"mean":{"hue":0}, "stddev":{"hue":0}},
"size": {"mean": {"x": 0.01, "y":0.01, "z":0.01}},
"drag": 1.0
}
],
"symmetric_scaling": true,
"blend": "solid",
"geometry": "mesh",
......
......@@ -97,23 +97,26 @@ namespace mirrage::renderer {
float drag = 0.f;
};
sf2_structDef(Particle_keyframe, color, rotation, size, time, base_mass, density, drag);
static_assert(sizeof(Particle_keyframe) == sizeof(float) * (4 * 3 * 2 + 4),
"Particle_keyframe contains padding");
/// describes how living particles are updated and drawn
struct Particle_type_config {
util::small_vector<Particle_keyframe, 3> keyframes;
bool color_normal_distribution_h = true;
bool color_normal_distribution_s = true;
bool color_normal_distribution_v = true;
bool color_normal_distribution_a = true;
bool rotation_normal_distribution_x = true;
bool rotation_normal_distribution_y = true;
bool rotation_normal_distribution_z = true;
bool size_normal_distribution_x = true;
bool size_normal_distribution_y = true;
bool size_normal_distribution_z = true;
bool color_normal_distribution_h = false;
bool color_normal_distribution_s = false;
bool color_normal_distribution_v = false;
bool color_normal_distribution_a = false;
bool rotation_normal_distribution_x = false;
bool rotation_normal_distribution_y = false;
bool rotation_normal_distribution_z = false;
bool size_normal_distribution_x = false;
bool size_normal_distribution_y = false;
bool size_normal_distribution_z = false;
bool rotate_with_velocity = false;
bool symmetric_scaling = false; //< also use x size for y and z
Particle_blend_mode blend = Particle_blend_mode::transparent;
Particle_geometry geometry = Particle_geometry::billboard;
......@@ -144,6 +147,7 @@ namespace mirrage::renderer {
size_normal_distribution_y,
size_normal_distribution_z,
rotate_with_velocity,
symmetric_scaling,
blend,
geometry,
update_range,
......@@ -167,6 +171,14 @@ namespace mirrage::renderer {
Random_value<float> velocity = {1.f};
glm::vec4 size = {0.f, 1.f, 0.f, 0.f}; // min_radius, max_radius, <ignored>, <ignored>
bool independent_direction = false; // initial velocity direction is independent of spawn position
Random_value<glm::vec4> direction = {
glm::vec4{0},
glm::vec4(glm::pi<float>())}; // elevation, azimuth, velocity-elevation, velocity-azimuth
bool direction_normal_distribution = false;
float parent_velocity = 0.f;
glm::vec3 offset{0, 0, 0};
......@@ -186,6 +198,10 @@ namespace mirrage::renderer {
spawn_loop,
ttl,
velocity,
size,
independent_direction,
direction,
direction_normal_distribution,
parent_velocity,
offset,
rotation,
......
......@@ -4,6 +4,7 @@
#include "global_uniforms.glsl"
#include "particle/data_structures.glsl"
#include "color_conversion.glsl"
layout(location = 0) in vec3 position;
......@@ -19,10 +20,11 @@ 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(location = 5) out vec4 out_particle_color;
layout(std140, set=2, binding = 0) uniform Particle_type_config_dummy {
Particle_type_config particle_config;
};
layout(std140, set=2, binding = 0) readonly buffer Particle_type_config {
PARTICLE_TYPE_CONFIG
} particle_config;
layout(push_constant) uniform Per_model_uniforms {
mat4 model_to_view;
......@@ -34,12 +36,71 @@ out gl_PerVertex {
vec4 gl_Position;
};
vec3 calc_size(uint keyframe_a, uint keyframe_b, float t, vec3 rand) {
return mix(rand_xyz(particle_config.keyframes[keyframe_a].size, rand),
rand_xyz(particle_config.keyframes[keyframe_b].size, rand),
t);
}
vec4 calc_color(uint keyframe_a, uint keyframe_b, float t, vec4 rand) {
return mix(rand_vec4(particle_config.keyframes[keyframe_a].color, rand),
rand_vec4(particle_config.keyframes[keyframe_b].color, rand),
t);
}
void calc_random(uint seed, out vec3 rotation, out vec3 size, out vec4 color) {
vec2 uniform_rotation = uniform_rand(seed, 20, 21);
vec2 uniform_shared = uniform_rand(seed, 22, 23);
vec2 uniform_size = uniform_rand(seed, 24, 25);
vec4 uniform_color = vec4(uniform_rand(seed, 10, 11), uniform_rand(seed, 12, 13));
vec2 normal_rotation = uniform_to_normal_rand(uniform_rotation);
vec2 normal_shared = uniform_to_normal_rand(uniform_shared);
vec2 normal_size = uniform_to_normal_rand(uniform_size);
vec4 normal_color = vec4(uniform_to_normal_rand(uniform_color.xy), uniform_to_normal_rand(uniform_color.zw));
color.x = (particle_config.normal_distribution_flags & 1)!=0 ? normal_color.x
: uniform_color.x*2-1;
color.y = (particle_config.normal_distribution_flags & 2)!=0 ? normal_color.y
: uniform_color.y*2-1;
color.z = (particle_config.normal_distribution_flags & 4)!=0 ? normal_color.z
: uniform_color.z*2-1;
color.w = (particle_config.normal_distribution_flags & 8)!=0 ? normal_color.w
: uniform_color.w*2-1;
size.x = (particle_config.normal_distribution_flags & 16)!=0 ? normal_shared.y
: uniform_shared.y*2-1;
size.y = (particle_config.normal_distribution_flags & 32)!=0 ? normal_rotation.x
: uniform_rotation.x*2-1;
size.z = (particle_config.normal_distribution_flags & 64)!=0 ? normal_size.x
: uniform_size.x*2-1;
rotation.x = (particle_config.normal_distribution_flags & 128)!=0 ? normal_rotation.x
: uniform_rotation.x*2-1;
rotation.y = (particle_config.normal_distribution_flags & 256)!=0 ? normal_rotation.y
: uniform_rotation.y*2-1;
rotation.z = (particle_config.normal_distribution_flags & 512)!=0 ? normal_shared.x
: uniform_shared.x*2-1;
}
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;
vec3 rand_rotation;
vec3 rand_size;
vec4 rand_color;
calc_random(seed, rand_rotation, rand_size, rand_color);
uint keyframe_a = floatBitsToUint(particle_ttl[2]);
uint keyframe_b = min(keyframe_a+1, particle_config.keyframe_count-1);
float keyframe_t = particle_ttl[3];
vec3 size = max(vec3(0,0,0), calc_size(keyframe_a, keyframe_b, keyframe_t, rand_size));
if(particle_config.symmetric_scaling!=0)
size.y = size.z = size.x;
vec3 p = position * size;
// TODO: rotation
vec4 view_pos = model_uniforms.model_to_view * vec4(p + particle_position.xyz, 1.0);
......@@ -50,5 +111,8 @@ void main() {
out_particle_velocity = particle_velocity;
out_particle_ttl = particle_ttl;
vec4 color = calc_color(keyframe_a, keyframe_b, keyframe_t, rand_color);;
out_particle_color = vec4(hsv2rgb(color.xyz), color.a);
gl_Position = global_uniforms.proj_mat * view_pos;
}
......@@ -10,18 +10,38 @@ struct Random_float {
// padded to vec4 if used as std140
};
vec2 uniform_to_normal_rand(vec2 rand) {
float x = 6.28318530718 * rand[1];
return sqrt(-2.0*log(rand[0])) * vec2(cos(x),sin(x));
}
// 0-1
float uniform_rand(uint seed, uint i) {
return floatConstruct(hash(uvec2(seed, i)));
}
vec2 uniform_rand(uint seed, uint i, uint j) {
return vec2(uniform_rand(seed, i), uniform_rand(seed, j));
}
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));
return uniform_to_normal_rand(uniform_rand(seed, i, j));
}
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));
vec4 rand_vec4(Random_vec4 range, vec4 rand) {
// 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;
}
vec3 rand_xyz(Random_vec4 range, vec3 rand) {
return range.mean.xyz + range.stddev.xyz*rand;
}
vec3 rand_dir(vec2 mean_angles, vec2 stddev_angles, vec2 rand) {
vec2 angles = mean_angles + stddev_angles * rand;
return vec3(sin(angles.x)*cos(angles.y),
sin(angles.x)*sin(angles.y),
cos(angles.x));
}
struct Effector {
......@@ -38,10 +58,9 @@ struct Particle {
vec4 position; // xyz + uintBitsToFloat(last_feedback_buffer_index)
vec4 velocity; // xyz + seed
vec4 ttl; // ttl_left, ttl_initial, keyframe, keyframe_interpolation_factor
// 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
// seed: 0=ttl, 1=velocity, 2+3=direction, 4+5=vel_direction, 6...=position
// 10-13=color
// 20-22=rotation, 23-25=size
};
struct Emitter_particle_range {
......@@ -60,26 +79,26 @@ struct Particle_keyframe {
float drag;
};
struct Particle_type_config {
uint normal_distribution_flags;
// 1 << 0 : color[0] normal/uniform
// 1 << 1 : color[1] normal/uniform
// 1 << 2 : color[2] normal/uniform
// 1 << 3 : color[3] normal/uniform
// 1 << 4 : rotation[0] normal/uniform
// 1 << 5 : rotation[1] normal/uniform
// 1 << 6 : rotation[2] normal/uniform
// 1 << 7 : size[0] normal/uniform
// 1 << 8 : size[1] normal/uniform
// 1 << 9 : size[2] normal/uniform
uint rotate_with_velocity; // bool
uint keyframe_count;
uint padding;
Particle_keyframe keyframes;
};
/*
normal_distribution_flags:
1 << 0 = 1 : color[0] normal/uniform
1 << 1 = 2 : color[1] normal/uniform
1 << 2 = 4 : color[2] normal/uniform
1 << 3 = 8 : color[3] normal/uniform
1 << 4 = 16 : rotation[0] normal/uniform
1 << 5 = 32 : rotation[1] normal/uniform
1 << 6 = 64 : rotation[2] normal/uniform
1 << 7 = 128 : size[0] normal/uniform
1 << 8 = 256 : size[1] normal/uniform
1 << 9 = 512 : size[2] normal/uniform
*/
#define PARTICLE_TYPE_CONFIG \
uint normal_distribution_flags;\
uint rotate_with_velocity; \
uint symmetric_scaling; \
uint keyframe_count; \
Particle_keyframe[] keyframes;
vec3 quaternion_rotate(vec3 dir, vec4 q) {
return dir + 2.0 * cross(q.xyz, cross(q.xyz, dir) + q.w * dir);
......
......@@ -9,10 +9,15 @@ layout(push_constant) uniform Push_constants {
vec4 parent_velocity;
vec4 position;
vec4 rotation_quat;
Random_vec4 direction;
vec2 size;
Random_float ttl;
Random_float velocity;
uint direction_flags; // 0x1 : independent; 0x2 : normal distribution
uint offset;
uint to_spawn;
uint base_seed;
......@@ -36,12 +41,27 @@ void main() {
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);
bool dir_normal_distr = (emitter_cfg.direction_flags & 2) != 0;
vec2 r23 = dir_normal_distr ? normal_rand(seed, 2, 3) : uniform_rand(seed, 2,3)*2-1;
vec3 dir = quaternion_rotate(rand_dir(emitter_cfg.direction.mean.xy,
emitter_cfg.direction.stddev.xy,
r23),
emitter_cfg.rotation_quat);
vec3 vel_dir = dir;
if((emitter_cfg.direction_flags & 1) != 0) {
vec2 r45 = dir_normal_distr ? normal_rand(seed, 4, 5) : uniform_rand(seed, 4, 5)*2-1;
vel_dir = quaternion_rotate(rand_dir(emitter_cfg.direction.mean.zw,
emitter_cfg.direction.stddev.zw,
r45),
emitter_cfg.rotation_quat);
}
float r = mix(emitter_cfg.size[0], emitter_cfg.size[1], uniform_rand(seed, 6));
vec3 pos = emitter_cfg.position.xyz + dir;
vec3 velocity = dir*rand_vel + emitter_cfg.parent_velocity.xyz;
vec3 velocity = vel_dir*rand_vel + emitter_cfg.parent_velocity.xyz;
// single euler step to distribute particles more evenly with bigger delta-times
pos += velocity * emitter_cfg.timestep*(float(invId)/float(emitter_cfg.to_spawn));
pout.particles[index].position.xyz = pos;
......
......@@ -5,9 +5,9 @@
layout (local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
layout(std140, set=1, binding = 0) uniform Particle_type_config_dummy {
Particle_type_config config;
};
layout(std140, set=1, binding = 0) readonly buffer Particle_type_config {
PARTICLE_TYPE_CONFIG
} particle_config;
layout(push_constant) uniform Push_constants {
......@@ -40,6 +40,20 @@ void main() {
uint out_offset = atomicAdd(feedback.ranges[feedback_index].count, 1);
uint out_index = out_offset + feedback.ranges[feedback_index].offset;
// update keyframe
float age = ttl[1] - ttl[0];
uint keyframe = 0;
for(; keyframe+1<particle_config.keyframe_count-1; keyframe++) {
if(particle_config.keyframes[keyframe].time >= age) {
keyframe--;
break;
}
}
float time_a = particle_config.keyframes[keyframe].time;
float time_b = particle_config.keyframes[min(keyframe+1, particle_config.keyframe_count-1)].time;
float keyframe_t = clamp(0, 1, time_b <= time_a ? 0.0 : (age - time_a) / (time_b - time_a));
// update position / velocity
vec3 velocity = pin.particles[index].velocity.xyz;
// TODO: velocity *= (1.0 - config.drag*dt);
......@@ -50,7 +64,7 @@ void main() {
pout.particles[out_index].position.w = uintBitsToFloat(feedback_index);
pout.particles[out_index].velocity.w = pin.particles[index].velocity.w;
pout.particles[out_index].ttl = vec4(ttl.xy, 0, 0);
pout.particles[out_index].ttl = vec4(ttl.x, ttl.y, uintBitsToFloat(keyframe), keyframe_t);
}
}
}
......@@ -13,6 +13,7 @@ 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 = 5) in vec4 out_particle_color;
layout(location = 0) out vec4 depth_out;
......@@ -26,9 +27,9 @@ 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_type_config_dummy {
Particle_type_config particle_config;
};
layout(std140, set=2, binding = 0) readonly buffer Particle_type_config {
PARTICLE_TYPE_CONFIG
} particle_config;
layout(push_constant) uniform Per_model_uniforms {
mat4 model;
......@@ -44,6 +45,7 @@ vec3 tangent_space_to_world(vec3 N);
void main() {
vec4 albedo = texture(albedo_sampler, tex_coords);
albedo *= out_particle_color;
vec3 N = tangent_space_to_world(decode_tangent_normal(texture(normal_sampler, tex_coords).rg));
......
......@@ -483,7 +483,11 @@ namespace mirrage::renderer {
| vk::ShaderStageFlagBits::eCompute}))
, _compute_storage_buffer_layout(_device->create_descriptor_set_layout(vk::DescriptorSetLayoutBinding{
0, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute}))
0,
vk::DescriptorType::eStorageBuffer,
1,
vk::ShaderStageFlagBits::eCompute | vk::ShaderStageFlagBits::eVertex
| vk::ShaderStageFlagBits::eFragment}))
, _compute_uniform_buffer_layout(_device->create_descriptor_set_layout(vk::DescriptorSetLayoutBinding{
0,
vk::DescriptorType::eUniformBuffer,
......
......@@ -155,12 +155,11 @@ namespace mirrage::renderer {
}
auto create_particle_script_pipeline_layout(graphic::Device& device,
vk::DescriptorSetLayout shared_desc_set,
vk::DescriptorSetLayout,
vk::DescriptorSetLayout uniform_buffer)
-> vk::UniquePipelineLayout
vk::DescriptorSetLayout storage_buffer,
vk::DescriptorSetLayout) -> vk::UniquePipelineLayout
{
// shared_data, emitter/particle_type data
auto desc_sets = std::array<vk::DescriptorSetLayout, 2>{shared_desc_set, uniform_buffer};
auto desc_sets = std::array<vk::DescriptorSetLayout, 2>{shared_desc_set, storage_buffer};
auto push_constants = vk::PushConstantRange{vk::ShaderStageFlagBits::eCompute, 0, 4 * 4 * 4 * 2};
return device.vk_device()->createPipelineLayoutUnique(
......
......@@ -201,7 +201,7 @@ namespace mirrage::renderer {
{
p.rasterization.cullMode = vk::CullModeFlagBits::eNone;
p.add_descriptor_set_layout(renderer.model_descriptor_set_layout());
p.add_descriptor_set_layout(renderer.compute_uniform_buffer_layout()); //< particle type data
p.add_descriptor_set_layout(renderer.compute_storage_buffer_layout()); //< particle type data
p.vertex<Model_vertex>(
0, false, 0, &Model_vertex::position, 1, &Model_vertex::normal, 2, &Model_vertex::tex_coords);
......
......@@ -59,8 +59,8 @@ namespace mirrage::renderer {
struct Type_uniforms {
std::uint32_t normal_distribution_flags;
std::uint32_t rotate_with_velocity;
std::uint32_t symmetric_scaling;
std::uint32_t keyframe_count;
std::uint32_t padding;
// + keyframes
};
......@@ -142,19 +142,19 @@ namespace mirrage::renderer {
auto create_info = vk::BufferCreateInfo{
vk::BufferCreateFlags{},
size_bytes,
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eUniformBuffer,
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eStorageBuffer,
vk::SharingMode::eConcurrent,
gsl::narrow<std::uint32_t>(allowed_queues.size()),
allowed_queues.data()};
buffer = renderer.device().create_buffer(create_info, true, graphic::Memory_lifetime::normal);
if(!desc_set)
desc_set = renderer.create_descriptor_set(renderer.compute_uniform_buffer_layout(), 1);
desc_set = renderer.create_descriptor_set(renderer.compute_storage_buffer_layout(), 1);
// update desc_set
auto bufferInfo = vk::DescriptorBufferInfo{*buffer, 0, VK_WHOLE_SIZE};
auto desc_write = vk::WriteDescriptorSet{
*desc_set, 0, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &bufferInfo};
*desc_set, 0, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &bufferInfo};
renderer.device().vk_device()->updateDescriptorSets(1u, &desc_write, 0, nullptr);
}
......@@ -505,6 +505,7 @@ namespace mirrage::renderer {
uniforms.keyframe_count = gsl::narrow<std::uint32_t>(cfg.keyframes.size());
uniforms.rotate_with_velocity = cfg.rotate_with_velocity ? 1 : 0;
uniforms.symmetric_scaling = cfg.symmetric_scaling ? 1 : 0;
auto ndf = std::uint32_t(0);
auto i = std::uint32_t(0);
......@@ -561,11 +562,17 @@ namespace mirrage::renderer {
glm::vec4 position;
glm::quat rotation_quat;
Random_value<glm::vec4> direction;
glm::vec2 size;
float ttl_mean;
float ttl_stddev;
float velocity_mean;
float velocity_stddev;
std::uint32_t direction_flags;
std::uint32_t offset;
std::uint32_t to_spawn;
std::uint32_t base_seed;
......@@ -594,12 +601,18 @@ namespace mirrage::renderer {
(p.system->position() - p.system->_last_position) / dt * cfg.parent_velocity, 0.f);
pcs.position = glm::vec4(p.system->emitter_position(*p.emitter), 0.f);
pcs.rotation_quat = p.system->emitter_rotation(*p.emitter);
pcs.size = glm::vec2(cfg.size.x, cfg.size.y);
pcs.direction = cfg.direction;
pcs.ttl_mean = cfg.ttl.mean;
pcs.ttl_stddev = cfg.ttl.stddev;
pcs.velocity_mean = cfg.velocity.mean;
pcs.velocity_stddev = cfg.velocity.stddev;
pcs.direction_flags = std::uint32_t(cfg.independent_direction ? 1 : 0)
| std::uint32_t(cfg.direction_normal_distribution ? 2 : 0);
pcs.offset = std::uint32_t(offset);
pcs.to_spawn = std::uint32_t(p.emitter->particles_to_spawn());
pcs.base_seed = std::uniform_int_distribution<std::uint32_t>{}(_rand);
......
Supports Markdown
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