Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Custom Spotlight Calculation not working

Discussion in 'Shaders' started by BrightBit, Apr 30, 2024.

  1. BrightBit

    BrightBit

    Joined:
    Jan 22, 2013
    Posts:
    270
    Does anyone know how the inner spotlight angle for a spotlight in Unity's built-in render pipeline is calculated (since I can't set them manually)?

    I fake spotlights with a custom shader based on billboards. The shader calculates the lighting like this:

    Code (csharp):
    1. float3 IncomingLight(float3 surfaceNormal, Light light)
    2. {
    3.    return saturate(dot(surfaceNormal, light.direction) * light.attenuation) * light.color;
    4. }
    5.  
    6. float GeneralAttenuation(float normalizedDistance)
    7. {
    8.     // return saturate(1.0 / (1.0 + 25.0 * square(normalizedDist)) * saturate((1 - normalizedDist) * 5.0));
    9.     return tex2D(_LightTextureB0, square(normalizedDistance).xx).UNITY_ATTEN_CHANNEL;
    10. }
    11.  
    12. Light Spotlight(float3 worldPos,
    13.                 float3 lightPos,
    14.                 float3 lightDir,
    15.                 float3 color,
    16.                 float innerSpotAngle,
    17.                 float outerSpotAngle,
    18.                 float range)
    19. {
    20.     Light light;
    21.  
    22.     float3 ray = lightPos.xyz - worldPos;
    23.  
    24.     float innerCos = cos(DEGREE_TO_RADIAN * 0.5f * innerSpotAngle);
    25.     float outerCos = cos(DEGREE_TO_RADIAN * 0.5f * outerSpotAngle);
    26.     float angleRangeInv = 1.0f / max(innerCos - outerCos, 0.001f);
    27.  
    28.     light.color = color;
    29.     light.direction = normalize(ray);
    30.  
    31.     float distanceSqr     = max(MAGNITUDE_SQUARED(ray), 1e-5);
    32.     float spotAttenuation = square(saturate((dot(-lightDir, light.direction) - outerCos) * angleRangeInv));
    33.     float normalizedDist  = sqrt(distanceSqr) / range;
    34.  
    35.     light.attenuation = spotAttenuation * GeneralAttenuation(normalizedDist);
    36.  
    37.     return light;
    38. }
    The final fragment color will then be calculated like this:

    float3 final = albedoBackground * IncomingLight(normalWorld, lightSource) * intensity;


    It's close but it does not entirely match Unity's Spotlights as you can see here:

    SpotlightComparison.gif

    For a comparison without artifacts caused by the GIF:
    Spotlight_Unity.png Spotlight_Custom.png

    I am using deferred rendering of the built-in render pipeline in Unity 2021.3.28f1. My current fake spotlight implementation doesn't take specularity into account. So the PBR materials I am using have Metallic set to 0 and Smoothness to 1.

    The parameters I currently use for my fake spotlights are as follows:

    range                 : 22.22222
    intensity : 1
    color : white
    outer spotlight angle : 50
    inner spotlight angle : 35


    Question:

    All inner spotlight angles I've used so far, didn't make it look like Unity's version. Does anyone know what I am doing wrong?
     
    Last edited: Apr 30, 2024
  2. BrightBit

    BrightBit

    Joined:
    Jan 22, 2013
    Posts:
    270
    No one? Too much text? Am I missing something?
     
  3. POOKSHANK

    POOKSHANK

    Joined:
    Feb 8, 2022
    Posts:
    362
    have you tried looking in the shader code of the built in shaders? there should be examples of how they calculate them in there.

    ctrl-f "spot" to find it quickly

    //found example here, more may be elsewhere
    Unity-Built-in-Shaders/CGIncludes/UnityDeferredLibrary.cginc at master · TwoTailsGames/Unity-Built-in-Shaders · GitHub

    IMO, the difference doesn't really matter, as long as you can still use them as spotlights, no player will question it. i actually calculate spotlights wrong in my lightmapper, but they still look like spotlights so idrc.
     
  4. BrightBit

    BrightBit

    Joined:
    Jan 22, 2013
    Posts:
    270
    Hi POOKSHANK,

    thank you. I actually didn't think about looking there as I thought that the spotlight angle calculation won't happen there. However, I looked there now and found some more interesting details:

    Code (CSharp):
    1. float4 uvCookie = mul(unity_WorldToLight, float4(worldPos, 1));
    2. float spotlightAttenuation = tex2D(_LightTexture0, uvCookie.xy / uvCookie.w + float2(0.5, 0.5)).UNITY_ATTEN_CHANNEL;
    It unfortunately raises another question: How is unity_WorldToLight calculated?
     
  5. BrightBit

    BrightBit

    Joined:
    Jan 22, 2013
    Posts:
    270
    My fake spotlights fall apart if you get close to them, so it does matter in my case. I want to switch to "real" spotlights when the player is close.
     
  6. POOKSHANK

    POOKSHANK

    Joined:
    Feb 8, 2022
    Posts:
    362
    i can't seem to find any place in the shader code where they actually define it, only where they use it. does just using it work? note it's included on the built in shader variables page, so i would figure you can use it as is, unless your case is ultra wonky.

    we may need @bgolus on the scene. he seems to know everything about every shader include file lol.
     
  7. BrightBit

    BrightBit

    Joined:
    Jan 22, 2013
    Posts:
    270
    You can't. Its calculated in the engine for each light individually. That's why it's so difficult to find out. :)