Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Light distance in shader

Discussion in 'Shaders' started by Tabb, Dec 18, 2017.

  1. Tabb

    Tabb

    Joined:
    Jan 2, 2015
    Posts:
    40
    Hi,

    I found a planet shader below.
    I want the illuminated side to be more or less illuminated depending on the distance of a light source.
    I don't know how to do this at all ?

    Thank you for your help.

    Code (CSharp):
    1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    2.  
    3. Shader "Custom/SphereBody" {
    4.     Properties
    5.     {
    6.         _AtmosphereColor ("Atmosphere Color", Color) = (0.1, 0.35, 1.0, 1.0)
    7.         _AtmospherePow ("Atmosphere Power", Range(1.5, 8)) = 2
    8.         _AtmosphereMultiply ("Atmosphere Multiply", Range(1, 3)) = 1.5
    9.  
    10.         _DiffuseTex("Diffuse", 2D) = "white" {}
    11.        
    12.         _CloudAndNightTex("Cloud And Night", 2D) = "black" {}
    13.  
    14.         _LightDir("Light Dir", Vector) = (-1,0,0,1)
    15.     }
    16.  
    17.     SubShader
    18.     {
    19.         ZWrite On
    20.         ZTest LEqual
    21.  
    22.         pass
    23.         {
    24.         CGPROGRAM
    25.             #include "UnityCG.cginc"
    26.             #pragma vertex vert
    27.             #pragma fragment frag
    28.            
    29.             sampler2D _DiffuseTex;
    30.             sampler2D _CloudAndNightTex;
    31.  
    32.             float4 _AtmosphereColor;
    33.             float _AtmospherePow;
    34.             float _AtmosphereMultiply;
    35.  
    36.             float4 _LightDir;
    37.  
    38.             struct vertexInput
    39.             {
    40.                 float4 pos                : POSITION;
    41.                 float3 normal            : NORMAL;
    42.                 float2 uv                : TEXCOORD0;
    43.             };
    44.  
    45.             struct vertexOutput
    46.             {
    47.                 float4 pos            : POSITION;
    48.                 float2 uv            : TEXCOORD0;
    49.                 half diffuse        : TEXCOORD1;
    50.                 half night            : TEXCOORD2;
    51.                 half3 atmosphere    : TEXCOORD3;
    52.             };
    53.            
    54.             vertexOutput vert(vertexInput input)
    55.             {
    56.                 vertexOutput output;
    57.                 output.pos = UnityObjectToClipPos(input.pos);
    58.                 output.uv = input.uv;
    59.  
    60.                 output.diffuse = saturate(dot(_LightDir.xyz, input.normal) * 1.2);
    61.                 output.night = 1 - saturate(output.diffuse * 2);
    62.  
    63.                 half3 viewDir = normalize(ObjSpaceViewDir(input.pos));
    64.                 half3 normalDir = input.normal;
    65.                 output.atmosphere = output.diffuse * _AtmosphereColor.rgb * pow(1 - saturate(dot(viewDir, normalDir)), _AtmospherePow) * _AtmosphereMultiply;
    66.  
    67.                 return output;
    68.             }
    69.  
    70.             half4 frag(vertexOutput input) : Color
    71.             {
    72.                 half3 colorSample = tex2D(_DiffuseTex, input.uv).rgb;
    73.  
    74.                 half3 cloudAndNightSample = tex2D(_CloudAndNightTex, input.uv).rgb;
    75.                 half3 nightSample = cloudAndNightSample.ggb;
    76.                 half cloudSample = cloudAndNightSample.r;
    77.  
    78.                 half4 result;
    79.                 result.rgb = (colorSample + cloudSample) * input.diffuse + nightSample * input.night + input.atmosphere;
    80.  
    81.                 result.a = 1;
    82.                 return result;
    83.             }
    84.         ENDCG
    85.         }
    86.     }
    87.    
    88.     Fallback "Diffuse"
    89. }
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    You're currently passing a light direction via _LightDir, presumably meaning you're calculating the direction to a "light source" in script? If so you could simply use that .w component to pass a brightness.

    Otherwise you should be passing a light position rather than a direction and calculating both the direction and the distance in the shader.

    For the actual falloff, that's kind of up to you to decide what you want. In the "real world" light falls off with what's known as the inverse square law. However that equation is means the distance light affects something is infinite and the drop in brightness near the light source is quite quick. Real time engines usually use one of many bodges based on a light radius, the most basic one being a linear fall off.

    surface brightness = light color * clamp(1.0 - distance from light / light radius. 0.0, 1.0)

    Other common ones are some kind of quadratic equation or smoothstep, or using one of those to clamp the inverse square falloff.

    Unity uses a texture with this equation baked into it:
    float atten = saturate(1.0 / (1.0 + 25.0*normalizedDist*normalizedDist) * saturate((1 - normalizedDist) * 5.0));
    where normalizedDist = dist / range

    Otherwise you can sample the texture directly with:
    float atten = tex2D(_LightTextureB0, (normalizedDist * normalizedDist).xx).UNITY_ATTEN_CHANNEL;
     
    BrightBit, theteadrinker and AlejMC like this.
  3. Tabb

    Tabb

    Joined:
    Jan 2, 2015
    Posts:
    40
    I will probably use a simple linear fall off.
    But to be honest, I'm really not confortable with shaders code. I barely understand what does what.

    Let say I use the .w component to pass the brightness to the shader.
    How should I code to take this brightness into account ?

    I ran into another question when I realized that planets using this shader does not receive shadows.
    Is there a way to change that ? (Receive Shadows is checked in the Mesh Renderer.)
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Multiply the diffuse value by it?
    output.diffuse = saturate(dot(_LightDir.xyz, input.normal) * 1.2) * _LightDir.w;
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    This gets into the real weeds of the issue.

    You're not using Unity's existing lighting system since you're passing in a light direction manually. If you want shadows you either need to write an entire custom shadow system yourself, or start using Unity's lighting system.

    The simplest solution is to use Surface Shaders.
    I highly recommend this tutorial series to start getting your head around how shaders work.
    https://www.alanzucconi.com/2015/06/10/a-gentle-introduction-to-shaders-in-unity3d/