Can Shapes be applied in screen space?

Avatar
  • updated

Hello!


Loving Shapes - great stuff!

I'd like to use the Draw.Matrix functionality to set a world view projection matrix (allowing me to draw shapes in absolute screen space, i.e. based on mouse cursor position). However, I'm having trouble getting this working (although if I hand multiply the points into world space using my created transform it works fine).

Should I expect shapes to support these kinds of matrices, and if so, would you mind sharing up any sample code that has this working (both the generation of the matrix, and its application).

Many thanks,

Adam.

Reporting a bug? please specify Unity version:
2021.3.26f1
Reporting a bug? please specify Shapes version:
Reporting a bug? please specify Render Pipeline:
Built-in render pipeline
Avatar
Freya Holmér creator

It should work! Assuming you're also drawing the shapes in immediate mode. The tricky part is the projective depth, but as long as you don't need correct depth handling, but it should work fine to use the local to world matrix of the camera you want to draw in. However, mind that the near clip is usually beyond 0, so you might have to offset the matrix by the near clip distance, plus some margin to ensure the shapes are within the camera's frustum.

I do something similar in the experimental ImmediateModeCanvas.cs, which might help as a reference!

Avatar

Did anyone have success in adapting the ImmediateModeCanvas matrix math to screen-space? I tried giving it a go, but couldn't quite get it.

public static Matrix4x4 GetScreenToWorldMatrix(Camera camera)
{
   var planeDistance = (camera.nearClipPlane + camera.farClipPlane) * 0.5f;
   var transform = camera.transform;
   var forward = transform.forward;
   var origin = transform.TransformPoint(0f, 0f, planeDistance);
   var height = camera.pixelHeight;
   var scale = 1f;
   if (camera.orthographic) 
      scale = 2f * camera.orthographicSize / height;
   else
   {
      var vFovHalfRad = camera.fieldOfView * Mathf.Deg2Rad * 0.5f;
      var halfYSize = (float)(planeDistance * Mathf.Tan(vFovHalfRad));
      scale = (float)(2f * halfYSize / height );
   }
   var rightScale = transform.right * scale;
   var upScale = transform.up * scale;
   var frwScale = forward * scale;
   return new Matrix4x4(rightScale, upScale, frwScale, new Vector4(origin.x, origin.y, origin.z, 1));
}

...

// Pseudo code. Draws what you see in the gif below
Matrix = GetScreenToWorldMatrix(camera);
Rect(0, 0, Width, Height);
// Draw line from bottom left to top right
Line(10, 10, Width - 10, Height - 10)
// Draw circle in middle of screen
Circle(Size / 2, 10);

The bottom left of the screen is in the middle, and the thickness is very thin/affected by the orthographic size:

Image 818

Any pointers would be much appreciated!

Avatar
Freya Holmér creator

when you say "screen space", what coordinates are you referring to? UI coordinates, world coordinates, or pixel coordinates?

Avatar
Freya Holmér creator

if you mean pixels then all you're missing is

origin += rightScale * ( -pxWidth / 2f ) + upScale * ( -pxHeight / 2f );

Before the last line of GetScreenToWorldMatrix.


Also, be sure to use meters for all your immediate mode drawing units (which would mean pixels in a screen space context like this)

Avatar

Thanks, it works perfectly now 🙌