godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.29k stars 21.05k forks source link

GPUParticles3D Emitter Velocity scales with FPS above 60 when movement is done in physics_process #87662

Closed CBerry22 closed 2 months ago

CBerry22 commented 8 months ago

Tested versions

Reproducible in 4.2.1 & 4.2.2 RC1

System information

Godot v4.2.1.stable - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3080 (NVIDIA; 31.0.15.4584) - 12th Gen Intel(R) Core(TM) i5-12600K (16 Threads)

Issue description

When a GPUParticles3D node is a child of a Rigidbody3D with Inherit_Velocity_Ratio set to a value higher than 0, the amount of velocity that gets added scales with FPS.

60 FPS behaves as expecting, but running at 120+ FPS makes the particles fling off into the distance and behave erratically.

Steps to reproduce

Prerequisites; have a monitor with a refresh rate higher than 60hz, or disable VSync in your GPU settings and in the Godot project.

  1. Create a Rigidbody3D, apply force to make it move
  2. Create a child GPUParticles3D node with Inherit_Velocity_Ratio set to 1.0
  3. Run the project at an uncapped FPS and then at 60 FPS, there is a noticeable difference in behaviour.

Minimal reproduction project (MRP)

Inherit Velocity MRP.zip

CBerry22 commented 8 months ago

Upon some further experimentation it's not so much to do with Rigidbody3D as it is to do with movement in _physics_process.

Moving a Node3D via translate within _physics_process produces different particle velocity to the same logic running in _process when ran at frame rates higher than 60 fps.

Here's an updated MRP to demonstrate this: Updated Inherit Velocity MRP.zip

michal2229 commented 3 months ago

For me in this setup (GPUParticles3D node is a child of a RigidBody3D with Inherit_Velocity_Ratio set to 1), particles are behaving erratically even at fixed 60fps. Reproducible on 4.2 and 4.3.beta.

Calinou commented 3 months ago

This probably has to do with movement not happening on every rendered frame when movement is done in _physics_process(), so the velocity goes from this (as seen by the particle system):

2 2 2 2 2 2 2

To something like this:

4 0 4 0 4 0 4 0 4 0

To resolve this, there are two solutions:

cc @QbieShay

QbieShay commented 3 months ago

I don't think they should average the 5 last rendered frames: often you use inherited velocity with projectiles or trails, and this will just make particles spawn in front of the character. Is there no way to calculate the velocity uniform in physics process instead?

Calinou commented 3 months ago

Emitter velocity is set here:

https://github.com/godotengine/godot/blob/9425535602a526a1b021838a7d021a5d75a62574/scene/2d/gpu_particles_2d.cpp#L713-L722

It looks like it's easy to move this to NOTIFICATION_INTERNAL_PHYSICS_PROCESS so that the velocity is only updated once per physics frame, which would make more sense to me.

Edit: Done in https://github.com/godotengine/godot/pull/93837. The change was applied in both 2D and 3D.