I’m using Unity 2020.Literally,it seems that they are both for converting view space to world space.However,I tried and found they are not the same thing.It looks like unity_MatrixInvV is right.So as metioned in title,what’s the difference between unity_MatrixInvV and unity_CameraToWorld?
The unity_MatrixInvV
is the inverse View matrix. I mean, that’s probably obvious already. This matches the c# script side cam.cameraToWorldMatrix
, and use OpenGL’s convention for view matrices. That is the forward is -Z.
The unity_CameraToWorld
is not the view matrix, not really at least. It isn’t the cam.cameraToWorldMatrix
either, even though they are confusingly both “camera to world”. That matrix matches the Unity game object transform, excluding scale. In Unity transform forward is +Z, but is otherwise the same. unity_WorldToCamera
is equivalent to Matrix4x4.TRS(cam.position, cam.rotation, Vector3.one)
, with unity_CameraToWorld
being the inverse of that.
Additionally, the unity_MatrixInvV
, along with the unity_MatrixV
and other unity_Matrix*
or MATRIX_*
matrices are all based on the currently rendering view. For example it could be view matrix for shadow map rendering, or a Blit()
for post processing, screen space UI, etc. The unity_Camera*
and unity_WorldToCamera
matrices are always the camera component that initiated rendering, so these remain unchanged during rendering of shadows or post processing.
hm , so i assume, when i am in a post process shader, and i want to reconstruct a world position:
unity_MatrixInvV,unity_MatrixV
etc. will be wrong, because they are using the blit camera settings.unity_WorldToCamera
is not the view matrix, so wrong- passing
cam.cameraToWorldMatrix
into the shader via script sould be the correct one, but this does not seem to work correctly. it looks exactly likeunity_WorldToCamera
Correct. During a blit they’re both just an identity matrix.
Eh yes, but … we’ll get back that.
They might both end up looking broken, but they are not going to be the same. If they look exactly alike, this tells me you’re missing something else.
So back to using unity_WorldToCamera
(or rather the inverse) for the case of reconstructing the world position presumably from the camera depth texture. This is the correct way to do it. It is in fact how Unity’s own code does it for directional shadows and post process effects. Because while it’s not technically “the view matrix”, because the view matrix explicitly refers to the matrix being used for rendering, it is an entirely accurate transform matrix for getting the world position in this use case. However I have to wonder how you’re doing the rest of your shader. The camera depth texture is in screen space, which is different than view space, and requires a few steps to convert from to view space. There are several methods out for doing that, like reconstruction NDC or clip space from the screen space UV and depth and using an inverse projection matrix, or setting up a view space ray direction and multiply it by the camera far plane, etc.
Here’s an example shader of mine that shows a number of different approaches for reconstructing the world normal from the depth texture, something that requires reconstructing the (view space) position first.
https://gist.github.com/bgolus/a07ed65602c009d5e2f753826e8078a0
See the viewSpacePosAtScreenUV()
and viewSpacePosAtPixelPosition()
functions, as well as the commented out note near the end about using unity_CameraToWorld
instead of unity_MatrixInvV
. The unity_CameraToWorld
may not be the inverse view matrix, but it’s so close that it’s trivial to use it like it is. The same kind of setup mention in that comment, invert the z value before transforming by the not-view-matrix, can be used to reconstruct the world space position from the view space position those functions return.
float3 worldPos = mul(unity_CameraToWorld, float4(viewPos * float3(1.0,1.0,-1.0), 1.0)).xyz;
The view space reconstruction is inspired by some code from Keijiro, someone you should want to be familiar with if you’re not already.
thank you for the explanation and the example code! i was missing the uv to viewpos conversion. also, i am still trying to wrap my head around the impact of the viewport z-position.
i am still having different results in perspective, and orthographic camera. but i will try out some more.
you definetely helped me out!
Oh, yeah, that code won’t work for orthographic. That requires a completely different setup. That shader simplifies stuff in a way that works for perspective cameras, but orthographic camera support requires doing some stuff more “correctly”.
See this function from Unity’s screen space shadows, which should work.
https://github.com/TwoTailsGames/Unity-Built-in-Shaders/blob/master/DefaultResourcesExtra/Internal-ScreenSpaceShadows.shader#L184
dude, you are a walking library!
thank you for the help, i was able to get the conversion working for both camera types!
i owe you one
main code snippet to make it work for me was:
versionA (according to line 207)
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
// Input position is just the UVs going from [-1,1] at the far clip plane (1.0 because we manually passed in the matrix, which Unity stores via GL convention)
o.viewDir = mul(_InvProjectionMatrix, float4 (o.uv * 2.0 - 1.0, 1.0, 1.0));
//calculate positions for orthographic camera
// code from https://github.com/TwoTailsGames/Unity-Built-in-Shaders/blob/master/DefaultResourcesExtra/Internal-ScreenSpaceShadows.shader#L88
float2 clipPos = v.uv * 2 - 1;
float3 orthoPosNear = mul(unity_CameraInvProjection, float4(clipPos.x, clipPos.y, -1, 1)).xyz;
float3 orthoPosFar = mul(unity_CameraInvProjection, float4(clipPos.x, clipPos.y, 1, 1)).xyz;
orthoPosNear.z *= -1;
orthoPosFar.z *= -1;
o.orthoPosNear = orthoPosNear;
o.orthoPosFar = orthoPosFar;
return o;
}
inline float3 computeCameraSpacePos(v2f i, float depth)
{
// view position calculation for perspective & ortho cases
float3 vposPersp = i.viewDir * depth;
float3 vposOrtho = lerp(i.orthoPosNear, i.orthoPosFar, 1-depth);
// pick the perspective or ortho position as needed
float3 camPos = lerp(vposPersp, vposOrtho, unity_OrthoParams.w);
return camPos;
}
VersionB (according to line 184
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
// Input position is just the UVs going from [-1,1] at the far clip plane (1.0 because we manually passed in the matrix, which Unity stores via GL convention)
o.viewDir = mul(_InvProjectionMatrix, float4 (o.uv * 2.0 - 1.0, 1.0, 1.0));
return o;
}
inline float3 computeCameraSpacePos(v2f i, float zdepth)
{
// View position calculation for oblique clipped projection case.
// this will not be as precise nor as fast as the other method
// (which computes it from interpolated ray & depth) but will work
// with funky projections.
float4 clipPos = float4(i.uv.xy, zdepth, 1.0);
clipPos.xyz = 2.0f * clipPos.xyz - 1.0f;
float4 camPos = mul(unity_CameraInvProjection, clipPos);
camPos.xyz /= camPos.w;
camPos.z *= -1;
return camPos.xyz;
}