To compensate for shaky hands when holding a mobile device I'm averaging the rotation of the phone over the last n
(in this case 16) frames, and setting the camera's rotation to this average. This works smoothly and allows the user to look around in 360° by moving their phone around without getting sick of vibrations.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class OrientationController : MonoBehaviour
{
private List<Quaternion> lastQuaternions = new List<Quaternion>();
private readonly int maxRotationCount = 16;
void Start()
{
//Load the cardboard after we've started up to prevent the cardboard overlay from showing
XRSettings.LoadDeviceByName("cardboard");
//Disable XR to go into "magic window" mode
XRSettings.enabled = false;
}
void Update()
{
UpdateCameraRotation();
}
/// <summary>
/// Update the main camera's rotation based on the data taken from the XRNode.Head
/// </summary>
private void UpdateCameraRotation()
{
lastQuaternions.Add(InputTracking.GetLocalRotation(XRNode.Head));
if (lastQuaternions.Count > maxRotationCount)
{
lastQuaternions.RemoveAt(0);
transform.localRotation = SmoothRotation();
}
}
/// <summary>
/// Get the average rotation over the last 16 frames
/// </summary>
/// <returns>The new rotation quaternion</returns>
private Quaternion SmoothRotation()
{
Quaternion quatA = lastQuaternions[0];
Quaternion quatB = lastQuaternions[1];
Quaternion quatC = lastQuaternions[2];
Quaternion quatD = lastQuaternions[3];
Quaternion quatE = lastQuaternions[4];
Quaternion quatF = lastQuaternions[5];
Quaternion quatG = lastQuaternions[6];
Quaternion quatH = lastQuaternions[7];
Quaternion quatI = lastQuaternions[8];
Quaternion quatJ = lastQuaternions[9];
Quaternion quatK = lastQuaternions[10];
Quaternion quatL = lastQuaternions[11];
Quaternion quatM = lastQuaternions[12];
Quaternion quatN = lastQuaternions[13];
Quaternion quatO = lastQuaternions[14];
Quaternion quatP = lastQuaternions[15];
Quaternion quatAB = Quaternion.Lerp(quatA, quatB, 0.5f);
Quaternion quatCD = Quaternion.Lerp(quatC, quatD, 0.5f);
Quaternion quatEF = Quaternion.Lerp(quatE, quatF, 0.5f);
Quaternion quatGH = Quaternion.Lerp(quatG, quatH, 0.5f);
Quaternion quatIJ = Quaternion.Lerp(quatI, quatJ, 0.5f);
Quaternion quatKL = Quaternion.Lerp(quatK, quatL, 0.5f);
Quaternion quatMN = Quaternion.Lerp(quatM, quatN, 0.5f);
Quaternion quatOP = Quaternion.Lerp(quatO, quatP, 0.5f);
Quaternion quatABCD = Quaternion.Lerp(quatAB, quatCD, 0.5f);
Quaternion quatEFGH = Quaternion.Lerp(quatEF, quatGH, 0.5f);
Quaternion quatIJKL = Quaternion.Lerp(quatIJ, quatKL, 0.5f);
Quaternion quatMNOP = Quaternion.Lerp(quatMN, quatOP, 0.5f);
Quaternion quatABCDEFGH = Quaternion.Lerp(quatABCD, quatEFGH, 0.5f);
Quaternion quatIJKLMNOP = Quaternion.Lerp(quatIJKL, quatMNOP, 0.5f);
//Quaternion quatABCDEFGHIJKLMNOP = Quaternion.Lerp(quatABCDEFGH, quatIJKLMNOP, 0.5f);//We don't currently want this extreme level of smoothness
return quatIJKLMNOP;
}
}
However, despite it working as intended my current approach doesn't feel optimal, as it relies on creating a lot of quaternion
s each frame, and lerp
ing between them.
It is also not scaleable at all, as I'd need to make hardcoded changes every time I want to change the intensity of the smoothing.
Using Unity 2019.1.8f1 running C# .Net 4.x scripting runtime, and IL2CPP scripting backend.
To recreate in Unity, you also need to set the build target to Android. Enable "Virtual reality supported" under project settings > player > XR Settings. enable the virtual reality SDKs "None" and "Cardboard" (In that order).
I'm not experiencing any performance issues with this implementation, but I am curious to know if there are approaches that don't require me to create so many quaternions, or at least make the number of quaternions I can average dynamic.