Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[Solved] [C#] Manually Convert World to Screen Position

Discussion in 'Scripting' started by twinnuke, Aug 16, 2016.

  1. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    I need to take this Function Camera.WorldToScreenPoint(Vector3 Position) and write it my self so I can use it in a separate thread. I'm calculating hundreds of these at once so it's important that I keep it off the main thread so it doesn't use tons of CPU power.

    I could do it in open GL converting it via Matrix but I'm not sure what to do in Unity.

    Anyone have any ideas?

    Solved.
    Code (CSharp):
    1.  /// <summary>
    2.     /// Updates The Target Icons Position (Threaded Version).
    3.     /// </summary>
    4.     void UpdateTarget(TargetUIInfo info)
    5.     {    
    6.         bool behindCamera = false;
    7.         // If target is pinned to a side of the screen.
    8.         bool horizontalPin = false;
    9.         bool verticalPin = false;
    10.         // Calculate Screen Point of Object.
    11.         Vector3 pointOnScreen = Vector3.zero;
    12.         Vector4 v = _ViewPort * new Vector4(info.targetPosition.x, info.targetPosition.y, info.targetPosition.z, 1);
    13.         Vector3 _ViewPortPoint = v / -v.w;
    14.         // Will keep _ViewPort Under 1 for multiplication.
    15.         Vector3 normalized_ViewPort = new Vector3(_ViewPortPoint.x + 1, _ViewPortPoint.y + 1, 0);
    16.         normalized_ViewPort /= 2;
    17.         // Get Vector (Distance essentially to use in Dot Product).
    18.         Vector3 heading = info.targetPosition - _CameraPosition;
    19.         // Check is object is behind camera.
    20.         behindCamera = (Vector3.Dot(_CameraForward, heading) < 0) ? true : false;
    21.         if (behindCamera)
    22.         {
    23.             // Calculate Inverse Transform Point to determine where target is located behind camera.
    24.             Vector3 inverseTransPoint = Quaternion.Inverse(_CameraRotation) * (info.targetPosition - _CameraPosition).normalized;
    25.             // Positive Right / Negative Left
    26.             if (inverseTransPoint.x > 0)
    27.             {
    28.                 pointOnScreen.x = Screen.width;
    29.                 horizontalPin = true;
    30.             }
    31.             // Positive Top / Negative Bottom
    32.             if (inverseTransPoint.y < 0)
    33.             {
    34.                 pointOnScreen.y = Screen.height;
    35.                 verticalPin = true;
    36.             }
    37.             // Position target positions to side of screen if inverseTransPoint indicated pinning.
    38.             if (!verticalPin)
    39.                 pointOnScreen.y = normalized_ViewPort.y * Screen.height;
    40.             if (!horizontalPin)
    41.                 pointOnScreen.x = normalized_ViewPort.x * Screen.width;
    42.             // Special Case if both are 0/0 and target is behind.
    43.             if (!verticalPin && !horizontalPin)
    44.             {
    45.                 pointOnScreen = info.targetPosition;
    46.             }
    47.         }
    48.         else
    49.         {
    50.             // Regular Positioning of Target Icon
    51.             pointOnScreen.x = normalized_ViewPort.x * Screen.width;
    52.             pointOnScreen.y = normalized_ViewPort.y * Screen.height;
    53.         }
    54.         // NGUI Required (Will not be neccesary when Moved over to Unity UI).
    55.         pointOnScreen *= ratio.y;
    56.         // Set position of UI Overlay (via Cache as transform cannot be accessed in Thread).
    57.         info.updatedPosition = pointOnScreen;      
    58.     }
     
    Last edited: Aug 19, 2016
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,519
    What thread you do it in will not affect how much "CPU power" it uses.

    The trick with performant game engineering is to find out what you DON'T have to calculate every frame.

    And in any case, don't optimize until you see an actual performance problem. And if you see a performance problem, the first thing you must do is MEASURE where it is coming from (see the Profiler) before making random changes to your codebase.
     
    look001 likes this.
  3. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    Nice Sentiment but I've actually optimized it already but I still want it off thread because it's not super important but it's important to keep up to date.

    If you're wondering what it is, it's target reticles for potentially hundreds of (filterable) targets. And depending on how fast you are moving / turning they can move pretty quickly so I need them updated every frame.

    Also they may come in and out of existence and are not sorted so trying to frame split the calculations can cause targets to not be updated properly.

    Keeping them off thread allows a continuous update without actually using cpu cycles from the main thread.

    And yes it definitely affects how much CPU power it uses, especially on systems with multiple cores.

    I have multi-threaded tons of things and noticed massive performance improvements, need examples?
     
    roxanne-thegamebakers likes this.
  4. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    How about making them follow world position of their objects, drawn last (on top) with scale and rotation controlled (or possibly in vertex shader)

    On topic: this should get you viewport coordinates (tho my matrix math is a bit rusty)
    Code (CSharp):
    1.         Matrix4x4 MVP = cam.projectionMatrix * cam.transform.localToWorldMatrix.inverse * objectTransform.localToWorldMatrix;
    2.         Vector4 v = MVP * new Vector4( 0, 0, 0, 1 );
    3.         Vector3 ViewportPoint = v / -v.w;
    4.  
     
  5. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    It occurs to me this morning that you probably just want to give a world position, instead of an object transform, inwhich case you can simplify a little to

    Code (CSharp):
    1. Matrix4x4 VP = cam.projectionMatrix * cam.transform.worldToLocalMatrix;
    2. Vector4 v = VP * new Vector4( worldPos.x, worldPos.y, worldPos.z, 1 );
    3. Vector3 ViewportPoint = v / -v.w;
    4.  
     
  6. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47

    I was actually looking into those matrices earlier but got side tracked. Thank you for your input I will implement later.

    The problem is they act as buttons and having them off In the distance may not work. So the ui scaling in the distance would probably not work as ray cast from mouse point is finicky.
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,519
    Let me make sure I understand your problem.

    - you have a large block of Vector3 data, probably originating in transform.position fields on your GameObjects
    - every frame you want to run this data through a world-to-screen transform so you have an up-to-date screen coordinate for each worldspace Vector3

    Option 1: (which I would choose)

    - just pump it all through the function on your main thread and use the results

    Option 2: (which you propose)

    - create a worker thread to do the work, and give it a shared area of memory to use

    Now every frame you will:

    - reach a point when you have updated all of your object positions
    - synchronize with your worker thread (i.e., stop the main thread and wait) to make sure it is "Ready" to receive data
    - marshall (copy) all the Vector3 data from main thread memory (GameObjects, etc.) over to the shared memory area
    - signal the worker thread that it may begin processing

    Now you will go about the rest of your frame, until you need the converted data back from your worker thread.

    - now you will block to verify that the worker thread has indeed finished processing everything for this frame
    - when it is finished, you will marshall (copy) the data back from shared memory and into every place that it was originally.

    Now you can go ahead and do things with the resultant computations.

    Am I missing something?
     
    KelsoMRK likes this.
  8. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    I actually dont care whether its ready.

    i start the worker thread,
    quickly copy the entire array
    compute all positions
    send a vector2 back because 3 is pointless in 2d space

    (main thread updates based on position change)

    so it wont be every frame and it will be on a spread out core computation wise.

    I need the main thread for AI as there are hundreds of them if not thousands of pieces.
    I have it option 1 atm, it claims way too much main thread time.

    Edit: Matrix Calculations are extremely expensive.
     
    Last edited: Aug 16, 2016
  9. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    My information may be wildly out of date, but I don't think Unity utilises more than one core.

    EDIT: You say you can't frame split the points, but have you thought about some sort of space partitioning? That way, you would be able to prune a bunch of points before calculating anything.

    DOUBLE EDIT: Whatever you choose to do, I'd probably start out by implementing a working, naive solution first, so you can compare. Do the matrix transforms first, IMO.
     
    Last edited: Aug 17, 2016
  10. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Came up with a way that doesnt use matrices

    Projects the world position onto the cameras near clip plane, then converts to vieport space

    The 'intersection' veriaable is the world position on the projected point, if you want to use a world space UI, then you could stop at that point
    Code (CSharp):
    1.     public Transform objectTransform;
    2.     public Camera cam;
    3.     public RectTransform image;
    4.     public RectTransform canvas;
    5.  
    6.  
    7.         Vector3 camPos = cam.transform.position;
    8.         Vector3 camDir = cam.transform.forward;
    9.         Vector3 camNearClipPoint = camPos + camDir * cam.nearClipPlane;
    10.  
    11.         float nearPlaneHeight = ( Mathf.Tan( cam.fieldOfView * 0.5f * Mathf.Deg2Rad ) * cam.nearClipPlane );
    12.         float nearPlaneWidth = nearPlaneHeight * cam.aspect;
    13.  
    14.         Vector3 worldPos = objectTransform.position;
    15.         Vector3 dir = ( camPos - worldPos ).normalized;
    16.  
    17.  
    18.         float dotNumerator = Vector3.Dot( ( camNearClipPoint - worldPos ), camDir );
    19.         float dotDenominator = Vector3.Dot( dir, camDir );
    20.         float length = dotNumerator / dotDenominator;
    21.         Vector3 intersection = worldPos + dir * length;
    22.  
    23.  
    24.         Vector3 intersectionDir = Quaternion.Inverse( cam.transform.rotation ) * (intersection - camNearClipPoint);
    25.         Vector3 viewPortPos = new Vector3( intersectionDir.x / nearPlaneWidth, intersectionDir.y / nearPlaneHeight, 0 );
    26.  
    27.         image.localPosition = new Vector3( viewPortPos.x * canvas.rect.width * 0.5f, viewPortPos.y * canvas.rect.height * 0.5f, 0 );
    28.  
    29.  
    30.  
    31.     }
    32.  
     
  11. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    I don't know, man. I think you can do all that in a single matrix multiplication.

    EDIT: If the problem is not being able to use Unity's API in your other thread, I would write my own matrix multiplication algorithm, since you can get the projection matrix from the camera(Camera.worldToCameraMatrix). Write your own basic Matrix class, if you can't use Unity's.
     
    Last edited: Aug 17, 2016
  12. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    Thanks for all the input guys, Already implemented. Saved about 9 FPS when calculating > 100 targets at once.

    Unity Does utilize more than one core, they even say it in the patch notes. It just doesn't have native threading through scripts so you have to manually use thread pools or threads.

    So you cannot access things like GameObject / Transform / Camera Matricies.
    So you cache them and then call them in the threads obviously checking for their existence first as threads don't throw error messages in the console.
     
  13. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Cool. Mind showing us how you did it?
     
  14. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47

    So it turns out I need to figure out the formula for InverseTransformPoint also. Unfortunately I am not well versed in Matrices.
     
  15. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    Sure once I solve the issue of getting the direction when the object is behind. Which I need to solve InverseTransformPoint for. The code is currently very dirty and I'd like to show off the clean version. It's also using NGUI from back when I was experimenting with this project so it's a bit odd in some parts.

    Edit: Solved it. Simple
    Code (CSharp):
    1. Quaternion.Inverse(Camera Rotation) * (Target Position - Camera Position));
     
    Last edited: Aug 18, 2016
  16. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    I assume you know this, but in case someone reads this thread and doesn't.

    The transformation/projection matrix is just a single matrix you can multiply a vector by, that both translates(moves), rotates, scales and shears. So, if you have a point in 3D space, and you multiply it by the cameras projection matrix, you get the points position on the screen. The neat thing here, is that you multiply all the points by the same matrix.

    All transforms have localToWorldMatrix and worldToLocalMatrix, and I bet if you decompile the transform functions, they just multiply be these. If you don't have one of them, you can always get the inverse from Matrix(it has a variable .inverse).

    EDIT: I just realized hpjohn mentioned most of this. In any case, I'd advise anyone looking to manipulating points and vectors in 3D to get a basic understanding of transformation matrices.

    This is probably a good article.
     
    Last edited: Aug 18, 2016
  17. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    Yeah I'm beginning to understand these things. Unity usually does the work for you but being on another thread doesn't allow me to access.
     
  18. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    You could make your own matrix class, just 4x4 array, and write your own matrix multiplaction method. It's pretty simple. Finding the inverse is much more complicated, so if possible, I'd send both matrices to your thread.

    If you don't want to bother with it, you could also just use a library.
     
  19. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    I'm all set actually. I figured it all out, it works just like it did in the main thread (conceptually) I just have to test it out full scale again tomorrow. Then I'll post the code.
     
  20. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Looking forward to it :)
     
  21. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    Here it is.
    Code (CSharp):
    1.     /// <summary>
    2.     /// Updates The Target Icons Position (Threaded Version).
    3.     /// </summary>
    4.     void UpdateTarget(TargetUIInfo info)
    5.     {      
    6.  
    7.         bool behindCamera = false;
    8.  
    9.         // If target is pinned to a side of the screen.
    10.         bool horizontalPin = false;
    11.         bool verticalPin = false;
    12.  
    13.         // Calculate Screen Point of Object.
    14.         Vector3 pointOnScreen = Vector3.zero;
    15.         Vector4 v = _ViewPort * new Vector4(info.targetPosition.x, info.targetPosition.y, info.targetPosition.z, 1);
    16.         Vector3 _ViewPortPoint = v / -v.w;
    17.  
    18.         // Will keep _ViewPort Under 1 for multiplication.
    19.         Vector3 normalized_ViewPort = new Vector3(_ViewPortPoint.x + 1, _ViewPortPoint.y + 1, 0);
    20.         normalized_ViewPort /= 2;
    21.  
    22.         // Get Vector (Distance essentially to use in Dot Product).
    23.         Vector3 heading = info.targetPosition - _CameraPosition;
    24.         // Check is object is behind camera.
    25.         behindCamera = (Vector3.Dot(_CameraForward, heading) < 0) ? true : false;
    26.  
    27.  
    28.         if (behindCamera)
    29.         {
    30.             // Calculate Inverse Transform Point to determine where target is located behind camera.
    31.             Vector3 inverseTransPoint = Quaternion.Inverse(_CameraRotation) * (info.targetPosition - _CameraPosition).normalized;
    32.  
    33.             // Positive Right / Negative Left
    34.             if (inverseTransPoint.x > 0)
    35.             {
    36.                 pointOnScreen.x = Screen.width;
    37.                 horizontalPin = true;
    38.             }
    39.             // Positive Top / Negative Bottom
    40.             if (inverseTransPoint.y < 0)
    41.             {
    42.                 pointOnScreen.y = Screen.height;
    43.                 verticalPin = true;
    44.             }
    45.  
    46.             // Position target positions to side of screen if inverseTransPoint indicated pinning.
    47.             if (!verticalPin)
    48.                 pointOnScreen.y = normalized_ViewPort.y * Screen.height;
    49.             if (!horizontalPin)
    50.                 pointOnScreen.x = normalized_ViewPort.x * Screen.width;
    51.  
    52.             // Special Case if both are 0/0 and target is behind.
    53.             if (!verticalPin && !horizontalPin)
    54.             {
    55.                 pointOnScreen = info.targetPosition;
    56.             }
    57.  
    58.         }
    59.         else
    60.         {
    61.             // Regular Positioning of Target Icon
    62.             pointOnScreen.x = normalized_ViewPort.x * Screen.width;
    63.             pointOnScreen.y = normalized_ViewPort.y * Screen.height;
    64.         }
    65.  
    66.         // NGUI Required (Will not be neccesary when Moved over to Unity UI).
    67.         pointOnScreen *= ratio.y;
    68.  
    69.         // Set position of UI Overlay (via Cache as transform cannot be accessed in Thread).
    70.         info.updatedPosition = pointOnScreen;        
    71.     }
     
  22. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    Confirming Steady 60FPS with over 166 targets with a 60 fps limit.
     
  23. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Looks cool. What's the performance difference if you don't cap the fps?
     
  24. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    What I've been doing.
     

    Attached Files:

  25. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    It brought me up from 53 to 60 so with cap about 7 (with about 100 targets). Without cap it really just depends on how many targets you are calculating. Obviously at low numbers it would be minimal. I have a pretty high end computer so it's hard to figure out how much it'd help on a slower one. But I'd imagine threading it would really give it a boost if you had a processor with a lower IPC count.
     
  26. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,519
    Something seems off here so I did a quick experiment.

    Without using any multi-threading, I am able to sling 830 cube objects through 3D space, get their screenspace coordinates back, and use those coordinates to operate an OnGUI() display system, before I dropped down to near (but solidly above) 60fps.

    To be fair, I have a LOT less going on light- and geometry-wise, but it is all main thread, and pumping text through OnGUI()'s immediate mode, plus I have shadows turned on.

    Are you sure threading is your performance bottleneck??

    My machine is a Mid 2015 MacBookPro retina 15" laptop.

    On an iOS iPod Touch 5th Generation (pretty much minimum spec iOS) I can get about 180 objects going at 30fps, no shadows.

    See enclosed screenshot and complete project package.
     

    Attached Files:

    image28 likes this.
  27. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    I have alot going on main thread. Taking one part of a full game and saying you can get 60 fps isn't a good benchmark.
     
  28. twinnuke

    twinnuke

    Joined:
    May 6, 2013
    Posts:
    47
    Keep in mind I sphere overlap for the target. Sort them and decide to render them based on priority. So there in the targeting system alone there is a lot going on.
     
  29. Knosis_Co-Verse

    Knosis_Co-Verse

    Joined:
    Apr 23, 2019
    Posts:
    12
    What would be the inverse of that? Get the world position given a screen position and distance?
     
  30. viruseg

    viruseg

    Joined:
    Jul 8, 2017
    Posts:
    23
    For those who come here through a search engine. Here is the code intended for the Burst compiler, which does the same thing as Camera.WorldToScreenPoint.

    Code (CSharp):
    1. var matrixVP4x4 = cam.projectionMatrix * cam.worldToCameraMatrix;
    2. var matrixVP = new float4x4(matrixVP4x4.m00, matrixVP4x4.m01, matrixVP4x4.m02, matrixVP4x4.m03,
    3.                             matrixVP4x4.m10, matrixVP4x4.m11, matrixVP4x4.m12, matrixVP4x4.m13,
    4.                             matrixVP4x4.m20, matrixVP4x4.m21, matrixVP4x4.m22, matrixVP4x4.m23,
    5.                             matrixVP4x4.m30, matrixVP4x4.m31, matrixVP4x4.m32, matrixVP4x4.m33);
    6. var camTargetSize = new float2(cam.pixelWidth, cam.pixelHeight);
    7.  
    8. var worldPoint = new float3(10, 10, 0);
    9. var screenPoint = WorldToScreenPoint(worldPoint, matrixVP, camTargetSize);
    10.  
    11. [BurstCompile]
    12. public static float2 WorldToScreenPoint(float3 worldPosition, float4x4 matrixVP, float2 camTargetSize)
    13. {
    14.     var pos = math.mul(matrixVP, new float4(worldPosition, 1.0f));
    15.     var o = pos * 0.5f;
    16.     o.xy += o.w;
    17.     o.zw = pos.zw;
    18.     o.xy /= o.w;
    19.     o.xy *= camTargetSize;
    20.     return o.xy;
    21. }
    Forget the code written by the author of the topic like a bad dream. You should not use such code.
     
    Nad_B likes this.
  31. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,919
    This part:
    is extremely weird. Why would you first convert from clipspace into viewport space before doing the homogeneous divide? You essentially add a mangled "0.5" to the position just to unmangle it later. When you switch the two operations as they usually happen this would not be necessary.

    Code (CSharp):
    1.  
    2.     // !!! untested !!!
    3.     // pos is in range -w to w. w contains depth dependent scaling which defines the clip boundingbox
    4.     // everything outside that range will be "clipped".
    5.     // that's why this is called clipspace
    6.     var pos = math.mul(matrixVP, new float4(worldPosition, 1.0f));
    7.  
    8.     // homogeneous divide. Now we're in NDC (normalized device coordinates) which goes from -1 to 1.
    9.     float2 o = pos.xy / pos.w;
    10.  
    11.     // NDC to viewport space (-1 to 1 --> 0 to 1)
    12.     o = (o+1) * 0.5; // or o = o*0.5 + 0.5;
    13.  
    14.     // viewport to screenspace / render texture space
    15.     o *= camTargetSize;
    16.     return o;
    Note that this code does not return the distance from the camera as Unity's WorldToScreenPoint method does. This is often times useful to determine if something is infront of the camera or behind. Technically the projection works in both directions. So when something is inside the back facing camera cone, it would also be mapped to a screen coordinate. I've seen many games that do not account for this and get some weird screen indicators when something is behind you..
     
    Nad_B likes this.
  32. viruseg

    viruseg

    Joined:
    Jul 8, 2017
    Posts:
    23
    You're absolutely right. I took the code from UnityCG.cginc and ported it as is. But your variant gives more accurate results.
    Code (CSharp):
    1. var cameraData = new CameraDataForBurst(camera);
    2.  
    3. var worldPoint = new float3(10, 10, 0);
    4. var screenPointWithZ = cameraData.WorldToScreenPointWithZ(worldPoint);
    5. var screenPoint = cameraData.WorldToScreenPoint(worldPoint);
    6.  
    7.  
    8.  
    9. public readonly struct CameraDataForBurst
    10. {
    11.     public readonly float4x4 matrixVP;
    12.     public readonly float4x4 matrixV;
    13.     public readonly float2 camTargetSize;
    14.     public readonly bool orthographic;
    15.  
    16.     public CameraDataForBurst(Camera camera)
    17.     {
    18.         matrixVP = CreateMatrix4x4(camera.projectionMatrix * camera.worldToCameraMatrix);
    19.         matrixV = CreateMatrix4x4(camera.worldToCameraMatrix);
    20.         camTargetSize = new float2(camera.pixelWidth, camera.pixelHeight);
    21.         orthographic = camera.orthographic;
    22.     }
    23.  
    24.     public float3 WorldToScreenPointWithZ(float3 worldPosition)
    25.     {
    26.         return new float3(WorldToScreenPoint(worldPosition),
    27.                           math.mul(matrixV, new float4(worldPosition, 1.0f)).z * -1);
    28.     }
    29.  
    30.     public float2 WorldToScreenPoint(float3 worldPosition)
    31.     {
    32.         var pos = math.mul(matrixVP, new float4(worldPosition, 1.0f));
    33.         var o = pos.xy;
    34.         if (!orthographic) o /= pos.w;
    35.         o = math.mad(o, 0.5f, 0.5f);
    36.         o *= camTargetSize;
    37.         return o;
    38.     }
    39.  
    40.     private static float4x4 CreateMatrix4x4(Matrix4x4 matrix)
    41.     {
    42.         return new float4x4(matrix.m00, matrix.m01, matrix.m02, matrix.m03,
    43.                             matrix.m10, matrix.m11, matrix.m12, matrix.m13,
    44.                             matrix.m20, matrix.m21, matrix.m22, matrix.m23,
    45.                             matrix.m30, matrix.m31, matrix.m32, matrix.m33);
    46.     }
    47. }
     
    Last edited: Dec 13, 2023
    Nad_B and Bunny83 like this.