Search Unity

Get the range of a point light in forward add mode.

Discussion in 'Shaders' started by cjddmut, Nov 26, 2013.

  1. cjddmut

    cjddmut

    Joined:
    Nov 19, 2012
    Posts:
    120
    In order to calculate the light attenuation from a point light to mimic the same effect of Unity's built in shader, I need the range of a point light. I found the following global:

    Code (csharp):
    1.  
    2. uniform float4 _LightPositionRange; // xyz = pos, w = 1/range
    3.  
    However _LightPositionRange.w appears to always be zero for me. Is there something I need to have turned on via pragma or another way to obtain the range of the point light in the forward add pass?

    Thanks!
    C.J.
     
  2. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    There's no easy way to get the range as a value, but you can get the attenuation value easily enough. It's exactly the same as you get the value in Forward Base mode.

    Code (csharp):
    1. Shader "Forward Shadows Shader" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _MainTex ("Diffuse (RGB)", 2D) = "white" {}
    5.     }
    6.     SubShader {
    7.         Tags {"Queue" = "Geometry" "RenderType" = "Opaque" "IgnoreProjector" = "True"}
    8.  
    9.         Pass {
    10.             // Need to have it know it's to be used as a forward base pass.
    11.             Tags {"LightMode" = "ForwardBase"}
    12.             CGPROGRAM
    13.                 #pragma vertex vert
    14.                 #pragma fragment frag
    15.                 // Need to have it know it's compiling it as forward base pass.
    16.                 #pragma multi_compile_fwdbase
    17.                 #pragma fragmentoption ARB_precision_hint_fastest
    18.                
    19.                 #include "UnityCG.cginc"
    20.                 // This includes the required macros for shadow and attenuation values.
    21.                 #include "AutoLight.cginc"
    22.  
    23.                 struct v2f {
    24.                     float4  pos         : SV_POSITION;
    25.                     float2  uv          : TEXCOORD0;
    26.                     float3  normal      : TEXCOORD1;
    27.                     float3  lightDir    : TEXCOORD2;
    28.                     LIGHTING_COORDS(3,4) // This fills the TEXCOORD of the given numbers - TEXCOORD3 and TEXCOORD4 in this case - with the values required for the vertex shader to send to the fragment shader.
    29.                 };
    30.  
    31.                 float4 _MainTex_ST;
    32.  
    33.                 v2f vert (appdata v)
    34.                 {
    35.                     v2f o;
    36.                    
    37.                     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    38.                     o.uv = TRANSFORM_TEX (v.texcoord, _MainTex).xy;
    39.                     o.normal = v.normal.xyz;
    40.                     o.viewDir = ObjSpaceViewDir (v.vertex).xyz;
    41.                     o.lightDir = ObjSpaceLightDir (v.vertex).xyz;
    42.                     TRANSFER_VERTEX_TO_FRAGMENT(o);  // This lets the fragment shader calculate the the attenuation + shadow value.
    43.                     return o;
    44.                 }
    45.  
    46.                 sampler2D _MainTex;
    47.                 fixed4 _LightColor0;
    48.  
    49.                 fixed4 frag(v2f i) : COLOR
    50.                 {
    51.                     fixed atten = LIGHT_ATTENUATION(i); // This gets you the attenuation + shadow value.
    52.                     fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
    53.                     fixed NdotL = saturate(dot(i.normal, i.lightDir));
    54.                     fixed4 c;
    55.                     c.rgb = (UNITY_LIGHTMODEL_AMBIENT.rgb * albedo) * _UnityAmbientStrength * 2;
    56.                     c.rgb += (NdotL * atten * 2) * (_LightColor0.rgb * albedo);
    57.                     c.a = 1.0;
    58.                     return c;
    59.                 }
    60.             ENDCG
    61.         }
    62.  
    63.         Pass {
    64.             // Need to have it know it's to be used as a forward add pass.
    65.             Tags {"LightMode" = "ForwardAdd"}
    66.             // And it's additive to the base pass.
    67.             Blend One One
    68.             CGPROGRAM
    69.                 #pragma vertex vert
    70.                 #pragma fragment frag
    71.                 // Need to have it know it's compiling it as forward add pass.
    72.                 // Alternatively, you can uncomment the line below and uncomment the line below that to tell it that the forward add pass should have shadows calculated rather than just attenuation.
    73.                 #pragma multi_compile_fwdadd
    74.                 // #pragma multi_compile_fwdadd_fullshadows
    75.                 #pragma fragmentoption ARB_precision_hint_fastest
    76.                
    77.                 #include "UnityCG.cginc"
    78.                 // This includes the required macros for shadow and attenuation values.
    79.                 #include "AutoLight.cginc"
    80.  
    81.                 float4 _MainTex_ST;
    82.  
    83.                 struct v2f {
    84.                     float4  pos         : SV_POSITION;
    85.                     float2  uv          : TEXCOORD0;
    86.                     float3  normal      : TEXCOORD1;
    87.                     float3  lightDir    : TEXCOORD2;
    88.                     LIGHTING_COORDS(3,4) // This fills the TEXCOORD of the given numbers - TEXCOORD3 and TEXCOORD4 in this case - with the values required for the vertex shader to send to the fragment shader.
    89.                 };
    90.  
    91.                 v2f vert (appdata v)
    92.                 {
    93.                     v2f o;
    94.                    
    95.                     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    96.                     o.uv = TRANSFORM_TEX (v.texcoord, _MainTex).xy;
    97.                     o.normal = v.normal.xyz;
    98.                     o.viewDir = ObjSpaceViewDir (v.vertex).xyz;
    99.                     o.lightDir = ObjSpaceLightDir (v.vertex).xyz;
    100.                     TRANSFER_VERTEX_TO_FRAGMENT(o);  // This lets the fragment shader calculate the the attenuation + shadow value.
    101.                     return o;
    102.                 }
    103.  
    104.                 sampler2D _MainTex;
    105.                 fixed4 _LightColor0;
    106.  
    107.                 fixed4 frag(v2f i) : COLOR
    108.                 {
    109.                     fixed atten = LIGHT_ATTENUATION(i); // This gets you the attenuation + shadow value.
    110.                     fixed4 c;
    111.                     c.rgb = (saturate(dot(i.normal, i.lightDir)) * atten * 2) * (_LightColor0.rgb * tex2D(_MainTex, i.uv).rgb);
    112.                     c.a = 1.0;
    113.                     return c;
    114.                 }
    115.             ENDCG
    116.         }
    117.     }
    118.     // Must have some fallback here that (eventually, from fallbacks of fallbacks) contains a shadow caster and reciever pass if you want shadows.
    119.     // Alternatuvely you can just throw in your own shadow caster/reciever passes at the end of the subshader.
    120.     Fallback "Mobile/VertexLit"
    121. }
     
  3. cjddmut

    cjddmut

    Joined:
    Nov 19, 2012
    Posts:
    120
    Thanks again! That took me some time to digest and figure out what was going on but I have a feel for it now.

    One thing I wouldn't mind confirming. It appears that the w channel of _LightTexture0 is effectively a look up to get the attenuation value? Is this done so a shader wouldn't need to compute the attenuation on every pixel (which I believe is 1 / (1 + 25 * r * r) where r is length to the vertex in light space)?

    While figuring this out I realized that the light space (for point lights) ranges from 0 (origin) to 1 (max range) for all three channels. With this realization I figured out how to calculate the range of a point light within the shader. In case anyone would need the range value instead of just looking up the attenuation value in _LightTexture0 (maybe you want your own attenuation function). The following will get you the range:

    Code (csharp):
    1.  
    2. float4 vertexWorld = mul(_Object2World, v.vertex);
    3. float3 lightCoord = mul(_LightMatrix0, vertexWorld).xyz;
    4. float3 toLight = _WorldSpaceLightPos0.xyz - vertexWorld.xyz;
    5.  
    6. float range = length(toLight) / length(lightCoord);
    7.  
    8. float rangeSq = dot(toLight, toLight) / dot(lightCoord, lightCoord);
    9.  
    Getting closer :).
     
    Last edited: Nov 26, 2013
    Sarkahn, NeatWolf and n00body like this.
unityunity