Search Unity

Spotlights and _LightMatrix0

Discussion in 'Shaders' started by Heisenbug, Jun 9, 2014.

  1. Heisenbug

    Heisenbug

    Joined:
    Aug 31, 2012
    Posts:
    40
    I'm trying to understand exactly how spotlights are implemented in Unity.

    I saw how attenuation is implemented:

    Code (CSharp):
    1.  
    2. inline fixed UnitySpotAttenuate(float3 LightCoord)
    3. {
    4.     return tex2D(_LightTextureB0, dot(LightCoord, LightCoord).xx).UNITY_ATTEN_CHANNEL;
    5. }
    6.  
    It uses a attenuation function encoded in the texture _LightTextureB0 and uses the squared distance as sampling coords, right?

    The shape of the spot light is implemented using a second lookup:

    Code (CSharp):
    1.  
    2. inline fixed UnitySpotCookie(float4 LightCoord)
    3. {
    4.    return tex2D(_LightTexture0, LightCoord.xy / LightCoord.w + 0.5).w;
    5. }
    6.  
    It looks like a projective texture.

    Now the question is, none of the above code snippets take in considerations things like spot light range and spot angle. Am I right?
    Because the transformation in light space is performed using _LightMatrix0, I guess that all the distance scaling and distortions introduced by Range and SpotAngle parameters are encoded inside it.

    So I have 2 questions:

    1. Did I understand correctly how spotlights work?
    2. Does anyone can tell me how _LightMatrix0 is constructed and how it takes in consideration Range and SpotAngle values?
    Side note:

    I need a single pass batchable way to render both lightmaps and dynamic spotlights. Since it appears that Unity only allows spotlights to be rendered with additional passes, increasing drawcalls number, I'm trying to optimize that for my specific requirements (mobile device, several dynamic spot lights). I already did that successfully (setting up my custom light matrices: one to transform vertices in light space and one to perform cookie texture projection), but the final effect is slightly different from the builtin spotlight and probably slightly more inefficient because I need an additional matrix multiplication in the vertex shader.
     
    vexe likes this.
  2. Heisenbug

    Heisenbug

    Joined:
    Aug 31, 2012
    Posts:
    40
    Thank you so much for spending your time in such a detailed and clear explanation.

    The part I was really missing was to use the cotangent to build the cookie projection matrix. I still didn't implemented it, but I'm pretty sure it works.

    I need this only in forward rendering to reduce my drawcalls number. I think I'll need to find also a proper way to use this shader only while rendering objects enlightened by the spot light (I still need to investigate how to do that efficiently, maybe using an octree and the light frustum to detect approximately the illuminated objects and switching their materials?).

    thank you again
     
  3. Heisenbug

    Heisenbug

    Joined:
    Aug 31, 2012
    Posts:
    40
    I tried to implement your solution but the result seems still wrong. I tested the shader for both the transformation in light space and the attenuation and that part is correct.

    This is how I'm building the matrices.

    Cookie projection matrix:

    Code (CSharp):
    1.  
    2. lightProjMatrix = Matrix4x4.identity;
    3.  
    4. float d = Mathf.Deg2Rad * l.spotAngle * 0.5f;
    5. d = Mathf.Cos (d) / Mathf.Sin(d);
    6. lightProjMatrix[3,2] = 2f / d;
    7.  
    8.  

    Light Range Matrix:

    Code (CSharp):
    1.  
    2. float val = 1f / l.range;
    3. Vector3 scale = new Vector3 (val, val, val);
    4. lightRangeMatrix =  Matrix4x4.Scale (scale);
    5.  
    World to light Matrix is transform.worldToLocalMatrix of the spotlight gameobject.

    I set the product as shader uniform:

    Code (CSharp):
    1. Shader.SetGlobalMatrix ("_LightMatrixCustom", lightRangeMatrix *lightProjMatrix  * transform.worldToLocalMatrix);
    The product is used in the shade this way (I show only the part relative to project the cookie):

    Code (CSharp):
    1.  
    2.  
    3. vertexOutput vert(vertexInput input)
    4.          {
    5.             vertexOutput output;
    6.             float4x4 modelMatrix = _Object2World;
    7.             output.posWorld = mul(modelMatrix, input.vertex);
    8.             output.posLight = mul(_LightMatrixCustom,output.posWorld);
    9.                  
    10.             output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
    11.  
    12.             .........
    13.             return output;
    14.          }
    15. float4 frag(vertexOutput input) : COLOR
    16. {
    17.        
    18.               float cookieAttenuation = tex2D(_LightTexture0, (input.posLight.xy / input.posLight.w)  + 0.5).w;
    19.               return  cookieAttenuation;
    20.            
    21.          }
    22.  
    What am I doing wrong?
     
    INeatFreak likes this.
  4. Heisenbug

    Heisenbug

    Joined:
    Aug 31, 2012
    Posts:
    40
    Sorry, was just a type while copying code. I'm already used the latter version.

    This is the effect showing only cookie attenuation:


    The spot appear to be larger than the range (without considering attenuation). The spotlight is at distance 1 along z axis from the plane.
     
  5. Heisenbug

    Heisenbug

    Joined:
    Aug 31, 2012
    Posts:
    40
    Oh..thank you so much... I've looked at the matrix so long without figuring it out.. It works perfectly !
     
  6. Heisenbug

    Heisenbug

    Joined:
    Aug 31, 2012
    Posts:
    40
    And what about Intensity? How would you implement it?
     
  7. Acegikmo

    Acegikmo

    Joined:
    Jun 23, 2011
    Posts:
    1,294
    I was wondering - could you elaborate on what those differences are? I'm having issues reading from the light matrix in deferred pre-pass lighting.
     
  8. Acegikmo

    Acegikmo

    Joined:
    Jun 23, 2011
    Posts:
    1,294
    Thanks for the elaborate explanation and testing :)
    I was talking to Aras before Unite about accessing the rotation of light sources. This was possible if the light had a cookie assigned, otherwise not. But yeah, I'm also interested in accessing the actual object space for the light sources, including scale. It would be useful!
     
  9. mumogj

    mumogj

    Joined:
    Jan 25, 2018
    Posts:
    1
    Is this the Matrix for object to lightspace?Is it right? Now I am puzzled for how to get this lightmatrix .lightmatrixs for spotlight and directionlight,can you tell me the lightmatrixs