FixedUpdate - Vector3 position with alpha

Started by
7 comments, last by JoeJ 1 year, 7 months ago

Hi everyone,

I read the fix your timestamp blog, I loved it, and I agree with it, my only issue is that if I want to use it, I will have to code something like this

template<typename T>
struct FixUpdT
{
    public:
        FixUpdT()
            : beg{}
            , end{}
            , cur{}
        {
        }

        void Init()
        {
            cur = beg = end;
        }

        void Sim( float alpha )
        {
            cur = Math::Lerp( beg, end, alpha );
        }

        T beg;
        T end;
        T cur;
};

class Entity
{
    public:
        void Update( float dt )
        {
            m_position.Init();
        }

        void Render( float dt, float alpha )
        {
            m_position.Sim( alpha );
        }

        Vector3& GetPosition() const { return m_position.end; }
        Vector3& GetSimPosition() const { return m_position.cur; }

        FixUpdT<Vector3> m_position;
}

All the entities would have to call Update and Init function (to fix this, I could have a dirty flag if position has change), also I would have to think when to use GetPosition (in update function prob) and when GetSimPosition (in render prob)

But I'm not 100% sure if this is the best way, anyone else have a comment about this, maybe I'm doing something wrong.

Thanks!

Advertisement

To me it seems a waste to store 3 states for everything.

The interpolation is needed only for rendering, which likely uses a transform matrix per entity.
You could then apply the interpolation when generating this matrix using velocity, which i guess each entity has.
So no extra states, and you can do it only for objects on screen eventually.

@JoeJ I do have a transform matrix, but, how do u apply the interpolation with the velocity? I will still need begin and end state.

Do u have any example? Because I don't understand how to do it. In my head I will still need 3 states, or at least 2.

// my matrix (transform)
struct Matrix
{
    public:
        Vector3 m_right;
        uint32  m_pad1;
        Vector3 m_up;
        uint32  m_pad2;
        Vector3 m_forward;
        uint32  m_pad3;
        Vector3 m_pos;
        uint32  m_pad4;
};

Rewaz said:
Do u have any example?

hmmm, i'll try….

float tdiff = realTime - physicsTime;
matrix.m_pos = rigidBody.pos + rigidBody.velocity * tdiff;

For rotation it's usually done using quaternions, but if you use a (physics) engine, it should have tooling functions to do such interpolation.

Rewaz said:
Because I don't understand how to do it. In my head I will still need 3 states, or at least 2.

Your idea to interpolate the current and the previous state is correct, but so is mine. Basically i do a simple Euler integration step, while you do a Verlet integration. Error should be similar for both. (I hope it's correct to say this.)

If physics time is ahead real time, and real time is ahead previous physics time, your idea has the advantage to be just an interpolation (robust), while mine may act more like an extrapolation in any case.

But i lack experience. I run my physics at 120 Hz and never cared to implement interpolation at all til yet.
It might be worth to check both methods. A difference might be noticeable if motion is not continuous, e.g. if collisions happen.

JoeJ said:

float tdiff = realTime - physicsTime;
matrix.m_pos = rigidBody.pos + rigidBody.velocity * tdiff;

For rotation it's usually done using quaternions, but if you use a (physics) engine, it should have tooling functions to do such interpolation.

I do have fn to do such interpolation, but, will not work, doing that it's the same as using the movement behavior where I do

pos += forward * speed * dt;

It's the same, but willn't work, physics run at a constant 0.0333 as dt. With rigidBody velocity (which I don't have as rigidBody but as movement behavior) there can be cases where position + velocity * dt will go beyond the position it should be, so isn't safe to do, I don't know if I left something behind, but if I try what u do, it will simple be wrong, with fixed update.

From fix your timestamp:

To understand what is going on consider a situation where the display framerate is 60fps and the physics is running at 50fps. There is no nice multiple so the accumulator causes the simulation to alternate between mostly taking one and occasionally two physics steps per-frame when the remainders “accumulate” above dt.

Now consider that the majority of render frames will have some small remainder of frame time left in the accumulator that cannot be simulated because it is less than dt. This means we’re displaying the state of the physics simulation at a time slightly different from the render time, causing a subtle but visually unpleasant stuttering of the physics simulation on the screen.

One solution to this problem is to interpolate between the previous and current physics state based on how much time is left in the accumulator:

Rewaz said:
From fix your timestamp:

I refuse to read this (again), but i guess they talk about how to run physics at a fixed step, and how to sync that to real time. They do not talk about a danger to extrapolate visuals.
At the bottom they mention one solution to adjust visuals from interpolating 2 states, i mention another solution of displacing current state with current velocity.

Rewaz said:
With rigidBody velocity (which I don't have as rigidBody but as movement behavior) there can be cases where position + velocity * dt will go beyond the position it should be

That's why i do not use dt (the fixed step), but the difference of physics to real time.
However, neither mine nor your method will get the ‘correct’ position on a curved trajectory, as both our methods use linear approximations. Which is fine for just visuals.

Ofc. i don't know if your movement vector represents velocity as used in simulations. Velocity means displacement per unit time, your's might mean something else. Regardless - the concept is probably still applicable.

But it's up to you.
If you go with storing 2 or even 3 states, i would at least store them in different arrays, so the sole purpose to have extra states just for visuals does not hurt your entire game by filling caches with data which isn't used often.

Okay, I get it now. Do u still update if GetVelocity() return 0 0 0?

Also, do you use the same for rotation? Like

RotateQuaternion( matrix, RigidBody.GetRotationSpeed(), tdiff );

Or u have something diff?

Here a snippet to integrate angular velocity to orientation (using tdiff as the timeStep, yes):

sQuat IntegrateVelocity (const sQuat &curOrn, const sVec3 &angvel, float timeStep)
{

	// convert angvel to quaternion rotation with angle multiplied by timeStep

	
	sVec3 axis;
	float angle = angvel.Length(); 
	
	if ( angle < 0.00001f ) // angvel is too small to integrate

		return curOrn;

	else
		axis = angvel * (sin (angle * timeStep * .5f) / angle);

	sQuat dorn (axis.x(), axis.y(), axis.z(), cos (angle * timeStep * .5f));
	
	
// integrate to current orientation
	

	sQuat predictedOrn = dorn * curOrn; // depending on quaternion library convention, eventually reverse multiplication order
	predictedOrn.Normalize();
	return predictedOrn;
}

Edit: If you don't store quaternions for your entities, it's probably suboptimal to convert matrix to quat and back after the integration.
I'd rather make a rotation matrix from angular velocity instead. Here is a tutorial about the math.

This topic is closed to new replies.

Advertisement