Search Unity

Easiest way to change Point Light attenuation ? (with Deferred path)

Discussion in 'Shaders' started by manutoo, Jun 29, 2014.

  1. manutoo

    manutoo

    Joined:
    Jul 13, 2010
    Posts:
    522
    Hello,

    I'm trying to change the Point Light attenuation from 1/(1+25*d^2) to 1/(1+25*d).

    In "Unity\Editor\Data\CGIncludes\AutoLight.cginc", I found out the attenuation macro uses the texture "_LightTexture0" to calculate the attenuation from d^2 , like this :
    Code (CSharp):
    1.   #define LIGHT_ATTENUATION(a)   (tex2D(_LightTexture0, dot(a._LightCoord,a._LightCoord).rr).UNITY_ATTEN_CHANNEL * SHADOW_ATTENUATION(a))
    So I did this in the Start() of my script attached to my camera :
    Code (CSharp):
    1.        Texture2D AttenTex = new Texture2D(256, 1, TextureFormat.RGB24, false);
    2.        Color[] AttenColor = new Color[256];
    3.  
    4.        for (int i = 0; i < 256; ++i)
    5.        {
    6.          float v = 1.0f; // Test with full white for now
    7.          AttenColor = new Color(v, v, v);
    8.        }
    9.  
    10.        AttenTex.SetPixels(AttenColor);
    11.        AttenTex.Apply();
    12.        Shader.SetGlobalTexture("_LightTexture0", AttenTex);
    It didn't change anything.
    I also tried to call SetGlobalTexture() from OnPreCull() & OnPreRender(), but still no result.

    So I tried to change the name of the texture in "AutoLight.cginc" and SetGlobalTexture(), but still nothing.

    I also tested to change the texture name in "AutoLight.cginc", and then use a renamed copy of the built-in shader "Normal-BumpSpec.shader", and still no change in the attenuation, either with "AutoLight.cginc" in
    "Unity\Editor\Data\CGIncludes" or in the copied shader folder. It's becoming very mysterious... >_<

    Is there any way to make this work ?
    I'd like to avoid to have to re-write all the standard shaders I'm using (Bump Specular, etc...)

    Note: I'm using deferred & linear space rendering.

    Twin question on Answers : http://answers.unity3d.com/questions/736758/easiest-way-to-change-point-light-attenuation.html
     
    tsukimi likes this.
  2. glacius3000

    glacius3000

    Joined:
    Oct 5, 2012
    Posts:
    69
    You can overwrite AutoLight.cginc. Copy it, put it your Resources folder, restart unity. Unity will now use the AutoLight.cginc in your resources folder. Finally, make any changed you need to Resources\AutoLight.cginc.
     
    tsukimi and mahdi_jeddi like this.
  3. kebrus

    kebrus

    Joined:
    Oct 10, 2011
    Posts:
    415
    I don't think you are supposed to set that texture directly via script, that texture is where unity stores the light attenuation, it doesn't use it to calculate anything, it uses it to store the calculations, instead of writing in it try reading it

    You can however change the calculation using the the method glacius3000 said

    Btw, you can check the prepass lighting builtin shader to check for where it calculates the light attenuation for the deferred pass
     
  4. manutoo

    manutoo

    Joined:
    Jul 13, 2010
    Posts:
    522
    glacius3000,
    I had already checked that AutoLight.cginc was used by my built-in shader copy (simply by adding a line that creates an error : "toto;" => "Shader error in 'Bumped Specular (Too)': syntax error, unexpected ';' at token ";" type name expected at token ";" at line 18").
    But to be sure, I just tested again by putting it in Resources, and it didn't change anything.
    I checked and the macro LIGHT_ATTENUATION() is actually not used in any other .cginc , so I have some doubt about when and how it's used. (I can see it's used in non-surface shaders I found with Google, though).
    I also tested with Forward rendering (with the same no-result), to be sure I wasn't touching something that didn't matter for Deferred path, because there's no "atten" for the deferred path here http://docs.unity3d.com/Manual/SL-SurfaceShaderLighting.html , so it's really not clear how it's done.

    kebrus,
    the surface shaders use some common lighting model, and thus the attenuation isn't visible in the shaders themselves.
    Unity does create its attenuation map in the way I did in my 1st post, but using "v = 1.0f / (1.0f + 25.0f * i / 255.0f)" (if I understood everything correctly ;) ). This is a texture look-up & avoid the division inside the shader (as shown by the macro I quoted at very start of my 1st post).
    So creating an alternate version of this texture should be the easiest way to modify the attenuation, if Unity can let us do that... In my case, it'd give : "v = 1.0f / (1.0f + 25.0f * Mathf.Sqrt(i / 255.0f))".
     
    Last edited: Jun 29, 2014
  5. kebrus

    kebrus

    Joined:
    Oct 10, 2011
    Posts:
    415
    I didn't know that, where does it say it is a lookup texture? if it is i'd be interested in changing it too
     
  6. glacius3000

    glacius3000

    Joined:
    Oct 5, 2012
    Posts:
    69
    Ok. ran some quick tests. For some reason modifying autolight isn't working. However, if you are using deferred then you can still change the Internal-prepasslighting file. Around line 228 you'll see this
    Code (CSharp):
    1. float att = dot(tolight, tolight) * _LightPos.w;
    change that to
    Code (CSharp):
    1. float att = tolight * _LightPos.w;
    or w/e suits your needs.

    Quick note: make sure the builtin shader files you are using match your version of Unity.
     
  7. manutoo

    manutoo

    Joined:
    Jul 13, 2010
    Posts:
    522
    glacius3000,
    thanks, that was the good file !
    A little side note : to get a rough approximate of the normalized distance instead of the squared distance, one could do:
    Code (CSharp):
    1.         float att = 1 - dot(tolight, tolight) * _LightPos.w;
    2.         att = 1 - att * att;
    to avoid to use the sqrt() function, as "float att = tolight * _LightPos.w;" doesn't really give a distance... ;)
    But actually, I found out I had to square the dot to get more light (as it was my final goal), so it was even easier => att = att * att ..! :p


    kebrus,
    I had found out it's a texture lookup by reading here :
    - http://en.wikibooks.org/wiki/Cg_Programming/Unity/Light_Attenuation
    - http://forum.unity3d.com/threads/light-attenuation-function.71158/
    and by checking the LIGHT_ATTENUATION() macro (although it seems it's not used anymore)


    So here the Easiest way to change Point Light attenuation ! (for the Deferred Path)

    0- Note: I'm using Unity 4.5.0f6 ; some things may change in future versions

    1- Download the built-in shader package matching your Unity version from here : http://unity3d.com/unity/download/archive/

    2- Extract "DefaultResources/Internal-PrePassLighting.shader" to "<YourProjectFolder>/Assets/Resources" or in a subfolder ; personally, I put it in "Resources/Shaders".

    3- Restart Unity, else the shader isn't used by the Editor renderer (as explained here : http://docs.unity3d.com/Manual/RenderTech-DeferredLighting.html )

    4- 1st option : change the calculation in this function "half4 CalculateLight (v2f i)" of Internal-PrePassLighting.shader .
    =====
    EDIT: with Unity 5, it must be done in UnityDeferredCalculateLightParams() in UnityDeferredLibrary.cginc
    You also have to go to Edit/ProjectSettings/Graphics and set Deferred to Custom shader and then link your custom Internal-DeferredShading shader.
    =====

    If like me, you want the point light to have less attenuation, you add the last line here :
    Code (CSharp):
    1.     #if defined (POINT) || defined (POINT_COOKIE)
    2.         float3 tolight = wpos - _LightPos.xyz;
    3.         half3 lightDir = -normalize (tolight);
    4.  
    5.         float att = dot(tolight, tolight) * _LightPos.w;
    6.         att = att * att;
    Instead of "att * att", you can put something else ; "sqrt(att)" will make everything more dark, and "att * att * att" will make everything even more clear

    5- 2nd option : change the lookup texture
    This option is interesting if you want something more complex. But it won't work for cookie light (it'd need some other stuff, I didn't look in details).
    a- in Internal-PrePassLighting.shader, under :
    Code (CSharp):
    1. _LightTextureB0 ("", 2D) = "" {}
    add :
    Code (CSharp):
    1. _LightTextureB02 ("", 2D) = "" {}
    then replace this line :
    Code (CSharp):
    1. sampler2D _LightTextureB0;
    by this one :
    Code (CSharp):
    1. sampler2D _LightTextureB0, _LightTextureB02;
    then lastly, for the point light, change this line :
    Code (CSharp):
    1. float atten = tex2D (_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;
    to :
    Code (CSharp):
    1. float atten = tex2D (_LightTextureB02, att.rr).UNITY_ATTEN_CHANNEL;
    Then in a monobehavior C# script, in its OnStart() function, add this :
    Code (CSharp):
    1.         //=== Point Light Attenutation
    2.         Texture2D m_AttenTex = new Texture2D(256, 1, TextureFormat.ARGB32, false, true);
    3.         m_AttenTex.filterMode = FilterMode.Bilinear;
    4.         m_AttenTex.wrapMode = TextureWrapMode.Clamp;
    5.         Color[] AttenColor = new Color[256];
    6.  
    7.         for (int i = 0; i < 256; ++i)
    8.         {
    9.             float v;
    10.  
    11.             if (i < 255)
    12.             {
    13.                 v = i / 255.0f;
    14.                 v = 1.0f / (1.0f + 25.0f * v);
    15.             }
    16.             else
    17.                 v = 0.0f;
    18.  
    19.             AttenColor[i] = new Color(v, v, v, v);
    20.         }
    21.  
    22.         m_AttenTex.SetPixels(AttenColor);
    23.         m_AttenTex.Apply();
    24.         Shader.SetGlobalTexture("_LightTextureB02", m_AttenTex);
    This is exactly the same attenuation texture than the default one created by Unity (except they probably don't have 256 pixels in it, but 16 or 32, I guess), so tune it to your liking.
    To match the above example to lower the attenuation, here what it'd give :
    Code (CSharp):
    1. v = 1.0f / (1.0f + 25.0f * v * v);
    Note: if don't use linear space lighting (ie: you use default gamma space), you may need to use this call instead :
    Code (CSharp):
    1. new Texture2D(256, 1, TextureFormat.ARGB32, false);
     
    Last edited: Jun 12, 2015
    st-VALVe, ModLunar, NeatWolf and 6 others like this.
  8. mriegger

    mriegger

    Joined:
    Mar 28, 2009
    Posts:
    7
    Very useful, thanks manutoo!
     
  9. jaywinyeah

    jaywinyeah

    Joined:
    Mar 12, 2014
    Posts:
    4
    That's exactly what I was looking for manutoo! I tried out the formula in the shader and it matches the built-in texture until the edge of the light radius, and then falls off too steeply. The texture width should be 4 so that the transition to black is more gradual. Here's the shader function that I wrote to test. I had to pass the light radius using a shader constant because I didn't know if it was exposed.

    float unityLightAttenuation(float fLightDistance, float fLightRadius)
    {
    float fRatio = saturate(fLightDistance / fLightRadius);
    fRatio *= fRatio;
    return 1 / (1 + 25 * fRatio) * saturate(4 * (1 - fRatio)); //fade to black as if 4 pixel texture
    }

    Here's simulating a texture width of 256.
    Capture256.PNG

    Here's simulating a texture width of 4, as in the shader code above. Capture4.PNG
     
  10. Blinkmann

    Blinkmann

    Joined:
    Nov 12, 2013
    Posts:
    5
    Do you know how to change that in unity 5? Because the "DefaultResources/Internal-PrePassLighting.shader" is missing in built-in shader package for unity 5.0.1
     
  11. manutoo

    manutoo

    Joined:
    Jul 13, 2010
    Posts:
    522
    @Blinkmann,
    this file has been split in 2 : CGIncludes/UnityDeferredLibrary.cginc & DefaultResourcesExtra/Internal-PrePassLighting.shader
    And the function UnityDeferredCalculateLightParams() in UnityDeferredLibrary.cginc is where the action takes place ! ;)
     
    OCASM likes this.
  12. Blinkmann

    Blinkmann

    Joined:
    Nov 12, 2013
    Posts:
    5
    Thank you very much!
     
  13. susiel

    susiel

    Joined:
    May 22, 2015
    Posts:
    1
    Thanks for the sharing.
    I'm a beginner in Unity. I have questions....
    Where does Unity put the tex image of LightTexture0 ?

    I have looked through the code of "DefaultResources/Internal-PrePassLighting.shader", it has 3 properties,
    Properties {
    _LightTexture0 ("", any) = "" {}
    _LightTextureB0 ("", 2D) = "" {}
    _ShadowMapTexture ("", any) = "" {}
    }
    so the _LightTexture0 variable must be the input of the internal shader, but how does it generated in Unity? Is it just realtime calculating, or it put the result image file in the related assets file path just like the baked lightMap's *.exr file ?


    ----------
    I google it...
    http://docs.unity3d.ru/Components/class-Light.html
    There is a "Attenuate" check box in the Inspector Window... I don't know which version of unity it is?
     
    Last edited: May 22, 2015
  14. manutoo

    manutoo

    Joined:
    Jul 13, 2010
    Posts:
    522
    @susiel,
    this texture is likely generated at real time with the code I put in my message (but only with 4 pixels as mentioned by jaywinyeah ).
     
  15. OCASM

    OCASM

    Joined:
    Jan 12, 2011
    Posts:
    328
    This is awesome and works great:



    However, when it comes to interacting with reflection probes... not so much:



    Anybody knows how to fix this?

    I tried both methods shown above and the reflection probes ignore them both.
     
  16. lsalaun

    lsalaun

    Joined:
    Mar 28, 2013
    Posts:
    2
    For any one stumbling on this post:

    Just in case it was not clear enough and since we spent some time figuring this one out: Do not forget to go to Edit/ProjectSettings/Graphics and set Deferred to Custom shader and then link your custom Internal-DeferredShading shader.
    We put that shader and the UnityDeferredLibrary inside the resources folder.

    Hope that helps some of you.

    L.
     
  17. manutoo

    manutoo

    Joined:
    Jul 13, 2010
    Posts:
    522
    @lsalaun,
    I just added your explanation to my little tutorial ! :)
     
  18. brwnryce

    brwnryce

    Joined:
    Jun 5, 2014
    Posts:
    1
    I set the Custom Shader to my copy of the Internal-DeferredShading, yet it seems to have no effect on the lights. I have UnityDeferredLibrary in the resources folder as well, and restarting didnt change anything.

    Anyone know why this might not be working?
     
  19. Jesus

    Jesus

    Joined:
    Jul 12, 2010
    Posts:
    502
    I'm having a similar issue I think.

    Is there some step that the tutorial missed perhaps?

    Using Unity 5.3 if that makes any difference.
     
  20. Fergicide

    Fergicide

    Joined:
    Mar 19, 2016
    Posts:
    21
    I needed to soften the sharp and abrupt spotlight attenuation (player torch) in my 5.4 project, and this topic gave me the answer. Thank you to all contributors.

    I am posting my summarized step-by-step as both a note to myself and for the benefit of others...


    Unity's default light falloff/attenuation is too sharp and abrupt, making things jump out of the darkness unnaturally.

    To make the attenuation softer, more gradual, and more natural do this:
      1. Go to Unity site, select your version, and download the shader sources: https://unity3d.com/get-unity/download/archive
      2. Create a project folder: Assets/Resources/Shaders
      3. Get two files "Internal-DeferredShading.shader" and "UnityDeferredLibrary.cginc" from the shader source archive and place them in the Resources folder from Step 2.
      4. Visit Unity menu: EDIT > PROJECT SETTINGS > GRAPHICS; for "built-in shader settings" change "Deferred" to "custom shader" and point it to "Internal-DeferredShading.shader" from Step 3.
      5. Now modify file "UnityDeferredLibrary.cginc". Locate function "UnityDeferredCalculateLightParams". Modify line:

        float att = dot(tolight, tolight) * _LightPos.w;

        to this:

        float att = 1 - dot(tolight, tolight) * _LightPos.w;
        att = 1 - att * att * att;
    Manutoo -- I was getting some artifacts with att * att. Cubing not only got rid of the artifacts but gave me the smoothest falloff I could wish for :)
     
    moco2k likes this.
  21. manutoo

    manutoo

    Joined:
    Jul 13, 2010
    Posts:
    522
    @Fergicide,
    pow 2 or pow 3 depends of your environments ; in mine, there wasn't too much reflection so pow 3 was looking unnatural. If you have metallic walls & stuff like that, pow 3 is probably a must, though !:)
     
  22. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    anybody manage to get the custom falloff working on reflection probe and baked lightmaps?
     
  23. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Yup. I bake my own probe and totally ignore Unity's way. :)
     
  24. slumberface

    slumberface

    Joined:
    May 22, 2016
    Posts:
    15
    Hm having trouble getting this to work in Unity 2017.3.1f1. My intention is to emulate toon style lighting (a crisp bright zone, a uniform fall off ring with no fade/gradient).

    I've downloaded the shaders (UnityDeferredLibrary, Internal-DeferredShading, Internal-PrePassLighting) and copied them into a project folder (assets/resources/shaders), but it does not seem to be affecting a point light in any way, even after setting the custom shaders in the graphics tab. (I am also specifically modifying the point light case section of the UnityDeferredLibrary script)

    Is anyone getting this to work correctly Unity 2017? Or might have an idea of what I'm doing wrong?
    Thank you for your efforts!
     
    CGDever likes this.