Detect obstacles on light sources

Hi everyone.
I am making a kind of toon shading style combining textures and normals (thanks to the Noob To Pro Writing Shaders from CGCOOKIE). I implemented the multiple light options by a second pass render, but my question is: How can I detect that there is an obstacle between the light source and my analized model?
Here is the image that explain that:


Between the lamp and the object there is a “wall”, so in real life there will be parts that the light will not reach the object, but on shaders I don’t know how to detect the “wall” that is inside the red box. If there is a defined variable that unity has for shaders that can detect other objects, is what I want to know.

Thanks for your answers :slight_smile:

You’re taking about shadows. If you’re using surface shaders you just need to add fullforwardshadows to the #pragma surface line.

If you’re using vertex fragment shaders it’s a bit more work, but not too bad. There’s an example for the base pass in the official documentation.

Adding shadows for the forward add pass is almost identical. You just need to include the same SHADOW lines and use multi_compile_fwdadd_fullshadows instead of multi_compile_fwdbase

Hi, thanks, the forwardbase shadows Works great but I am having problems with the forwardAdd shadows. I put in the second pass the multi_compile_fwdadd_fullshadows but I have an error message that says: “undeclared identifier UnityDecodeCobeShadowDepth at UnityShadowLibrary”, I look for that variable and it says that is related to cubemaps o_o, How can it be configure this kind of shadows?

Shouldn’t be anything you need to do particularly special in the shader code. Cube map shadows are used by point lights, and all the special handling should be done by those shadow macros (the lines all in caps). Best guess is this is problem caused by having missing #include lines.

Oh!, so I cannot use point lights on this?, because the light you see on the image is a point light.

No, you can use pointlights … once you fix the shader so it compiles properly. Post the whole shader here (inline in a code block, not as an attachment) and we can look at it, otherwise I can’t really say what’s wrong for sure.

Ok, it is too long but I am still learning how to optimize this, so here it is :):

