0
\$\begingroup\$

I'm confused about ScreenPointToRay

Definition of this method says "Returns a ray from camera through a screen point". I'm trying to visualize it to understand how it works. But its not giving result I expected.

Those are code :

 public GameObject Cube;

private void FixedUpdate()
{

    Ray ray2 = Camera.main.ScreenPointToRay(Cube.transform.position);
    Debug.DrawLine(ray2.origin, ray2.origin +  ray2.direction * 250, Color.red);

    
}

That is screenshot This is screenshot

There are few things that blows my mind.

1)According to definition of this method, it should draw a line from camera through Vector3. But when I give a position of Cube as you see below. It's not drawing a line from camera through position of cube. Its drawing a line through a silly position as you see(the red ray). Why is that ? Why it's not drawing a line from camera through Cube ? 2) When I give Input.mousePosition as parameter it works correctly. What I mean is function is drawing a line from camera through my mouse cursor. It's working properly. Why is that ?

  1. The last thing that blows my mind is that how this function works when my mouse cursor out of game window sceen ? How can Unity draw a line when my mouse is out of the boundary of game sceen ? What is reference for Unity ? How does Unity know where I point with my cursor even if I'm not in game scene ?
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

But when I give a position of Cube as you see below. It's not drawing a line from camera through position of cube.

That's a position in the world. That means it gives the number of meters east, up, and north, measured from the world origin point.

The function name tells you it works with positions on the screen. That means it expects a number of pixels right and up, measured from the bottom-left corner of the game window.

If you want to convert a point in the world to a point on the screen, you can use Camera.WorldToScreenPoint.

When I give Input.mousePosition as parameter it works correctly.

Input.mousePosition is a position on the screen. So it's already in the correct coordinate system for Camera.ScreenPointToRay to work with.

How can Unity draw a line when my mouse is out of the boundary of game screen?

Because the projection math it's using does not care where the border of the window is. If the cursor is some number of pixels right and some number of pixels up from the center of the window, that corresponds to a ray with some particular yaw and pitch angle relative to the camera's facing direction. If those angles happen to be outside the camera's field of view, so be it. Every point on the infinite plane formed by extending your screen without limit in all directions still maps to a valid direction in the world, whether or not you can see that direction it in the rendered output.

In code it would be implemented something a bit like this (assuming a symmetric view frustum):

Ray ScreenToWorldPoint(Vector3 screenPos) {
     // Remap so (0, 0) is the center of the window,
     // and the edges are at -0.5 and +0.5.
     Vector2 relative = new Vector2(
          screenPos.x / Screen.width - 0.5f,
          screenPos.y / Screen.height - 0.5f
     );

     if (!orthographic) {
          // Angle in radians from the view axis
          // to the top plane of the view pyramid.
          float verticalAngle = 0.5f * Mathf.Deg2Rad * fieldOfView;

          // World space height of the view pyramid
          // measured at 1 m depth from the camera.
          float worldHeight = 2f * Mathf.Tan(verticalAngle);

          // Convert relative position to world units.
          Vector3 worldUnits = relative * worldHeight;
          worldUnits.x *= aspect;
          worldUnits.z = 1;

          // Rotate to match camera orientation.
          Vector3 direction = transform.rotation * worldUnits;

          // Output a ray from camera position, along this direction.
          return new Ray(transform.position, direction);
     } else {
          // Scale using half-height of camera.
          Vector3 worldUnits = relative * orthographicSize * 2f;
          worldUnits.x *= aspect;

          // Orient and position to match camera transform.
          Vector3 origin = transform.rotation * worldUnits;
          origin += transform.position;
          
          // Output a ray from this point, along camera's axis.
          return new Ray(origin, transform.forward);
     }
}
\$\endgroup\$
3
  • \$\begingroup\$ Thanks. I have understood almost everything. But there are some parts I can't imagine 1)If the function expect a number of pixels right and up, what does it do when I give a world position ? (like a cube position that I gave) Why doesn't ıt give an error ? Even if I change the position of the cube, ray direction doesn't change. Why is that ? \$\endgroup\$
    – Apoleytaa
    Jul 9, 2021 at 18:15
  • \$\begingroup\$ Here are some numbers: (3, 7, 0). Can you tell me whether these are worldspace coordinates in meters, or screenspace coordinates in pixels? If not, how would the computer know in order to generate the error? It has no additional metadata to look at, just some numbers. The reason the ray direction doesn't (visibly) change is that you have a reasonably high rendering resolution — that means many pixels per degree of view. So you would need to move your cube by dozens or hundreds of worldspace units to change the direction by a noticeable angle. \$\endgroup\$
    – DMGregory
    Jul 9, 2021 at 18:35
  • \$\begingroup\$ Thanks for explanation. It really helped a lot. It's really hard to imagine if you are not fan of 3D geometry. \$\endgroup\$
    – Apoleytaa
    Jul 9, 2021 at 18:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .