Hi, I am having trouble converting a 3d world position to a 2d screen position.
I"ve been at it for a while, trying every code snipped I could find but the results are always wrong.
Iam trying to do a simple example where a point in world space is rendered as a red circle on screen from any view angle.
This transforms from the current world position to the clip space position ⌠for the geometry that is currently being rendered. The fact youâre comparing the calculated screen position to the uv makes me believe this is being done for a post process effect. When rendering a post process, the geometry thatâs being rendered is a single quad or triangle thatâs covering the entire screen. The easiest way to do that is by having the âworldâ be the camera plane, and thus the âworld to clip spaceâ matrix is essentially empty. It is not the world space of the scene anymore. So you canât use it to get a clip space position of something in the game scene.
For that you need to use the unity_WorldToCamera and unity_CameraProjection matrices. Check the above links you posted for examples that use those specifically.
Thanks, yes I forgot to mention that I am indeed trying to work with post processing.
Iâve tried the snipped in the first link before and this is the result I am getting:
It works, but there is an issue with it where it also draws an inverted positioned circle when I turn around and look away from the stated world position.
The other issue with it is that I have no idea why it works or what it does.
Why does the wpos need to be processed first?
Why does it need that unity*_CameraProjection._m11* single matrix value?
What are those height, width, divide by 2 transformations?
To answer some of these questions, Iâm going to start by correcting / simplifying a few things in the above code:
float4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_point_clamp, IN.uv);
float3 circle_worldPos = float3(0,0,4);
// this avoids the extra line of code to subtract the world space camera position
float4 circle_cameraPos = mul(unity_WorldToCamera, float4(circle_worldPos, 1.0));
// if behind the camera, ignore
if (circle_cameraPos.z <= 0.0)
return tex;
// the WorldToCamera matrix is +z forwad, but the projection matrix expects a -z forward view space
circle_cameraPos.z = -circle_cameraPos.z;
// transform view space to clip space position
float4 circle_clipPos = mul(unity_CameraProjection, circle_cameraPos);
// clip space has a -w to +w range for on screen elements, so divide x and y by w to get a -1 to +1 range
// then multiply by 0.5 and 0.5 to bring from a -1 to +1 range to 0.0 to 1.0 screen position UV
float2 circle_screenPos = (circle_clipPos.xy / circle_clipPos.w) * 0.5 + 0.5;
// offset between the circle center and the screen position
float2 circle_screenOffset = IN.uv - circle_screenPos;
// get the aspect ratio of the screen and adjust the x axis of the offset so you get a circle instead of an oval
float aspect = _ScreenParams.x/_ScreenParams.y;
circle_screenOffset.x *= aspect;
float dist_to_circle = length(circle_screenOffset);
if (dist_to_circle < 0.05)
return float4(1,0,0,0);
return tex;
Most of the code you used was trying to account for a few mistakes they made. Like they used a float3 in the mul with unity_WorldToCamera, which only applies the rotation from that matrix. You need to use a float4 with a w of 1.0 for it to apply the transform too. And then they are taking a camera space position (which is a position relative to the camera, like if you move an object to be a child of the Camera game object) and then are using the camera space depth (cpos.z), the projectionâs FOV (unity_CameraProjection._m11), and the screenâs aspect ratio to try and rescale that camera space position back into screen space. And it all kind of works, but is just way more complicated than it needed to be.