Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Raycasting from Camera to a Sphere Collider attached to Camera fails to detect RaycastHits.

Discussion in 'Scripting' started by asperatology, Nov 19, 2015.

  1. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Here's a GIF of me playing around with the main camera. I then started to notice something weird on how RaycastHit gets the hit points.

    The main camera has a sphere collider attached to it, and I have a script attached to the main camera where it would shoot 4 rays and raycast them from the main camera to the sphere collider, which is the first thing the raycasts first hit.



    If the main camera is pointing towards the horizon, or is pointing up, funky world coordinates start to show up. World coordinates are pointed out in the second GIF below:



    EDIT:

    Could someone explain to me why the world coordinates are funky sometimes? Funky, as in, sometimes, the world coordinates would be all zeroes. Or the bottom right would be the same as the top right. They are not consistent sometimes.
     

    Attached Files:

    • Bug.zip
      File size:
      259.2 KB
      Views:
      354
    Last edited: Nov 19, 2015
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,808
    Are the zeroes happening when the camera points at the sky and the raycast hits nothing?
     
  3. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Yes. But this is where it gets confusing.

    The Raycasts should be always hitting the sphere collider, because the main camera is completely inside the sphere collider. It should at least hit that collider instead of endlessly going into the sky.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,808
    The raycast against colliders are actually sensitive to direction: the sphere collider faces outward, therefore raycasts originating inside it will not collide with it.

    Similarly if you had a plane (mesh collider) facing one way, if a ray went through it in that same direction as its normal, it would not collide.
     
  5. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Oh, this is something unexpected. Never knew about this.

    How do you "flip" or "inverse" the sphere collider, so that the raycast will hit the sphere collider? Or, is it because the normal of the raycast is equal to the ray that the Raycast() is casting, therefore it could not detect any "collisions" or "hits"?

    And I thought Raycast() could just give me where the rays would stop at if it hits a collider, and gives me a RaycastHit....

    I guess I would need help on finding out where a raycast stops at in the world scene if it touches something (colliders, objects, etc.). Does anyone know? Thanks.

    EDIT:

    One way I could probably use is to switch out the sphere collider with a cube collider. But then again, Raycast() is sensitive to directions, and shooting a raycast from inside a collider would be null in many cases. (Null means, I wouldn't be able to get any RaycastHit info, or any info on where the raycast ends at in the world scene.)
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,808
    If the normal of the collider at the point where your ray hits it is facing in the same hemisphere as the direction of the ray, then it won't collide. That goes for all colliders, both mesh-based ones and coordinate/computational based ones like spheres: colliders are sensitive to rays only impinging upon their "outward" surface.

    If you expect the sphere to always be there and know its size and want to see where a ray from your camera touches it, you can simply compute that point of contact based on the unit vector in the direction of the ray multiplied by the radius of the sphere and skip the raycast entirely.
     
  7. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    For unit vector calculations, the only problem I would have is how to calculate the precise locations of the 4 corners of the camera's viewport, aka, the player's screen coordinates at viewport (0f, 0f), (1f, 0f), (0f, 1f), and (1f, 1f)? Or rather, the precise angles that I need to rotate the unit vectors, so the 4 unit vectors are aligned with the camera's 4 corners if the unit vectors were extended by the length of the sphere collider's radius.

    Raycasting from the camera is easier, because you can just obtain the viewport's coordinates, and the camera's matrix projection handles calculating the angles. And I'm not familar with obtaining the matrix projection from the camera as the camera rotates on the fly...
     
  8. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    EDIT:

    Ray.direction seems to be what I may be looking for, to use as unit vectors. Am I right? It's really hard to tell when I'm experimenting it, because I don't have anything else to compare for accuracy.
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,808
    How are you raycasting in the first place? Do you know about the camera helpers that let you raycast from any arbitrary point on your view plane?

    This is the function:

    http://docs.unity3d.com/ScriptReference/Camera.ViewportPointToRay.html

    Lower left is from (0,0) and upper right is from (1,1).

    I think that ray will start from your near clipping plane, NOT from the camera's location. You can simply offset it.
     
  10. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    This is what I used for raycasting. CameraView class is the only thing I changed. I cannot get any trapezoid shape in the minimap as I angle my main camera. Maybe it's because the corners just so happens to be a rectangle....



    Code (CSharp):
    1. //CameraView - This is where I used raycasting.
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class CameraView : MonoBehaviour {
    7.     public Collider sphereCollider;
    8.     public Vector3 bottomLeft;
    9.     public Vector3 bottomRight;
    10.     public Vector3 topLeft;
    11.     public Vector3 topRight;
    12.  
    13.     public void Update() {
    14.         Ray bottomLeftRay = Camera.main.ViewportPointToRay(new Vector3(0f, 0f));
    15.         Ray bottomRightRay = Camera.main.ViewportPointToRay(new Vector3(1f, 0f));
    16.         Ray topLeftRay = Camera.main.ViewportPointToRay(new Vector3(0f, 1f));
    17.         Ray topRightRay = Camera.main.ViewportPointToRay(new Vector3(1f, 1f));
    18.  
    19.         float radius = 30f;
    20.         this.bottomLeft = bottomLeftRay.direction.normalized * radius;
    21.         this.bottomRight = bottomRightRay.direction.normalized * radius;
    22.         this.topLeft = topLeftRay.direction.normalized * radius;
    23.         this.topRight = topRightRay.direction.normalized * radius;
    24.     }
    25. }
    26.  
    27.  
    Code (CSharp):
    1. //DrawLines - This is where I used the obtained RaycastHit hit points for rendering lines in the minimap camera.
    2.  
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6.  
    7. public class DrawLines : MonoBehaviour {
    8.     public CameraView mainCameraView;
    9.     public Camera orthographicCamera;
    10.     public Vector3 topLeft, topRight, bottomLeft, bottomRight;
    11.  
    12.     public void Start() {
    13.         if (this.orthographicCamera == null) {
    14.             GameObject obj = GameObject.FindGameObjectWithTag("Orthographic");
    15.             if (obj != null) {
    16.                 this.orthographicCamera = obj.GetComponent<Camera>();
    17.             }
    18.         }
    19.         if (this.mainCameraView == null) {
    20.             GameObject obj = GameObject.FindGameObjectWithTag("MainCamera");
    21.             if (obj != null) {
    22.                 this.mainCameraView = obj.GetComponent<CameraView>();
    23.             }
    24.         }
    25.     }
    26.  
    27.     public void Update() {
    28.         this.topLeft = this.orthographicCamera.WorldToViewportPoint(this.mainCameraView.topLeft);
    29.         this.topRight = this.orthographicCamera.WorldToViewportPoint(this.mainCameraView.topRight);
    30.         this.bottomRight = this.orthographicCamera.WorldToViewportPoint(this.mainCameraView.bottomRight);
    31.         this.bottomLeft = this.orthographicCamera.WorldToViewportPoint(this.mainCameraView.bottomLeft);
    32.  
    33.         this.topLeft.z = -1f;
    34.         this.topRight.z = -1f;
    35.         this.bottomLeft.z = -1f;
    36.         this.bottomRight.z = -1f;
    37.     }
    38.  
    39.     public void OnPostRender() {
    40.         GL.PushMatrix();
    41.         {
    42.             GL.LoadOrtho();
    43.             GL.Begin(GL.LINES);
    44.             {
    45.                 GL.Color(Color.red);
    46.                 GL.Vertex(this.topLeft);
    47.                 GL.Vertex(this.topRight);
    48.                 GL.Vertex(this.topRight);
    49.                 GL.Vertex(this.bottomRight);
    50.                 GL.Vertex(this.bottomRight);
    51.                 GL.Vertex(this.bottomLeft);
    52.                 GL.Vertex(this.bottomLeft);
    53.                 GL.Vertex(this.topLeft);
    54.             }
    55.             GL.End();
    56.         }
    57.         GL.PopMatrix();
    58.     }
    59. }
    60.  
     
    Last edited: Nov 21, 2015
  11. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    The other way would be to do this instead:



    Code (CSharp):
    1. //CameraView - The only thing changed.
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class CameraView : MonoBehaviour {
    7.     public Collider sphereCollider;
    8.     public Vector3 bottomLeft;
    9.     public Vector3 bottomRight;
    10.     public Vector3 topLeft;
    11.     public Vector3 topRight;
    12.  
    13.     public void Update() {
    14.         Ray bottomLeftRay = Camera.main.ViewportPointToRay(new Vector3(0f, 0f));
    15.         Ray bottomRightRay = Camera.main.ViewportPointToRay(new Vector3(1f, 0f));
    16.         Ray topLeftRay = Camera.main.ViewportPointToRay(new Vector3(0f, 1f));
    17.         Ray topRightRay = Camera.main.ViewportPointToRay(new Vector3(1f, 1f));
    18.  
    19.         //Radius is 3f.
    20.         float radius = 30f;
    21.         RaycastHit hit;
    22.         if (Physics.Raycast(bottomLeftRay, out hit)) {
    23.             this.bottomLeft = hit.point;
    24.         }
    25.         else {
    26.             this.bottomLeft = bottomLeftRay.direction.normalized * radius;
    27.         }
    28.  
    29.         if (Physics.Raycast(bottomRightRay, out hit)) {
    30.             this.bottomRight = hit.point;
    31.         }
    32.         else {
    33.             this.bottomRight = bottomRightRay.direction.normalized * radius;
    34.         }
    35.  
    36.         if (Physics.Raycast(topLeftRay, out hit)) {
    37.             this.topLeft = hit.point;
    38.         }
    39.         else {
    40.             this.topLeft = topLeftRay.direction.normalized * radius;
    41.         }
    42.  
    43.         if (Physics.Raycast(topRightRay, out hit)) {
    44.             this.topRight = hit.point;
    45.         }
    46.         else {
    47.             this.topRight = topRightRay.direction.normalized * radius;
    48.         }
    49.     }
    50. }
    51.  
     
  12. jovalent

    jovalent

    Joined:
    May 27, 2017
    Posts:
    5
    The solution for me was to take my ray, and invert it so it was pointing toward the camera instead of away from it.

    Code (csharp):
    1. var ray = Camera.main.ScreenPointToRay( inputPosition );
    2. ray.origin = ray.GetPoint( 20f ); // some value larger than the radius of the sphere
    3. ray.direction *= -1f; // invert the direction of the ray
    4. RaycastHit hit;
    5. if( !Physics.Raycast( ray, out hit, 20f, _layerMask ) ) return;