Obi Official Forum

Full Version: ObiCloth + character interpolation - how to fix jittering?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
In my game, characters are updated in FixedUpdate, and ObiCloth is updated in LateFixedUpdate. Everything works fine:

https://www.youtube.com/watch?v=cSCiK8088Fw

(The forum software won't let me add two videos to a post, so I'm just linking the one above instead.)

I'd like to interpolate my characters' transforms in LateUpdate so that their motion is smoother. I'm using a character controller called KinematicCharacterController, which implements interpolation how you'd expect - which is to say, characters have transforms that are restored to their last-ticked position at the start of the character update, and then those transforms are position/rotation-interpolated in LateUpdate.

This causes ugly jittering with my current ObiCloth setup, presumably because characters are not at their simulated positions when they're rendered:



Is there a solution to this? (I'd rather not simulate cloth in LateUpdate if I can avoid it, given the caveats in ObiLateUpdater.)
(06-01-2022, 02:11 AM)timconkling Wrote: [ -> ]In my game, characters are updated in FixedUpdate, and ObiCloth is updated in LateFixedUpdate. Everything works fine:

I'd like to interpolate my characters' transforms in LateUpdate so that their motion is smoother. I'm using a character controller called KinematicCharacterController, which implements interpolation how you'd expect - which is to say, characters have transforms that are restored to their last-ticked position at the start of the character update, and then those transforms are position/rotation-interpolated in LateUpdate.

This causes ugly jittering with my current ObiCloth setup, presumably because characters are not at their simulated positions when they're rendered:

Is there a solution to this? (I'd rather not simulate cloth in LateUpdate if I can avoid it, given the caveats in ObiLateUpdater.)

Hi!

For physics of any kind to be accurate, they must be updated using a fixed timestep ---> must be updated in FixedUpdate().
FixedUpdate happens before Update and LateUpdate every frame. So if you're altering character position in LateUpdate, your character is moving away from the cloth, the cloth does not pickup this change on the same frame (as it's been already updated), and this causes jitter.

However this is not the only jitter source you will encounter. FixedUpdate can be called multiple times per frame, or not called at all some frames. In practice this happens when the rendering frequency is high and your timestep large (eg, 120 fps using a timestep of 0.02) since each frame does not accumulate enough time for the engine to advance one timestep. This will cause jitter because some frames, cloth might not be updated at all but your character controller will.

The only way to get this to work without resorting to update cloth in LateUpdate is to take full control over the update order of both your character controller and the cloth simulation. Things must happen in this specific order:

1) - Character animation
2) - Character interpolation
3) - Cloth simulation.

You will have to modify your character controller so that it performs the interpolation before cloth simulation. This can be done in a variety of ways, the simplest being:

- Hooking onto ObiSolver.OnBeginStep, which is called right before updating the cloth. This is a regular C# event that you can subscribe to.
- Writing your own ObiUpdater component (you can just copy the included ObiFixedUpdater) and insert the character interpolation before cloth sim.

Hope this is of some help. Let me know how it goes!
(10-01-2022, 08:36 AM)josemendez Wrote: [ -> ]The only way to get this to work without resorting to update cloth in LateUpdate is to take full control over the update order of both your character controller and the cloth simulation. Things must happen in this specific order:

1) - Character animation
2) - Character interpolation
3) - Cloth simulation.

You will have to modify your character controller so that it performs the interpolation before cloth simulation. This can be done in a variety of ways, the simplest being:

Hey Jose, apologies for necroing this thread after so long! I'm (finally) revisiting the issue.

Yep, totally understand that cloth needs to be updated in `FixedUpdate` (and, because of the way my game works, characters also need to be updated in the `FixedUpdate` timestep as well). I think I did a poor job of explaining the original issue:

My character controller (which is updated in FixedUpdate) performs interpolation in Update, similar to how `ObiUpdater.Interpolate` works. That is, it interpolates between the previous and current states of the characters transform's position and rotation, just before scene rendering. No simulation is happening in this interpolate step; I update my characters (and the rest of the game logic) at 30fps, but render at 60+ fps, and so the interpolation just smoothes out character motion. 

