Search Unity

Light Space to World Space

Discussion in 'Shaders' started by Ewanuk, Apr 5, 2018.

  1. Ewanuk

    Ewanuk

    Joined:
    Jul 9, 2011
    Posts:
    257
    I'm doing some manipulation to the lighting at the shader level to adjust how the light direction behaves. The algorithm I use needs the light direction in light space. Easy enough with unity_WorldToLight.

    However I need to bring this direction back to world space due to how the vast majority of light is calculated throughout other shaders. But:
    • There's no associated unity_LightToWorld variable
    • inverse(float4x4) is not supported
    • There's no way (as far as I can tell) to set a shader variable on a per-light basis. That is, just before the additive pass for a light is executed, set a global shader variable. I looked at light command buffers, but it seems like there's no "BeforeAdditivePass" event or something equivalent
    I really don't want to rewrite my algorithm, is there any way to get a LightToWorld matrix into a shader variable?

    Edit: I found an inverse function here, but it doesn't look like it's correct, anyone care to take a look?
    https://answers.unity.com/questions/218333/shader-inversefloat4x4-function.html
     
    Last edited: Apr 5, 2018
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
  3. Ewanuk

    Ewanuk

    Joined:
    Jul 9, 2011
    Posts:
    257
    Was thinking of that, but I need to set a variable for every light (not practical), or change it every time there's an additive pass, which, I can't find any way to do.
     
  4. Ewanuk

    Ewanuk

    Joined:
    Jul 9, 2011
    Posts:
    257
    Don't suppose there's a smarter way to get a light space direction into world space? I feel like inverting the whole matrix is overkill. There's also some odd scaling values the world to light matrix has
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    I'm curious what your doing to the light vector, as it's likely it'd be easy to do everything in world space with a dot product or two.

    However for a directional light, I think the transpose matrix is the same as the inverse. Reversing the order of the vector and matrix in the mul function will apply the matrix as if it was the transpose.

    worldSpaceDir = normalize(mul(lightSpaceDir, float3x3(unity_WorldToLight)));
     
  6. Ewanuk

    Ewanuk

    Joined:
    Jul 9, 2011
    Posts:
    257
    We are doing a hacky, poor man's area light with user-defined elliptical sizes.

    So, if they define a certain light as 1m x 0.5m wide, we render that light as if it's coming from a surface that large.

    We've got the attenuation defined and working, but the fragment-to-light direction vector needs to be adjusted to look at the closest possible point on the light surface. This algorithm for this adjustment is done in light space, and for the life of me I can't figure out how to do it in world-space because I can't figure out the light's "up" and "right" vectors in world space. If I can figure that out, I can do it all in world space I think.

    It's not perfect, but we found it gives a much better approximation of an area light in real time.

    We are doing all this w/ spot lights, which have funky worldToLight matricies that differ between forward and deferred pipelines. Whole thread on that here: https://forum.unity.com/threads/deferred-lighting-how-get-light-space-positions.402574/
     
  7. Ewanuk

    Ewanuk

    Joined:
    Jul 9, 2011
    Posts:
    257
    This seems to work nicely for the forward pipeline (which is incredible, thanks!), but not the deferred pipeline, guessing it has something to do with the deferred matrix applying a projection while the forward one doesn't.
     
  8. Ewanuk

    Ewanuk

    Joined:
    Jul 9, 2011
    Posts:
    257
    Here are the given matricies. I'm not sure what it is about the deferred matrix that makes the output so broken.

    Code (CSharp):
    1. float3x3 worldToLight = {
    2.     unity_WorldToLight[0].xyz,
    3.     unity_WorldToLight[1].xyz,
    4.     unity_WorldToLight[2].xyz
    5. };
    6.  
    7. // Works in forward, but not in deferred
    8. return normalize(mul(lightDirLightSpace, worldToLight));
    9.  
    10. unity_WorldToLight  (forward)
    11. ~0          ~0          0.026           -0.026
    12. ~0          0.026       ~0              -0.079
    13. -0.026      ~0          ~0              0.12
    14. -6          ~0          ~0              27      
    15.  
    16. unity_WorldToLight  (deferred)
    17. 0.5         ~0          -0.0044         -2.3
    18. 0.5         -0.0044     ~0              -2.2
    19. 1           ~0          ~0              -4.5
    20. 1           ~0          ~0              -4.5
     
  9. Ewanuk

    Ewanuk

    Joined:
    Jul 9, 2011
    Posts:
    257
    Well, we've got the forward pipeline working, which is great, (thanks again bgolus).

    Deferred we're hacking around it somewhat. We're living with only having the altered light direction in light space, but to get around the shading issues, we're transforming the world normals and view direction into light space before they're sent to any deferred lighting calculations.

    Looks like it's doing the trick.