Where is "unity_CameraToWorld" documented?

Where is this documented?:

unity_CameraToWorld

I’m using a shader and can’t find any information in the manuals or on google on this.
Thanks

fixed3 camForward_n = normalize(mul((float3x3)unity_CameraToWorld, float3(0,0,1)));

There’s a lot in the Unity shaders not documented in the official documentation, but rather in the shader code source itself.

    // Projection matrices of the camera. Note that this might be different from projection matrix
    // that is set right now, e.g. while rendering shadows the matrices below are still the projection
    // of original camera.
    float4x4 unity_CameraProjection;
    float4x4 unity_CameraInvProjection;
    float4x4 unity_WorldToCamera;
    float4x4 unity_CameraToWorld;

Now that probably doesn’t fully answer your question since the comment in the code is more specifically referencing the unity_CameraProjection and unity_CameraInvProjection matrices, but generally the names of the matrices are self descriptive.

The unity_CameraToWorld is the camera to world transform matrix. This is similar to the also undocumented UNITY_MATRIX_I_V (or unity_MatrixInvV, the former is a macro that points at the later), which is the view to world transform matrix… but different.

To explain what those are first I’m going to explain the unity_WorldToCamera matrix. This is the world space to camera space transform matrix. In Unity, if you have an object that has no parent game object, its transform component will show its world space position. If you move that object to be a child of the camera, the position it shows is now the camera space position. It’s now a position relative to the camera’s orientation and using the camera’s position as the origin. A transform matrix defines how you convert from one position space to another.

So unity_CameraToWorld is the transform matrix for converting from camera space to world space.* This is the inverse transform of the unity_WorldToCamera matrix.

So what about the view matrix? Unity’s rendering uses OpenGL’s conventions for view space, which defines the camera’s forward as -Z. This is different than Unity’s transforms, which uses +Z forward, thus there are two different matrices passed to the shader. The UNITY_MATRIX_I_V is the view to world matrix, or inverse world to view matrix (UNITY_MATRIX_V). The world to view matrix is often just referred to as just the “view matrix” as it’s implicitly assumed to be transforming from world space.

The other difference is the UNITY_MATRIX_V (and UNITY_MATRIX_I_V) is used directly by Unity’s default rendering setup, meaning it’s always set to the current view matrix being rendered with. This isn’t always the same “camera” as the unity_WorldToCamera matrix. For example, when rendering shadow maps the “camera” matrices are for the camera that those shadows are being rendered for rather than the matrix being used to render which, which would be aligned to the position and/or orientation of the light being rendered. Similarly a command buffer that’s set to override the projection and view matrices will override the UNITY_MATRIX_V and UNITY_MATRIX_P, but not the unity_CameraProjection and unity_WorldToCamera matrices.

So what is that line of code doing? It’s getting the camera forward vector. Exactly as the variable’s name implies.

* A small caveat: Both the view matrices and camera matrices passed to the shader are unscaled, so the later doesn’t exactly match the Unity component transform positions if any scale is applied to the camera or any of its parent game objects. Also, confusingly, Unity’s Camera component has a .worldToCameraMatrix property, which actually matches the view matrix, not the world to camera matrix.

3 Likes

Thanks for the info. It is useful.
One more question:
Does Unity generate some default directional light if none exists in the scene?
I don’t’ have a directional light in the scene, but the shader I’m using is accessing
_WorldSpaceLightPos0
The shader is behaving as if there’s some directional light shining towards the +Z world axis, even though there’s none in the hierarchy.
Thanks

In the ForwardBase pass, _WorldSpaceLightPos0 will indeed always be “something”. You can’t have null values in HLSL, and having the “light position” (actually the inverse light direction for directional lights) be all zeros can cause some shaders to produce a NaN or infinity, so defaulting to something is usually preferable.

If you want to check if there’s actually a directional light in the forward base pass, you need to check if _LightColor0 is black or not. The easiest way to do that is to use:

if (any(_LightColor0.rgb)) {
  // your code that uses the light direction
} else {
  // the light color is black and there's no directional lighting
}

Note, that if (_LightColor0.rgb == half3(0,0,0)) isn’t an option here. Direct vector comparisons aren’t allowed in HLSL for if statements, and while you could use a ternary like:

