Quaternion.SmoothDamp?

I am working on AR/VR and often find I need to smooth damp rotations. Either to simulate a steady cam. Or to show head up display that won’t be fixed with the user’s rotation but will rather “follow” the gaze trying not to introduce motion sickens.

I often end up with something like:

    float eulerXVelocity;
    float eulerYVelocity;
    float eulerZVelocity;

    void Update()
    {
        var cam = Camera.main.transform;

        var currentAngles = this.transform.rotation.eulerAngles;
        var targetAngles = cam.rotation.eulerAngles;

        var x = Mathf.SmoothDampAngle(currentAngles.x, targetAngles.x, ref eulerXVelocity, 0.3f);
        var y = Mathf.SmoothDampAngle(currentAngles.y, targetAngles.y, ref eulerYVelocity, 0.3f);
        var z = Mathf.SmoothDampAngle(currentAngles.z, targetAngles.z, ref eulerZVelocity, 0.3f);

        this.transform.rotation = Quaternion.Euler(x, y, z);

        this.transform.position = cam.transform.position + this.transform.rotation * Vector3.forward;
    }

Resolving to Mathf.SmoothDampAngle, but I was kind of expecting something similar to exist on Quaternion or Vector3.SmoothDampAngle.

Am I missing something?

2 Likes

Unity - Scripting API: Quaternion.Slerp is the ‘correct’ way mathematically to move between two quaternions as it’s guaranteed to take the shortest distance. Interpolating between eulers can produce some really odd results although in your case it’ll probably be ok as the gap between them is very small.

You could use slerp with a small T value and that would gradually ‘chase’ to the correct destination quaternion over a period of frame updates.

1 Like

There is huge difference between what slerp and smooth damp do. The sensors from VR devices usually provide noisy input and the lerp functions tend to be much more reactive to sudden spikes in values compared to the smooth damp. Another issue is that smooth damp velocity will gradually increase and decrease, while the slerp will chase with large velocity that gradually decreases when you reach your target. That makes the smooth damp somewhat better for VR for things that may introduce motion sickness. For example a head up display that is lerped to stay infront of the camera with react quickly to movement and rotation and appear to be fixed to the cam, while with smooth damp it will take some time for the hud to build up velocity, when you move or rotate your head the HUD will appear to be fixed in space and chase its desired position later introducing less motion sickness.

With all that said - I am pretty familiar with slerp but it’s not what I want. Slerp version that keeps track of velocity as smooth damp would be ok.

Maybe you could try this code - it has a version of SmoothDamp for quaternions:

This code here also has the original Unity code for SmoothDamp that you could modify to work with quatnerions:

11 Likes

Wow, that’s much more helpful. I will check these as soon as I get in the office.
Thank you!

I know this is necro, but I just want to include that I’ve found a very sharp work-around with this little snippet:transform.rotation = Quaternion.Euler(Vector3.SmoothDamp(transform.rotation.eulerAngles, targetRotation.eulerAngles, ref velocity, rotateTime * Time.deltaTime));

Hope this helps anyone working for a smooth rotation!

8 Likes

See Vector3.SmoothDamp for explanation of usage :slight_smile:

public static Quaternion SmoothDampQuaternion(Quaternion current, Quaternion target, ref Vector3 currentVelocity, float smoothTime)
{
  Vector3 c = current.eulerAngles;
  Vector3 t = target.eulerAngles;
  return Quaternion.Euler(
    Mathf.SmoothDampAngle(c.x, t.x, ref currentVelocity.x, smoothTime),
    Mathf.SmoothDampAngle(c.y, t.y, ref currentVelocity.y, smoothTime),
    Mathf.SmoothDampAngle(c.z, t.z, ref currentVelocity.z, smoothTime)
  );
}
public static Vector3 SmoothDampEuler(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime)
{
  return new Vector3(
    Mathf.SmoothDampAngle(current.x, target.x, ref currentVelocity.x, smoothTime),
    Mathf.SmoothDampAngle(current.y, target.y, ref currentVelocity.y, smoothTime),
    Mathf.SmoothDampAngle(current.z, target.z, ref currentVelocity.z, smoothTime)
  );
}

Edit: About 2.5 years later… adding a note here that with SmoothDamp/SmoothDampAngle functions, you can end up with NaN issues if you have a smoothTime value of 0 or Time.deltaTime is 0 like with a paused game using a Time.timeScale of 0. You can add the following two lines at the top of each function to avoid both of these issues easily:

if (Time.deltaTime == 0) return current;
if (smoothTime == 0) return target;

Related info: Smoohtdamp NaN problem

10 Likes

The code from this answer is simple enough and achieved a very smoothdamp-like effect for me:

float delta = Quaternion.Angle(transform.rotation, targetRotation);
if (delta > 0f)
{
    float t = Mathf.SmoothDampAngle(_delta, 0.0f, ref currVel, rotateSmoothTime);
    t = 1.0f - (t / delta);
    transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, t);
}
9 Likes

I wanted a smooth camera to trak along with my vr pov - and sent the visual out to vpt8 vj software (using klakspout)

smooths position and rotation of “this” object according to a “player” GameObject

Code (CSharp):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Smooth : MonoBehaviour
{
[SerializeField] //for explicity sake
public GameObject player;
public float smoothTime = 0.3F;
private Vector3 velocity = Vector3.zero;
void LateUpdate()
{
this.transform.position = Vector3.SmoothDamp(this.transform.position, player.transform.position, ref velocity, smoothTime);
this.transform.rotation = Quaternion.Lerp(transform.rotation, player.transform.rotation, Time.deltaTime * smoothTime);
}
}