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.
/// <summary>
/// Updates The Target Icons Position (Threaded Version).
/// </summary>
void UpdateTarget(TargetUIInfo info)
{
bool behindCamera = false;
// If target is pinned to a side of the screen.
bool horizontalPin = false;
bool verticalPin = false;
// Calculate Screen Point of Object.
Vector3 pointOnScreen = Vector3.zero;
Vector4 v = _ViewPort * new Vector4(info.targetPosition.x, info.targetPosition.y, info.targetPosition.z, 1);
Vector3 _ViewPortPoint = v / -v.w;
// Will keep _ViewPort Under 1 for multiplication.
Vector3 normalized_ViewPort = new Vector3(_ViewPortPoint.x + 1, _ViewPortPoint.y + 1, 0);
normalized_ViewPort /= 2;
// Get Vector (Distance essentially to use in Dot Product).
Vector3 heading = info.targetPosition - _CameraPosition;
// Check is object is behind camera.
behindCamera = (Vector3.Dot(_CameraForward, heading) < 0) ? true : false;
if (behindCamera)
{
// Calculate Inverse Transform Point to determine where target is located behind camera.
Vector3 inverseTransPoint = Quaternion.Inverse(_CameraRotation) * (info.targetPosition - _CameraPosition).normalized;
// Positive Right / Negative Left
if (inverseTransPoint.x > 0)
{
pointOnScreen.x = Screen.width;
horizontalPin = true;
}
// Positive Top / Negative Bottom
if (inverseTransPoint.y < 0)
{
pointOnScreen.y = Screen.height;
verticalPin = true;
}
// Position target positions to side of screen if inverseTransPoint indicated pinning.
if (!verticalPin)
pointOnScreen.y = normalized_ViewPort.y * Screen.height;
if (!horizontalPin)
pointOnScreen.x = normalized_ViewPort.x * Screen.width;
// Special Case if both are 0/0 and target is behind.
if (!verticalPin && !horizontalPin)
{
pointOnScreen = info.targetPosition;
}
}
else
{
// Regular Positioning of Target Icon
pointOnScreen.x = normalized_ViewPort.x * Screen.width;
pointOnScreen.y = normalized_ViewPort.y * Screen.height;
}
// NGUI Required (Will not be neccesary when Moved over to Unity UI).
pointOnScreen *= ratio.y;
// Set position of UI Overlay (via Cache as transform cannot be accessed in Thread).
info.updatedPosition = pointOnScreen;
}
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.
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?
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
Matrix4x4 VP = cam.projectionMatrix * cam.transform.worldToLocalMatrix;
Vector4 v = VP * new Vector4( worldPos.x, worldPos.y, worldPos.z, 1 );
Vector3 ViewportPoint = v / -v.w;
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.
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.
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.
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.
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.
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
Quaternion.Inverse(Camera Rotation) * (Target Position - Camera Position));
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.
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.
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.