SteamVR: tweak update poses (fake tracking latency)

Hi,


Unity version: 2017.1.0f3


I’m willing to simulate latency in the head tracking of my Oculus: when one moves the head, the image should but updated a few ms later and not “immediately”.


Since SteamVR_UpdatePoses is deprecated, the script that handles head position is SteamVR_Render.cs. Here are the lines that seems to be related to the tracking:


// [...]

public TrackedDevicePose_t[] poses = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount];
public TrackedDevicePose_t[] gamePoses = new TrackedDevicePose_t[0];

// [...]

public void UpdatePoses()
{
	var compositor = OpenVR.Compositor;
	if (compositor != null)
	{
		compositor.GetLastPoses(poses, gamePoses);
		SteamVR_Events.NewPoses.Send(poses);
		SteamVR_Events.NewPosesApplied.Send();
	}
 }

void OnBeforeRender()
{
    UpdatePoses();
}

Here is what I tried:

// [...]

public TrackedDevicePose_t[] poses = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount];
public TrackedDevicePose_t[] gamePoses = new TrackedDevicePose_t[0];

// [...]

public void UpdatePoses()
{
     var compositor = OpenVR.Compositor;
     if (compositor != null)
     {
        compositor.GetLastPoses(poses, gamePoses);
        SteamVR_Events.NewPoses.Send(poses);
        SteamVR_Events.NewPosesApplied.Send();
     }
}

void OnBeforeRender()
{
     StartCoroutine(Lag());
}

private IEnumerator Lag()
{
     yield return new WaitForSeconds(1f);
     UpdatePoses();
}

As you can see, i only store the current position/rotation values and wait for a given time (1 second here but much less in actual development) before updating the camera transform.


When I try to make a non-tracked object follow (with lag) a tracked object with a similar piece of code (same coroutine system), it DOES work perfectly. However, when I apply my code (coroutine) directly (as above) to the tracking: nothing changes. The object moves in “real time” despite the “custom” tracking, as if I did not tweak anything.


My question is then: has anyone already played with the SteamVR tracking and/or can anyone point me what I am missing ? Or maybe there is an other way to cheat the tracking system of a VR headset ?


Cheers,


KdRWaylander

Here is the workaround I found (I was never able to tweak directly the tracking system):


  • I added an other camera with a higher depth and a target eye parameter set to “Both” in my scene. See this post that describes how to have 2 cameras in a scene.
  • I added a script on my new camera that makes the camera follow the SteamVR camera after a given amount of time (see below). This may be done differently (for exemple with a position buffer) and this may not be optimized but anyhow, it works.
  • I added the Tracked Pose Driver component (available since v2017.3 at least, maybe v2017.2) and then DISABLED IT. It is important to do so, this allows to make Unity understand that it must not override the position of the object (it seems to do so by default if there’s a Camera component on the gameobject)
  • Lastly, i added the same Tracked Pose Driver component on the SteamVR camera (unless what nothing would move, I don’t know why)

My follow script (C#):

using System.Collections.Generic;
using UnityEngine;

public class FollowWithLag : MonoBehaviour {
    [SerializeField] Transform  m_targetToFollow;
    [SerializeField] bool       m_lag;
    [SerializeField] float      m_lagTime;

    Vector3     m_targetPosition;
    Quaternion  m_targetRotation;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.L))
        {
            m_lag = !m_lag;
        }
    }

    void LateUpdate ()
    {
        m_targetPosition = new Vector3(m_targetToFollow.position.x, m_targetToFollow.position.y, m_targetToFollow.position.z);
        m_targetRotation = m_targetToFollow.transform.rotation;

        if (m_lag && m_lagTime != 0)
        {
            StartCoroutine(LaggyFollow(m_targetPosition, m_targetRotation));
        }
        else if (!m_lag || m_lagTime == 0)
        {
            transform.position = m_targetPosition;
            transform.rotation = m_targetRotation;
        }
    }

    private IEnumerator LaggyFollow(Vector3 _pos, Quaternion _rot)
    {
        yield return new WaitForSeconds(m_lagTime/1000f);
        transform.position = _pos;
        transform.rotation = _rot;
    }
}

Hope this helps someone one day.

Waylander

We had a hard time figuring this out until we saw your simple explanation! We are working on an experiment at our university for a course, so with the deadline approaching we thought we wouldn’t make it. Thank you so much!

For people still looking at this issue, the script below does the lag part very efficiently

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

public class CameraFollow : MonoBehaviour
{
    public Transform mainCam; //this would be VR Camera or AR camera
    public float delay = 0.5f;


    public Camera camera; //this would be the 2nd added camera
    private struct PointInSpace
    {
        public Vector3 Position;
        public float Time;

    }

    private Queue<PointInSpace> pointsInSpace = new Queue<PointInSpace>();


    void FixedUpdate()
    {
        // Add the current target position to the list of positions
        pointsInSpace.Enqueue(new PointInSpace() { Position = mainCam.position, Time = Time.time });

        // Move the camera to the position of the target X seconds ago 
        while (pointsInSpace.Count > 0 && pointsInSpace.Peek().Time <= Time.time - delay + Mathf.Epsilon)
        {
            transform.position = pointsInSpace.Dequeue().Position;
        }
    }
}

However, this works only in Unity editor for AR cases, which I am looking into. @KdRWaylander if you have any suggestion or idea please let us know.