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

Hello,

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

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 :

  #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 :

       Texture2D AttenTex = new Texture2D(256, 1, TextureFormat.RGB24, false);
       Color[] AttenColor = new Color[256];

       for (int i = 0; i < 256; ++i)
       {
         float v = 1.0f; // Test with full white for now
         AttenColor = new Color(v, v, v);
       }

       AttenTex.SetPixels(AttenColor);
       AttenTex.Apply();
       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 : Easiest way to change Point Light attenuation ? - Questions & Answers - Unity Discussions

1 Like

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.

2 Likes

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

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 Unity - Manual: Custom lighting models in Surface Shaders , 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 :wink: ). 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))”.

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

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

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

change that to

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.

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:

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

to avoid to use the sqrt() function, as “float att = tolight * _LightPos.w;” doesn’t really give a distance… :wink:
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 …! :stuck_out_tongue:

kebrus,
I had found out it’s a texture lookup by reading here :

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 “/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 :

    #if defined (POINT) || defined (POINT_COOKIE)
        float3 tolight = wpos - _LightPos.xyz;
        half3 lightDir = -normalize (tolight);

        float att = dot(tolight, tolight) * _LightPos.w;
        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 :

_LightTextureB0 ("", 2D) = "" {}

add :

_LightTextureB02 ("", 2D) = "" {}

then replace this line :

sampler2D _LightTextureB0;

by this one :

sampler2D _LightTextureB0, _LightTextureB02;

then lastly, for the point light, change this line :

float atten = tex2D (_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;

to :

float atten = tex2D (_LightTextureB02, att.rr).UNITY_ATTEN_CHANNEL;

Then in a monobehavior C# script, in its OnStart() function, add this :

        //=== Point Light Attenutation
        Texture2D m_AttenTex = new Texture2D(256, 1, TextureFormat.ARGB32, false, true);
        m_AttenTex.filterMode = FilterMode.Bilinear;
        m_AttenTex.wrapMode = TextureWrapMode.Clamp;
        Color[] AttenColor = new Color[256];

        for (int i = 0; i < 256; ++i)
        {
            float v;

            if (i < 255)
            {
                v = i / 255.0f;
                v = 1.0f / (1.0f + 25.0f * v);
            }
            else
                v = 0.0f;

            AttenColor[i] = new Color(v, v, v, v);
        }

        m_AttenTex.SetPixels(AttenColor);
        m_AttenTex.Apply();
        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 :

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 :

new Texture2D(256, 1, TextureFormat.ARGB32, false);
9 Likes

Very useful, thanks manutoo!

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.

Here’s simulating a texture width of 4, as in the shader code above.

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

@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 ! :wink:

1 Like

Thank you very much!

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?

@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 ).

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.

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.

@lsalaun ,
I just added your explanation to my little tutorial ! :slight_smile:

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?

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.

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:

  • Go to Unity site, select your version, and download the shader sources: Download Archive

  • Create a project folder: Assets/Resources/Shaders

  • Get two files “Internal-DeferredShading.shader” and “UnityDeferredLibrary.cginc” from the shader source archive and place them in the Resources folder from Step 2.

  • 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.

  • 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 :slight_smile:

1 Like