Search Unity

Question Physics .Raycast not working as expected in AR Foundation (dragging)

Discussion in 'AR' started by DoAugMenRo, Jan 4, 2021.

  1. DoAugMenRo

    DoAugMenRo

    Joined:
    Sep 17, 2019
    Posts:
    18
    I want to use the physics.raycast system rather than the ARRaycastManager, because i need to position objects on a regular plane instead of the AR tracked plane. However if I position and move an object with the physics raycast, there seem to be an unwanted drag. What causes this problem and how can i fix it? I would appreciate Your help very much, Thank You!

    The problem is visualized in the appended .gif. I cast two rays from two points near the screen center (just with a slight offset in y to better visualize the positioned objects). On the point of intersection between the rays and an according plane a cylinder is spawned and moved. A red cylinder is positioned at the hit position of the ARRaycast and an AR tracked plane and the green cylinder is spawned at the hit position of a regular plane and the physics.raycast. The regular plane is at the same height as the AR tracked plane. If I now move the camera, the ARRaycast moves the red cylinder as expected, while the green cylinder dargs.

    PhysicsRaycastDragInAR.gif

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.XR.ARFoundation;
    4. using UnityEngine.XR.ARSubsystems;
    5.  
    6.  
    7. [RequireComponent(typeof(ARRaycastManager))]
    8. [RequireComponent(typeof(ARPlaneManager))]
    9. public class RaycastComparison : MonoBehaviour
    10. {
    11.     ARRaycastManager m_RaycastManager;
    12.     public Camera ARCamera;
    13.  
    14.     public GameObject physicalPlane;
    15.     private GameObject spawnedPlane;
    16.     public float heightOffset = 0.0f;
    17.     public int layerMask;
    18.    
    19.     public GameObject ARPrefab;
    20.     public GameObject physicsPrefab;
    21.  
    22.     private Vector2 screenPositionAR;
    23.     private Vector2 screenPositionPhys;
    24.  
    25.     public GameObject ARRaycastObject { get; private set; }
    26.     public GameObject physicalRaycastObject { get; private set; }
    27.  
    28.     static List<ARRaycastHit> s_Hits = new List<ARRaycastHit>();
    29.  
    30.  
    31.  
    32.     void Awake()
    33.     {
    34.         m_RaycastManager = GetComponent<ARRaycastManager>();
    35.         layerMask = 1 << 10;
    36.         screenPositionAR = new Vector2(Screen.currentResolution.width / 2, Screen.currentResolution.height / 2 + 50);
    37.         screenPositionPhys = new Vector2(Screen.currentResolution.width / 2, Screen.currentResolution.height / 2 - 50);
    38.     }
    39.  
    40.     void Update()
    41.     {  
    42.         placeSphereAt(screenPositionAR);
    43.         placeSphereAtPhysical(screenPositionPhys);
    44.     }
    45.  
    46.     void placeSphereAt(Vector2 position)
    47.     {
    48.         if (m_RaycastManager.Raycast(position, s_Hits, TrackableType.PlaneWithinPolygon))
    49.         {
    50.             var hitPose = s_Hits[0].pose;
    51.  
    52.             if (spawnedPlane == null)
    53.             {
    54.                 Vector3 planePosition = hitPose.position;
    55.                 planePosition.y += heightOffset;
    56.                 spawnedPlane = Instantiate(physicalPlane, planePosition, hitPose.rotation);
    57.             }
    58.  
    59.             if (ARRaycastObject == null)
    60.             {
    61.                 ARRaycastObject = Instantiate(ARPrefab, hitPose.position, hitPose.rotation);
    62.             }
    63.             else
    64.             {
    65.                 ARRaycastObject.transform.position = hitPose.position;
    66.             }
    67.         }
    68.     }
    69.  
    70.     void placeSphereAtPhysical(Vector2 position)
    71.     {
    72.         Ray ray = ARCamera.ScreenPointToRay(position);
    73.         RaycastHit hit;
    74.  
    75.         if (Physics.Raycast(ray, out hit, float.MaxValue, layerMask))//5 meter distance
    76.         {
    77.             if (physicalRaycastObject == null)
    78.             {
    79.                 physicalRaycastObject = Instantiate(physicsPrefab, hit.point, Quaternion.identity);
    80.             }
    81.             else
    82.             {
    83.                 physicalRaycastObject.transform.position = hit.point;
    84.             }
    85.         }
    86.     }
    87. }
     
  2. DoAugMenRo

    DoAugMenRo

    Joined:
    Sep 17, 2019
    Posts:
    18
    The issue also arises also when using the following code of the ARRaycastManager:
    Code (CSharp):
    1.  
    2. Ray ray = ARCamera.ScreenPointToRay(position);
    3.         //Ray ray = new Ray(ARCamera.ScreenToWorldPoint(position), ARCamera.transform.forward);
    4.         if (m_RaycastManager.Raycast(ray, s_Hits, TrackableType.PlaneWithinPolygon))
    5.         {
    6.             var hitPose = s_Hits[0].pose;
    7.  
    8.             if (ARRaycastObject == null)
    9.             {
    10.                 ARRaycastObject = Instantiate(ARPrefab, hitPose.position, hitPose.rotation);
    11.             }
    12.             else
    13.             {
    14.                 ARRaycastObject.transform.position = hitPose.position;
    15.             }
    16.         }
    But i still can not find a solution. Any help is very welcome!
     
  3. mfuad

    mfuad

    Unity Technologies

    Joined:
    Jun 12, 2018
    Posts:
    335
    Hi @DoAugMenRo, put the raycast logic in an Application.onBeforeRender callback.

    The reason for this is the camera's transform doesn't get updated until later in the frame, so raycasts based on the camera's position in an Update callback will be a frame behind.
     
    DoAugMenRo likes this.
  4. DoAugMenRo

    DoAugMenRo

    Joined:
    Sep 17, 2019
    Posts:
    18
    Thank you @mfuad for your suggestion!

    Sadly the issue seems to be not fixed by using Application.onBeforeRender. Did i miss something? Is Application.onBeforeRender triggered at the same time as LateUpdate() and if so what are the differences?

    Here is my code:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.XR.ARFoundation;
    4. using UnityEngine.XR.ARSubsystems;
    5.  
    6.  
    7. [RequireComponent(typeof(ARRaycastManager))]
    8. [RequireComponent(typeof(ARPlaneManager))]
    9.  
    10. public class RaycastComparison2 : MonoBehaviour
    11. {
    12.     ARRaycastManager m_RaycastManager;
    13.     public Camera ARCamera;
    14.     public GameObject ARPrefab;
    15.  
    16.     private Vector2 screenCenter;
    17.     private GameObject ARRaycastObject;
    18.  
    19.     static List<ARRaycastHit> s_Hits = new List<ARRaycastHit>();
    20.  
    21.  
    22.  
    23.     void Awake()
    24.     {
    25.         m_RaycastManager = GetComponent<ARRaycastManager>();
    26.         screenCenter = new Vector2(Screen.currentResolution.width / 2, Screen.currentResolution.height / 2);
    27.      
    28.         Application.onBeforeRender+= placeSphereAt;
    29.     }
    30.  
    31.  
    32.     void placeSphereAt()
    33.     {
    34.         Ray ray = ARCamera.ScreenPointToRay(screenCenter);
    35.         if (m_RaycastManager.Raycast(ray, s_Hits, TrackableType.PlaneWithinPolygon))
    36.         {
    37.             var hitPose = s_Hits[0].pose;
    38.  
    39.             if (ARRaycastObject == null)
    40.             {
    41.                 ARRaycastObject = Instantiate(ARPrefab, hitPose.position, hitPose.rotation);
    42.             }
    43.             else
    44.             {
    45.                 ARRaycastObject.transform.position = hitPose.position;
    46.             }
    47.         }
    48.     }
    49. }
    50.  
     
  5. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,145
    @DoAugMenRo try moving the Physics.Raycast() into FixedUpdate(). Physics in Unity is updated on the fixed interval and by default, is updated less frequently than game logic. Of course, you can decrease Time.fixedTimeStep, but this may impact the performance.
     
  6. DoAugMenRo

    DoAugMenRo

    Joined:
    Sep 17, 2019
    Posts:
    18
    I also tried:
    yield return new WaitForEndOfFrame()
    Which also did not solve the issue. Further suggestions are very welcome!

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnityEngine.XR.ARFoundation;
    5. using UnityEngine.XR.ARSubsystems;
    6.  
    7.  
    8. [RequireComponent(typeof(ARRaycastManager))]
    9. [RequireComponent(typeof(ARPlaneManager))]
    10.  
    11. public class RaycastComparison3 : MonoBehaviour
    12. {
    13.     ARRaycastManager m_RaycastManager;
    14.     public Camera ARCamera;
    15.     public GameObject ARPrefab;
    16.  
    17.     private Vector2 screenCenter;
    18.     private GameObject ARRaycastObject;
    19.  
    20.     bool running = false;
    21.  
    22.     static List<ARRaycastHit> s_Hits = new List<ARRaycastHit>();
    23.  
    24.     void Awake()
    25.     {
    26.         m_RaycastManager = GetComponent<ARRaycastManager>();
    27.         screenCenter = new Vector2(Screen.currentResolution.width / 2, Screen.currentResolution.height / 2);
    28.     }
    29.  
    30.     void Update()
    31.     {
    32.         StartCoroutine(placeSphereAtEndOfFrame());
    33.     }
    34.  
    35.     IEnumerator placeSphereAtEndOfFrame()
    36.     {
    37.         yield return new WaitForEndOfFrame();
    38.         placeSphere();
    39.     }
    40.  
    41.     void placeSphere()
    42.     {
    43.         Ray ray = ARCamera.ScreenPointToRay(screenCenter);
    44.         if (m_RaycastManager.Raycast(ray, s_Hits, TrackableType.PlaneWithinPolygon))
    45.         {
    46.             var hitPose = s_Hits[0].pose;
    47.  
    48.             if (ARRaycastObject == null)
    49.             {
    50.                 ARRaycastObject = Instantiate(ARPrefab, hitPose.position, hitPose.rotation);
    51.             }
    52.             else
    53.             {
    54.                 ARRaycastObject.transform.position = hitPose.position;
    55.             }
    56.         }
    57.     }
    58. }
    59.  
     
  7. DoAugMenRo

    DoAugMenRo

    Joined:
    Sep 17, 2019
    Posts:
    18
    Thank you for your reply @KirillKuzyk, i am a big fan of your ARF Remote Editor!

    I tested FixedUpdate() and changing the the values of Time.fixedTimeStep for the physics raycasts. But it didnt help.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.XR.ARFoundation;
    4. using UnityEngine.XR.ARSubsystems;
    5.  
    6.  
    7. [RequireComponent(typeof(ARRaycastManager))]
    8. [RequireComponent(typeof(ARPlaneManager))]
    9. public class RaycastComparison1 : MonoBehaviour
    10. {
    11.     ARRaycastManager m_RaycastManager;
    12.     public Camera ARCamera;
    13.  
    14.  
    15.     public float heightOffset = 0.0f;
    16.     public int layerMask;
    17.  
    18.     public GameObject physicsPrefab;
    19.     public GameObject physicalPlane;
    20.  
    21.     private GameObject spawnedPlane;
    22.     private Vector2 screenCenter;
    23.  
    24.     private GameObject physicalRaycastObject;
    25.  
    26.     static List<ARRaycastHit> s_Hits = new List<ARRaycastHit>();
    27.  
    28.  
    29.  
    30.  
    31.  
    32.     void Awake()
    33.     {
    34.         m_RaycastManager = GetComponent<ARRaycastManager>();
    35.         layerMask = 1 << 10;
    36.         screenCenter = new Vector2(Screen.currentResolution.width / 2, Screen.currentResolution.height / 2);
    37.     }
    38.  
    39.  
    40.     void FixedUpdate()
    41.     {
    42.         placeSphereAtPhysical();
    43.     }
    44.  
    45.     private void Update()
    46.     {
    47.         placePhysicsPlane();
    48.     }
    49.  
    50.     void placePhysicsPlane()
    51.     {
    52.         if (m_RaycastManager.Raycast(screenCenter, s_Hits, TrackableType.PlaneWithinPolygon))
    53.         {
    54.             var hitPose = s_Hits[0].pose;
    55.  
    56.             if (spawnedPlane == null)
    57.             {
    58.                 Vector3 planePosition = hitPose.position;
    59.                 planePosition.y += heightOffset;
    60.                 spawnedPlane = Instantiate(physicalPlane, planePosition, hitPose.rotation);
    61.             }
    62.         }
    63.     }
    64.  
    65.  
    66.  
    67.     void placeSphereAtPhysical()
    68.     {
    69.         Ray ray = ARCamera.ScreenPointToRay(screenCenter);
    70.         RaycastHit hit;
    71.  
    72.         if (Physics.Raycast(ray, out hit, float.MaxValue, layerMask))
    73.         {
    74.             if (physicalRaycastObject == null)
    75.             {
    76.                 physicalRaycastObject = Instantiate(physicsPrefab, hit.point, Quaternion.identity);
    77.             }
    78.             else
    79.             {
    80.                 physicalRaycastObject.transform.position = hit.point;
    81.             }
    82.         }
    83.     }
    84. }
    85.  
    The problem seems to be somewhere else. The delay is also part of m_RaycastManager.Raycast if i calculate the ray
    Ray ray = ARCamera.ScreenPointToRay(screenCenter);
    As @mfuad was saying it might be due to the Camera transform updating later in the frame. But up to now i also had no luck with that.
     
    KyryloKuzyk likes this.
  8. DoAugMenRo

    DoAugMenRo

    Joined:
    Sep 17, 2019
    Posts:
    18
    In my case the problem was solved by using: RenderPipelineManager.beginCameraRendering, which is required due to me using the URP. Even though the camera transform was actually updated before Application.onBeforeRender, this did not solve the issue.

    If anyone could also explain to me why Application.onBeforeRender was not helping i would appreciate the insight.

    Best regards