From 01c8b25cb64153194c6dd1bb484b5fce3790ec72 Mon Sep 17 00:00:00 2001 From: Elderme <talsi.elderme@gmail.com> Date: Thu, 13 Aug 2015 14:50:33 +0200 Subject: [PATCH 1/2] When the particles position is computed, the position of the emitter in both current and previous frames is now taken into account. There is no more visual artifact when the particles velocity and emitter velocity are not aligned (for example with nitro particles when a kart is skidding) --- data/shaders/pointemitter.vert | 58 +++++++++++++++++++++------------- src/graphics/gpu_particles.cpp | 28 ++++++++++------ src/graphics/gpu_particles.hpp | 5 ++- 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/data/shaders/pointemitter.vert b/data/shaders/pointemitter.vert index 68fb15c8b..361ace64e 100644 --- a/data/shaders/pointemitter.vert +++ b/data/shaders/pointemitter.vert @@ -1,4 +1,5 @@ uniform int dt; +uniform mat4 previous_frame_sourcematrix; uniform mat4 sourcematrix; uniform int level; uniform float size_increase_factor; @@ -32,31 +33,44 @@ out float new_size; void main(void) { - float updated_lifetime = lifetime + (float(dt)/lifetime_initial); - if (updated_lifetime > 1.) - { - if (gl_VertexID < level) + float updated_lifetime = lifetime + (float(dt)/lifetime_initial); + if (updated_lifetime > 1.) { - float dt_from_last_frame = fract(updated_lifetime) * lifetime_initial; - vec4 updated_initialposition = sourcematrix * vec4(particle_position_initial, 1.0); - vec4 updated_initial_velocity = sourcematrix * vec4(particle_position_initial + particle_velocity_initial, 1.0) - updated_initialposition; - new_particle_position = updated_initialposition.xyz + updated_initial_velocity.xyz * float(dt_from_last_frame); - new_particle_velocity = updated_initial_velocity.xyz; - new_lifetime = fract(updated_lifetime); - new_size = mix(size_initial, size_initial * size_increase_factor, fract(updated_lifetime)); + if (gl_VertexID < level) + { + float dt_from_last_frame = fract(updated_lifetime) * lifetime_initial; + float coeff = 1. - dt_from_last_frame / dt; + + vec4 previous_frame_position = previous_frame_sourcematrix * vec4(particle_position_initial, 1.0); + vec4 current_frame_position = sourcematrix * vec4(particle_position_initial, 1.0); + + vec4 updated_initialposition = mix(previous_frame_position, + current_frame_position, + coeff); + + vec4 updated_initial_velocity = mix(previous_frame_sourcematrix * vec4(particle_velocity_initial, 0.0), + sourcematrix * vec4(particle_velocity_initial, 0.0), + coeff) ; //TODO: add emitter speed + //+ (current_frame_position - previous_frame_position) / dt; + + new_particle_position = updated_initialposition.xyz + dt_from_last_frame * updated_initial_velocity.xyz; + new_particle_velocity = updated_initial_velocity.xyz; + + new_lifetime = fract(updated_lifetime); + new_size = mix(size_initial, size_initial * size_increase_factor, fract(updated_lifetime)); + } + else + { + new_lifetime = fract(updated_lifetime); + new_size = 0; + } } else { - new_lifetime = fract(updated_lifetime); - new_size = 0; + new_particle_position = particle_position + particle_velocity.xyz * float(dt); + new_particle_velocity = particle_velocity; + new_lifetime = updated_lifetime; + new_size = (size == 0) ? 0. : mix(size_initial, size_initial * size_increase_factor, updated_lifetime); } - } - else - { - new_particle_position = particle_position + particle_velocity.xyz * float(dt); - new_particle_velocity = particle_velocity; - new_lifetime = updated_lifetime; - new_size = (size == 0) ? 0. : mix(size_initial, size_initial * size_increase_factor, updated_lifetime); - } - gl_Position = vec4(0.); + gl_Position = vec4(0.); } diff --git a/src/graphics/gpu_particles.cpp b/src/graphics/gpu_particles.cpp index e63363de5..9eb8525d0 100644 --- a/src/graphics/gpu_particles.cpp +++ b/src/graphics/gpu_particles.cpp @@ -37,7 +37,7 @@ /** Transform feedback shader that simulates the particles on GPU. */ class PointEmitterShader : public Shader - < PointEmitterShader, core::matrix4, int, int, float > + < PointEmitterShader, core::matrix4, core::matrix4, int, int, float > { public: PointEmitterShader() @@ -45,7 +45,8 @@ public: const char *varyings[] = { "new_particle_position", "new_lifetime", "new_particle_velocity", "new_size" }; loadTFBProgram("pointemitter.vert", varyings, 4); - assignUniforms("sourcematrix", "dt", "level", "size_increase_factor"); + assignUniforms("previous_frame_sourcematrix", "sourcematrix", + "dt", "level", "size_increase_factor"); } // PointEmitterShader }; // PointEmitterShader @@ -262,7 +263,7 @@ void ParticleSystemProxy::generateParticlesFromPointEmitter(scene::IParticlePoin ParticleParams[i].PositionZ = 0; // Initial lifetime is >1 InitialValues[i].Lifetime = 2.; - + memcpy(&(InitialValues[i].PositionX), &(ParticleParams[i].PositionX), 3 * sizeof(float)); generateLifetimeSizeDirection(emitter, ParticleParams[i].Lifetime, ParticleParams[i].Size, @@ -466,11 +467,11 @@ void ParticleSystemProxy::CommonSimulationVAO(GLuint position_vbo, GLuint initia } void ParticleSystemProxy::simulate() -{ +{ int timediff = int(GUIEngine::getLatestDt() * 1000.f); int active_count = getEmitter()->getMaxLifeTime() * getEmitter()->getMaxParticlesPerSecond() / 1000; core::matrix4 matrix = getAbsoluteTransformation(); - + glEnable(GL_RASTERIZER_DISCARD); if (has_height_map) { @@ -482,9 +483,10 @@ void ParticleSystemProxy::simulate() else { PointEmitterShader::getInstance()->use(); - PointEmitterShader::getInstance()->setUniforms(matrix, timediff, active_count, size_increase_factor); + PointEmitterShader::getInstance()->setUniforms(m_previous_frame_matrix, matrix, timediff, active_count, size_increase_factor); } - + m_previous_frame_matrix = matrix; + glBindVertexArray(current_simulation_vao); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfb_buffers[1]); @@ -617,8 +619,14 @@ void ParticleSystemProxy::render() { return; } if (m_first_execution) + { generateVAOs(); - m_first_execution = false; - simulate(); - draw(); + m_previous_frame_matrix = getAbsoluteTransformation(); + m_first_execution = false; + } + else + { + simulate(); + draw(); + } } diff --git a/src/graphics/gpu_particles.hpp b/src/graphics/gpu_particles.hpp index f04464661..b988ef167 100644 --- a/src/graphics/gpu_particles.hpp +++ b/src/graphics/gpu_particles.hpp @@ -42,7 +42,10 @@ protected: bool m_randomize_initial_y; GLuint texture; - + + /** Previous frame particles emitter source matrix */ + core::matrix4 m_previous_frame_matrix; + /** Current count of particles. */ unsigned m_count; /** Previous count - for error handling only. */ From 0b748634aeaad5207ae955ee8149dd9411ba0400 Mon Sep 17 00:00:00 2001 From: Elderme <talsi.elderme@gmail.com> Date: Thu, 13 Aug 2015 18:15:34 +0200 Subject: [PATCH 2/2] Removed calls to resizeBox for nitro emitter --- data/shaders/pointemitter.vert | 18 +++++++++++------- src/graphics/gpu_particles.hpp | 2 +- src/karts/kart.cpp | 5 ++--- src/karts/kart_gfx.cpp | 10 ++-------- src/karts/kart_gfx.hpp | 2 +- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/data/shaders/pointemitter.vert b/data/shaders/pointemitter.vert index 361ace64e..503acd2d1 100644 --- a/data/shaders/pointemitter.vert +++ b/data/shaders/pointemitter.vert @@ -39,19 +39,23 @@ void main(void) if (gl_VertexID < level) { float dt_from_last_frame = fract(updated_lifetime) * lifetime_initial; - float coeff = 1. - dt_from_last_frame / dt; + float coeff = dt_from_last_frame / dt; vec4 previous_frame_position = previous_frame_sourcematrix * vec4(particle_position_initial, 1.0); vec4 current_frame_position = sourcematrix * vec4(particle_position_initial, 1.0); - vec4 updated_initialposition = mix(previous_frame_position, - current_frame_position, - coeff); + vec4 updated_initialposition = mix(current_frame_position, + previous_frame_position, + coeff); - vec4 updated_initial_velocity = mix(previous_frame_sourcematrix * vec4(particle_velocity_initial, 0.0), - sourcematrix * vec4(particle_velocity_initial, 0.0), - coeff) ; //TODO: add emitter speed + vec4 updated_initial_velocity = mix(sourcematrix * vec4(particle_velocity_initial, 0.0), + previous_frame_sourcematrix * vec4(particle_velocity_initial, 0.0), + coeff); //+ (current_frame_position - previous_frame_position) / dt; + //To be accurate, emitter speed should be added. + //But the simple formula ( (current_frame_position - previous_frame_position) / dt ) with a constant speed + //between 2 frames creates visual artifacts when the framerate is low, and a more accurate formula would need + //more complex computations. new_particle_position = updated_initialposition.xyz + dt_from_last_frame * updated_initial_velocity.xyz; new_particle_velocity = updated_initial_velocity.xyz; diff --git a/src/graphics/gpu_particles.hpp b/src/graphics/gpu_particles.hpp index b988ef167..d23d3952f 100644 --- a/src/graphics/gpu_particles.hpp +++ b/src/graphics/gpu_particles.hpp @@ -40,7 +40,7 @@ protected: float m_color_to[3]; bool m_first_execution; bool m_randomize_initial_y; - + GLuint texture; /** Previous frame particles emitter source matrix */ diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 6012a0a0b..dd7cffc06 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2605,9 +2605,8 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, // the normal maximum speed of the kart. if(nitro_frac>1.0f) nitro_frac = 1.0f; } - // speed * dt is the new size of the box in which particles start - m_kart_gfx->updateNitroGraphics(nitro_frac, getSpeed()*dt); - + m_kart_gfx->updateNitroGraphics(nitro_frac); + // Handle leaning of karts // ----------------------- // Note that we compare with maximum speed of the kart, not diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index ae62b1073..35785a31c 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -350,11 +350,10 @@ void KartGFX::update(float dt) } // update // ---------------------------------------------------------------------------- -/** Updates nitro dependent particle effects (and box sizes). +/** Updates nitro dependent particle effects. * \param nitro_frac Nitro fraction/ - * \param new_size New size of the box in which new particles are emitted. */ -void KartGFX::updateNitroGraphics(float nitro_frac, float new_size) +void KartGFX::updateNitroGraphics(float nitro_frac) { // Upate particle effects (creation rate, and emitter size // depending on speed) @@ -375,11 +374,6 @@ void KartGFX::updateNitroGraphics(float nitro_frac, float new_size) setCreationRateAbsolute(KartGFX::KGFX_NITROSMOKE2, 0); m_nitro_light->setVisible(false); } - resizeBox(KartGFX::KGFX_NITRO1, new_size); - resizeBox(KartGFX::KGFX_NITRO2, new_size); - resizeBox(KartGFX::KGFX_NITROSMOKE1, new_size); - resizeBox(KartGFX::KGFX_NITROSMOKE2, new_size); - resizeBox(KartGFX::KGFX_ZIPPER, new_size); } // updateGraphics diff --git a/src/karts/kart_gfx.hpp b/src/karts/kart_gfx.hpp index 5ac31df26..8f3c26490 100644 --- a/src/karts/kart_gfx.hpp +++ b/src/karts/kart_gfx.hpp @@ -99,7 +99,7 @@ public: void setCreationRateRelative(const KartGFXType type, float f); void updateTerrain(const ParticleKind *pk); void update(float dt); - void updateNitroGraphics(float f, float new_size); + void updateNitroGraphics(float f); void updateSkidLight(unsigned int level); }; // KartWGFX