half4 col = _LightColor0 == half4(0,0,0,0) ? _ColorA : _ColorB;

This is evaluated on a per-component basis, so it’s equivalent to:

half4 col;
col.r = _LightColor0.r == 0 ? _ColorA.r : _ColorB.r;
col.g = _LightColor0.g == 0 ? _ColorA.g : _ColorB.g;
col.b = _LightColor0.b == 0 ? _ColorA.b : _ColorB.b;
col.a = _LightColor0.a == 0 ? _ColorA.a : _ColorB.a;

Technically you could do:

if (any(_LightColor0.rgb == half3(0,0,0)))

But that gets the same result as !any(_LightColor0.rgb), but uses more math (ie: is more expensive).

2 Likes

Hi, I’m a little confused by your answer - specifically the “small caveat”. How can I use the camera component’s .worldToCameraMatrix in shader code. If I use the unity_CameraToWorld in the shader it doesn’t match the camera components world to camera matrix. But I need to use unity_CameraToWorld as I’m working in VR and need a different matrix for each eye - which this does for you (from my understanding).

I’m unsure if I read your reply incorrectly, but you say the camera component .worldToCameraMatrix matrix actually matches the view matrix, but using UNITY_MATRIX_I_V (or inverse) doesn’t match the camera component’s matrix.

Is there another variable or macro in the shader that I can use that would match the camera component .worldToCameraMatrix matrix? If not, do you have any ideas on how I can pass both eye matrices into the shader when I only get access to one camera while rendering?

Thanks!

UNITY_MATRIX_V == cam.worldToCameraMatrix

UNITY_MATRIX_I_V == cam.cameraToWorldMatrix

unity_WorldToCamera == Matrix4x4(cam.transform.position, cam.transform.rotation, Vector3.one)

unity_CameraToWorld == Matrix4x4(cam.transform.position, cam.transform.rotation, Vector3.one).inverse

3 Likes

Hi again, it seems that UNITY_MATRIX_I_V isn’t the same as cam.cameraToWorldMatrix. I resorted to setting a matrix variable in the shader to the cam to world matrix and using that - but this seems to be causing some rendering issues in VR. I’m using a custom Renderer Feature in the Universal Render Pipeline, would you know of any reason why the matrix in the shader doesn’t match the camera’s cameraToWorldMatrix? I’ve attached some images below of how the shader is acting differently:

This is what happens when uploading the cameraToWorldMatrix from the camera (intended behaviour):
https://gyazo.com/8e59bdba0559d1d77c2af35dd78be3f0

This is what happens when using the UNITY_MATRIX_I_V in the shader (not behaving correctly):
https://gyazo.com/e0b13c20b5998f70ce9ef1c8b45c5b5f

Here is how I’m using the camera to world matrix:

Ray ray = CreateCameraRay(i.uv, _CamToWorldMatrix);
Ray ray = CreateCameraRay(i.uv, UNITY_MATRIX_V_I);

And here is the CreateCameraRay function, I don’t know if I’m doing something wrong there:

inline Ray CreateCameraRay(float2 uv, float4x4 camToWorld)
{
    float3 origin = GetCameraPositionWS();
    float3 direction = mul(unity_CameraInvProjection, float4(uv * 2 - 1, 0, 1)).xyz;
    direction = mul(camToWorld, float4(direction, 0)).xyz;
    float dirLength = length(direction);
    direction = normalize(direction);

    return CreateRay(origin, direction, dirLength);
}

Thanks again!! :slight_smile:

Edit: Added links for GIFs as they aren’t showing up in the post properly

UNITY_MATRIX_I_V is broken in some version of Unity. It should be the same as cam.cameraToWorldMatrix and is for most releases, but unfortunately not in some recent ones.

Thanks for the reply!

Oh, that’s quite annoying - would you happen to know which Unity versions are affected? And does Unity know about the issue (if not I’ll post a bug report)?

Sorry, I don’t have an answer to either question. I’ve not seen the issue in the editor version I’ve been using (2018 & 2019), so I believe it’s something new in some 2020, maybe 2021 versions. I just know I’ve seen other Unity devs complain about it on Twitter. It’s also something that may have already been fixed if you get the latest version.

1 Like