In other words, the game's characters get rendered at a slightly different location than they were last simulated at. What I'd like to do is have the cloth's render location match up with the interpolated character location (WITHOUT performing any additional cloth simulation; the cloth is already simulated, but now it just needs to be shifted slightly to the new interpolated location for rendering purposes - and then shifted back to its proper location before the next cloth simulation step executes).

Does this clarify things at all? Is it possible to achieve what I'm looking for?

Thanks!
@josemendez - bumping this thread in the hopes of getting a response!
(27-09-2023, 07:34 PM)timconkling Wrote: [ -> ]Hey Jose, apologies for necroing this thread after so long! I'm (finally) revisiting the issue.

Yep, totally understand that cloth needs to be updated in `FixedUpdate` (and, because of the way my game works, characters also need to be updated in the `FixedUpdate` timestep as well). I think I did a poor job of explaining the original issue:

My character controller (which is updated in FixedUpdate) performs interpolation in Update, similar to how `ObiUpdater.Interpolate` works. That is, it interpolates between the previous and current states of the characters transform's position and rotation, just before scene rendering. No simulation is happening in this interpolate step; I update my characters (and the rest of the game logic) at 30fps, but render at 60+ fps, and so the interpolation just smoothes out character motion. 

In other words, the game's characters get rendered at a slightly different location than they were last simulated at. What I'd like to do is have the cloth's render location match up with the interpolated character location (WITHOUT performing any additional cloth simulation; the cloth is already simulated, but now it just needs to be shifted slightly to the new interpolated location for rendering purposes - and then shifted back to its proper location before the next cloth simulation step executes).

Does this clarify things at all? Is it possible to achieve what I'm looking for?

Thanks!

Hi Tim,

Sorry for taking so long to reply, your post slipped trough the cracks. Very sorry for that.

Yes, it is possible to do what you need. Particles in Obi have a "renderablePosition" property, which is used to store interpolated positions (when solver interpolation is enabled, which I assume you're already using but found it doesn't work in your case due to the specifics of your custom interpolation system) and used for rendering the cloth. 

The physics simulation outputs particle "positions" in FixedUpdate(), then every frame in LateUpdate() it interpolates the previous and current particle positions and stores the result in "renderablePositions". These values are then used to update the cloth mesh. Renderable positions are not fed back into the next simulation step, they're discarded and re-calculated the next LateUpdate().

So if you're performing custom character interpolation, you can shift the values in solver.renderablePositions so that they match up with your character. This is best done in the solver.OnInterpolate callback event, which is called
after the solver populates the renderablePositions array, but before using it to update the cloth mesh. This will basically overwrite the results of Obi's internal interpolation with your own values right before rendering each frame.

kind regards,
(12-10-2023, 06:47 AM)josemendez Wrote: [ -> ]Hi Tim,

Sorry for taking so long to reply, your post slipped trough the cracks. Very sorry for that.

Yes, it is possible to do what you need. Particles in Obi have a "renderablePosition" property, which is used to store interpolated positions (when solver interpolation is enabled, which I assume you're already using but found it doesn't work in your case due to the specifics of your custom interpolation system) and used for rendering the cloth. 

The physics simulation outputs particle "positions" in FixedUpdate(), then every frame in LateUpdate() it interpolates the previous and current particle positions and stores the result in "renderablePositions". These values are then used to update the cloth mesh. Renderable positions are not fed back into the next simulation step, they're discarded and re-calculated the next LateUpdate().

So if you're performing custom character interpolation, you can shift the values in solver.renderablePositions so that they match up with your character. This is best done in the solver.OnInterpolate callback event, which is called
after the solver populates the renderablePositions array, but before using it to update the cloth mesh. This will basically overwrite the results of Obi's internal interpolation with your own values right before rendering each frame.

kind regards,

This makes sense - thanks so much! I'll post back here once I've had a chance to implement.
The solution turned out to be even simpler: I needed to ensure that my Obi cloth interpolation was performed *after* character controller interpolation. (The character controller was interpolating in LateUpdate, and Obi was interpolating in Update.)

Thanks again for the help!