Search Unity

Where is "unity_CameraToWorld" documented?

Discussion in 'Shaders' started by ArachnidAnimal, Jun 9, 2020.

  1. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,815
    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)));
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    There's a lot in the Unity shaders not documented in the official documentation, but rather in the shader code source itself.

    https://github.com/TwoTailsGames/Un.../master/CGIncludes/UnityShaderVariables.cginc
    Code (CSharp):
    1.     // Projection matrices of the camera. Note that this might be different from projection matrix
    2.     // that is set right now, e.g. while rendering shadows the matrices below are still the projection
    3.     // of original camera.
    4.     float4x4 unity_CameraProjection;
    5.     float4x4 unity_CameraInvProjection;
    6.     float4x4 unity_WorldToCamera;
    7.     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.
     
    Billy4184, J_Kost and ArachnidAnimal like this.
  3. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,815
    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
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    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:
    Code (csharp):
    1. if (any(_LightColor0.rgb)) {
    2.   // your code that uses the light direction
    3. } else {
    4.   // the light color is black and there's no directional lighting
    5. }
    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:
    Code (csharp):
    1. half4 col = _LightColor0 == half4(0,0,0,0) ? _ColorA : _ColorB;
    This is evaluated on a per-component basis, so it's equivalent to:
    Code (csharp):
    1. half4 col;
    2. col.r = _LightColor0.r == 0 ? _ColorA.r : _ColorB.r;
    3. col.g = _LightColor0.g == 0 ? _ColorA.g : _ColorB.g;
    4. col.b = _LightColor0.b == 0 ? _ColorA.b : _ColorB.b;
    5. col.a = _LightColor0.a == 0 ? _ColorA.a : _ColorB.a;
    Technically you could do:
    Code (csharp):
    1. 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).
     
    Ruchir and ArachnidAnimal like this.
  5. WSWhitehouse

    WSWhitehouse

    Joined:
    Sep 26, 2018
    Posts:
    8

    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!
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    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
     
  7. WSWhitehouse

    WSWhitehouse

    Joined:
    Sep 26, 2018
    Posts:
    8
    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:

    Code (CSharp):
    1. Ray ray = CreateCameraRay(i.uv, _CamToWorldMatrix);
    2. 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:
    Code (CSharp):
    1. inline Ray CreateCameraRay(float2 uv, float4x4 camToWorld)
    2. {
    3.     float3 origin = GetCameraPositionWS();
    4.     float3 direction = mul(unity_CameraInvProjection, float4(uv * 2 - 1, 0, 1)).xyz;
    5.     direction = mul(camToWorld, float4(direction, 0)).xyz;
    6.     float dirLength = length(direction);
    7.     direction = normalize(direction);
    8.  
    9.     return CreateRay(origin, direction, dirLength);
    10. }
    Thanks again!! :)

    Edit: Added links for GIFs as they aren't showing up in the post properly
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    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.
     
  9. WSWhitehouse

    WSWhitehouse

    Joined:
    Sep 26, 2018
    Posts:
    8
    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)?
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    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.
     
    WSWhitehouse likes this.