Shader "UnityCookie/NoobToProTut/Intermediate/10 - Toon Map"
{
    Properties
    {
        _Color ("Lit Color", Color) = (1.0,1.0,1.0,1.0)
        _UnlitColor ("Unlit Color", Color) = (0.5,0.5,0.5,1.0)
        _MainTex ("Diffuse Texture", 2D) = "white" {}
        _BumpMap ("Normal Texture", 2D) = "bump" {}
        _BumpDepth ("Bump Depth", Range(0.0, 1.0)) = 1.0
        _DiffuseThreshold ("Lighting Threshold", Range(0.0, 1.0)) = 0.1        // Control the line transition between both colors
        _Diffusion ("Diffusion", Range(0.0, 1.0)) = 0.0                            // Control the blurry the line transition it is
        _SpecColor ("Specular Color", Color) = (1.0,1.0,1.0,1.0)
        _Shininess ("Shininess", Range(0.5, 1)) = 1
        _SpecDiffusion ("Specular Diffusion", Range(0, 0.99)) = 0.0                // Controls the blurry border line of the specular light
        _OutlineColor ("Outline Color", Color) = (0.0,0.0,0.0,1.0)
        _OutlineThickness ("Outline Thickness", Range(0.0, 1.0)) = 0.1
        _OutlineDiffusion ("Outline Diffusion", Range(0.0, 1.0)) = 0
    }
    SubShader
    {
        // FIRST RENDERER PASS
        Pass {
            Tags {"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // compile shader into multiple variants, with and without shadows
            // (we don't care about any lightmaps yet, so skip these variants)
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
            // shadow helper functions and macros
            #include "AutoLight.cginc"
            #include "UnityCG.cginc"

            //user defined variables
            uniform fixed4 _Color;
            uniform fixed4 _UnlitColor;
            uniform sampler2D _MainTex;
            uniform half4 _MainTex_ST;
            uniform sampler2D _BumpMap;
            uniform half4 _BumpMap_ST;
            uniform fixed _BumpDepth;
            uniform fixed _DiffuseThreshold;
            uniform fixed _Diffusion;
            uniform fixed4 _SpecColor;
            uniform fixed _Shininess;
            uniform fixed _SpecDiffusion;
            uniform fixed4 _OutlineColor;
            uniform fixed _OutlineThickness;
            uniform fixed _OutlineDiffusion;

            //unity defined variables
            uniform half4 _LightColor0;

            //base input structs
            struct vertexInput
            {
                half4 vertex : POSITION;
                half3 normal : NORMAL;
                half4 texcoord : TEXCOORD0;        // Gets the UV map from the texture on the object
                half4 tangent : TANGENT;        // Gets the tangent direction of the normals
            };
            struct vertexOutput
            {
                half4 pos : SV_POSITION;
                half4 tex : TEXCOORD0;
                SHADOW_COORDS(1) // put shadows data into TEXCOORD1           
                fixed3 normalDir : TEXCOORD2;
                fixed4 lightDir : TEXCOORD3;
                fixed3 viewDir : TEXCOORD4;
                fixed3 tangentDir : TEXCOORD5;    // Store the tangent direction of the normals
                fixed3 binomialDir : TEXCOORD6;    // Store the perpendicular direction between the normal and tangent
            };
           
            //vertex Function
            vertexOutput vert(vertexInput v)
            {
                vertexOutput o;
               
                //normalDirection
                o.normalDir = normalize( mul( half4( v.normal, 0.0 ), unity_WorldToObject ).xyz );

                // calculate directions for bump map
                o.tangentDir = normalize( mul( unity_ObjectToWorld, v.tangent).xyz);
                o.binomialDir = normalize( cross( o.normalDir, o.tangentDir) * v.tangent.w);

                // Store texture coordinates
                o.tex = v.texcoord;

                //unity transform position
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
               
                //world position
                half4 posWorld = mul(unity_ObjectToWorld, v.vertex);
                //view direction
                o.viewDir = normalize( _WorldSpaceCameraPos.xyz - posWorld.xyz );
                //light direction
                half3 vertexToLightSource = _WorldSpaceLightPos0.xyz - posWorld.xyz;
                o.lightDir = fixed4(
                    normalize( lerp(_WorldSpaceLightPos0.xyz , vertexToLightSource, _WorldSpaceLightPos0.w) ),
                    lerp(1.0 , 1.0/length(vertexToLightSource), _WorldSpaceLightPos0.w)
                );

                // compute shadows data
                TRANSFER_SHADOW(o)

                return o;
            }
           
            //fragment function
            fixed4 frag(vertexOutput i) : SV_TARGET
            {
                // Texture and Bump Maps
                fixed4 tex = tex2D(_MainTex, i.tex.xy * _MainTex_ST.xy + _MainTex_ST.zw);
                fixed4 texN = tex2D(_BumpMap, i.tex.xy * _BumpMap_ST.xy + _BumpMap_ST.zw);

                // Unpack Normal function
                fixed3 localCoords = fixed3( 2.0 * texN.ag - float2( 1.0, 1.0), _BumpDepth);

                // Normal transpose matrix
                fixed3x3 local2WorldTranspose = fixed3x3(
                    i.tangentDir,
                    i.binomialDir,
                    i.normalDir
                );

                // Calculate Bump Direction
                fixed3 bumpDirection = normalize( mul( localCoords, local2WorldTranspose));

                // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
                fixed shadow = smoothstep(0.0, 1.0, SHADOW_ATTENUATION(i));

                // Transition Masks
                fixed diffuseCutoff = saturate( smoothstep( _DiffuseThreshold, 1.0, i.lightDir.w * dot(bumpDirection, i.lightDir.xyz) * shadow) * pow( (2 - _Diffusion), 10));
                fixed specularCutoff = saturate( smoothstep( _Shininess, 1.0, i.lightDir.w * dot( reflect( -i.lightDir.xyz, bumpDirection), i.viewDir)) * shadow * pow( ( 2 - _SpecDiffusion), 10));

                // Calculate outlines
                fixed outlineStrenth = saturate( ( dot( i.normalDir, i.viewDir) - _OutlineThickness) * pow( ( 2 - _OutlineDiffusion), 10) + _OutlineThickness);
                fixed3 outlineOverlay = (_OutlineColor.xyz * _LightColor0.xyz * ( 1 - outlineStrenth)) + outlineStrenth;

                // Lighting
                fixed3 ambientLight = tex.xyz * ( 1 - diffuseCutoff) * _UnlitColor.xyz * (_LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz);
                fixed3 diffuseReflection = tex.xyz * (1 - specularCutoff) * _Color.xyz * _LightColor0.xyz * diffuseCutoff;
                fixed3 specularReflection = specularCutoff * _SpecColor.xyz * tex.a;

                fixed3 lightFinal = (ambientLight + diffuseReflection) * outlineOverlay + specularReflection;
               
                return fixed4(lightFinal, 1.0);
            }
           
            ENDCG       
        }

        // SECOND RENDERER PASS
        Pass {
            Tags {"LightMode" = "ForwardAdd"}
            Blend SrcAlpha OneMinusSrcColor
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdadd_fullshadows
            #include "AutoLight.cginc"
            #include "UnityCG.cginc"

            //user defined variables
            uniform fixed4 _Color;
            uniform fixed4 _UnlitColor;
            uniform sampler2D _MainTex;
            uniform half4 _MainTex_ST;
            uniform sampler2D _BumpMap;
            uniform half4 _BumpMap_ST;
            uniform fixed _BumpDepth;
            uniform fixed _DiffuseThreshold;
            uniform fixed _Diffusion;
            uniform fixed4 _SpecColor;
            uniform fixed _Shininess;
            uniform fixed _SpecDiffusion;
            uniform fixed4 _OutlineColor;
            uniform fixed _OutlineThickness;
            uniform fixed _OutlineDiffusion;

            //unity defined variables
            uniform half4 _LightColor0;

            //base input structs
            struct vertexInput
            {
                half4 vertex : POSITION;
                half3 normal : NORMAL;
                half4 texcoord : TEXCOORD0;       
                half4 tangent : TANGENT;       
            };
            struct vertexOutput
            {
                half4 pos : SV_POSITION;
                half4 tex : TEXCOORD0;           
                SHADOW_COORDS(1)       
                fixed3 normalDir : TEXCOORD2;
                fixed4 lightDir : TEXCOORD3;
                fixed3 viewDir : TEXCOORD4;
                fixed3 tangentDir : TEXCOORD5;
                fixed3 binomialDir : TEXCOORD6;
            };
           
            //vertex Function
            vertexOutput vert(vertexInput v)
            {
                vertexOutput o;
               
                //normalDirection
                o.normalDir = normalize( mul( half4( v.normal, 0.0 ), unity_WorldToObject ).xyz );

                // calculate directions for bump map
                o.tangentDir = normalize( mul( unity_ObjectToWorld, v.tangent).xyz);
                o.binomialDir = normalize( cross( o.normalDir, o.tangentDir) * v.tangent.w);

                // Store texture coordinates
                o.tex = v.texcoord;

                //unity transform position
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
               
                //world position
                half4 posWorld = mul(unity_ObjectToWorld, v.vertex);
                //view direction
                o.viewDir = normalize( _WorldSpaceCameraPos.xyz - posWorld.xyz );
                //light direction
                half3 vertexToLightSource = _WorldSpaceLightPos0.xyz - posWorld.xyz;
                o.lightDir = fixed4(
                    normalize( lerp(_WorldSpaceLightPos0.xyz , vertexToLightSource, _WorldSpaceLightPos0.w) ),
                    lerp(1.0 , 1.0/length(vertexToLightSource), _WorldSpaceLightPos0.w)
                );

                // compute shadows data
                TRANSFER_SHADOW(o)
               
                return o;
            }
           
            //fragment function
            fixed4 frag(vertexOutput i) : SV_TARGET
            {
                // Texture and Bump Maps
                fixed4 tex = tex2D(_MainTex, i.tex.xy * _MainTex_ST.xy + _MainTex_ST.zw);
                fixed4 texN = tex2D(_BumpMap, i.tex.xy * _BumpMap_ST.xy + _BumpMap_ST.zw);

                // Unpack Normal function
                fixed3 localCoords = fixed3( 2.0 * texN.ag - float2( 1.0, 1.0), _BumpDepth);

                // Normal transpose matrix
                fixed3x3 local2WorldTranspose = fixed3x3(
                    i.tangentDir,
                    i.binomialDir,
                    i.normalDir
                );

                // Calculate Bump Direction
                fixed3 bumpDirection = normalize( mul( localCoords, local2WorldTranspose));

                // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
                fixed shadow = smoothstep(0.0, 1.0, SHADOW_ATTENUATION(i));

                // Transition Masks
                fixed diffuseCutoff = saturate( smoothstep( _DiffuseThreshold, 1.0, i.lightDir.w * shadow * dot(bumpDirection, i.lightDir.xyz)) * pow( (2 - _Diffusion), 10));
                fixed specularCutoff = saturate( smoothstep( _Shininess, 1.0, i.lightDir.w * dot( reflect( -i.lightDir.xyz, bumpDirection), i.viewDir)) * shadow * pow( ( 2 - _SpecDiffusion), 10));

                // Calculate outlines
                fixed outlineStrenth = saturate( ( dot( i.normalDir, i.viewDir) - _OutlineThickness) * pow( ( 2 - _OutlineDiffusion), 10) + _OutlineThickness);
                fixed3 outlineOverlay = (_OutlineColor.xyz * ( 1 - outlineStrenth)) + outlineStrenth;

                // Lighting
                fixed3 ambientLight = tex.xyz * ( 1 - diffuseCutoff) * _UnlitColor.xyz;
                fixed3 diffuseReflection = tex.xyz * (1 - specularCutoff) * _Color.xyz * _LightColor0.xyz * diffuseCutoff;
                fixed3 specularReflection = specularCutoff * _SpecColor.xyz * _LightColor0.xyz * tex.a;

                fixed3 lightFinal = (ambientLight + diffuseReflection) * outlineOverlay + specularReflection;
               
                return fixed4(lightFinal, 1.0);
            }
           
            ENDCG       
        }

        // SHADOW CASTER RENDERING PASS
        Pass
        {
            Tags {"LightMode" = "ShadowCaster"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"

            //base input structs
            struct v2f
            {
                V2F_SHADOW_CASTER;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }

            half4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)
            }

            ENDCG
        }

    }
    //Fallback "Specular"
}

The order of #include lines can matter! Try swapping them so the #include “UnityCG.cginc” is before the #include “AutoLight.cginc” line in the add pass specifically, though it wouldn’t hurt to do it for both.

Oh yeah, it Works, Thanks!!!. Besides it is a little complicate for me to understand this kind of programming. I noticed some other details, when the object recieves a shadow I notice that this shadow is a little sharpy, different what I have on the lighting threshold transition. As you can see on the code, I try to put a smoothstep function on the “shadow” variable, but the recieve shadow still seems sharpy, and the bump map doesn’t follow the shadow at all. How can I change the shadow variable to work as the “diffuseCutoff” that has that smooth chagne between the shadow and the light?

Not really, no.

You should read up on shadow maps. Cat like coding has a decent tutorial delving deep into shadow maps in Unity. You can try turning on soft shadows on your light to help some, however Unity’s point light shadows in particular are kind of terrible and always a little harsh. There are some assets out there that soften the shadows at the cost of performance and / or noise.