Search Unity

Help Reproducing Screen To World Conversion

Discussion in 'General Graphics' started by jdtec, Apr 14, 2019.

  1. jdtec

    jdtec

    Joined:
    Oct 25, 2017
    Posts:
    302
    Hi, I'm trying to recreate the ScreenPointToRay function in code using the camera MVP matrices.

    What am I doing wrong below that is leading to different results compared with the built-in camera function?

    Code (CSharp):
    1.  
    2.      
    3.       var model       = camera.transform.localToWorldMatrix;
    4.       var view        = camera.worldToCameraMatrix;
    5.       var projection  = camera.projectionMatrix;
    6.  
    7.       Matrix4x4 MVP = projection*view*model;
    8.  
    9.       float4 mousePos_clipSpace = new float4(
    10.                                     ((mouseX * 2.0f) / Screen.width) - 1.0f,
    11.                                     (1.0f - (2.0f * mouseY) / Screen.height),
    12.                                     0.0f,
    13.                                     1.0f);
    14.  
    15.       float4 wspace = MVP.inverse * mousePos_clipSpace;
    16.       wspace /= wspace.w;
    17.      
    18.       // Why don't these match?
    19.  
    20.       float3 ray_Wrong = math.normalize (new float3 (wspace.x, wspace.y, wspace.z));
    21.  
    22.       Ray startCameraRay = camera.ScreenPointToRay (new Vector3 (mouseX, mouseY, 0.0f));
    23.       float3 ray_Correct = startCameraRay.direction;
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Don't use the "model" matrix, it's meaningless here as the camera doesn't really have one. Using the transform.localToWorldMatrix for anything rendering related is actually wrong as it does not necessarily match the model space used for rendering, but it's doubly wrong here. You're trying to get a ray in world space, and the camera's cameraToWorldMatrix (or the inverse of the worldToCameraMatrix like you're using) is all that's needed for getting a camera space ray into world space.
     
    jdtec likes this.
  3. jdtec

    jdtec

    Joined:
    Oct 25, 2017
    Posts:
    302
    Thanks for replying, @bgolus . In the end I got it working with the following code should it interest anyone. If there's a better/quicker way let me know.

    fyi this was handy so that I could call it from a bursted job using the new DOTs packages.

    Code (CSharp):
    1. public static float3 ScreenToWorldRay (float2 screenPosition,
    2.                                          float screenWidth,
    3.                                          float screenHeight,
    4.                                          float4x4 cameraToWorld,
    5.                                          float4x4 cameraProjectionInverse,
    6.                                          float3 cameraOrigin)
    7.   {
    8.     screenPosition = new float2 (screenPosition.x, screenHeight - screenPosition.y);
    9.  
    10.     Vector4 clipSpace = new Vector4 (((screenPosition.x * 2.0f) / screenWidth) - 1.0f, (1.0f - (2.0f * screenPosition.y) / screenHeight), 0.0f, 1.0f);
    11.  
    12.     float4 viewSpace = math.mul (cameraProjectionInverse, clipSpace);
    13.     viewSpace /= viewSpace.w;
    14.    
    15.     float4 worldSpace = math.mul (cameraToWorld, viewSpace);
    16.  
    17.     float3 worldDirection = math.normalize (worldSpace.xyz - cameraOrigin);
    18.    
    19.     return worldDirection;
    20.   }
     
    PrimalCoder likes this.