Search code examples
c#unity-game-engineshader

Is there a way to hide a player who is in the shadow in Unity 2D Light System?


I want to recreate the field of view effect in the game Among Us in Unity by using the Universal Pipeline and 2D Lighting system.

But when I use 2D light and Shadow, I still see the character in the shadow, and I don't want to have the shadow completely black because I want to recreate the look as in the game Among Us where the background still has some light on it.

Here is what I mean by Among Us Field of View Among Us in Unity Field of View

As you can see there are shadows, the background is lightly light, and the character's body is half in shadow which is invisible and half in the light where they are visible.

Here is What I mean By Player is Blacked Out enter image description here

There are a couple of YouTube videos about it, but the view becomes sharp and there is no fallout at the edges of the view as with 2D lights. Which really gives it a nice look.

I need help with that, is there away to do that in Unity using 2D Lights and Shadows? And if so how do I go about doing that?

Thank you in advance for your help, all is appreciated.


Solution

  • It took me a while to figure it out, and tbh I wasn't the one who figured it out, I got help from someone on the Unity Discord server. He went into the Sprite-Lit-Default and changed some code that accounts for the luminance, now I don't know anything about shader programming so I won't be able to explain it much. But he created a .hlsl file with the same functionality as the CombinedShapeLightShared.hlsl file which Unity uses to calculate lights, can be found here, but added some argument that accounts for luminance. Now here are the file content of both the .shader file and the .hlsl file.

    But YOU NEED TO KEEP BOTH FILES IN THE SAME FOLDER, OTHERWISE IT WONT WORK. ALSO KEEP THE FILE NAMES THE SAME, UNLESS YOU KNOW WHAT YOU ARE DOING

    File name HideInShadow.shader

    Shader "Universal Render Pipeline/2D/HideInShadow"
    {
        Properties
        {
            _MainTex("Diffuse", 2D) = "white" {}
            _MaskTex("Mask", 2D) = "white" {}
            _NormalMap("Normal Map", 2D) = "bump" {}
    
            // Legacy properties. They're here so that materials using this shader can gracefully fallback to the legacy sprite shader.
            [HideInInspector] _Color("Tint", Color) = (1,1,1,1)
            [HideInInspector] _RendererColor("RendererColor", Color) = (1,1,1,1)
            [HideInInspector] _Flip("Flip", Vector) = (1,1,1,1)
            [HideInInspector] _AlphaTex("External Alpha", 2D) = "white" {}
            [HideInInspector] _EnableExternalAlpha("Enable External Alpha", Float) = 0
        }
    
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        ENDHLSL
    
        SubShader
        {
            Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" }
    
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off
            ZWrite Off
    
            Pass
            {
                Tags { "LightMode" = "Universal2D" }
                HLSLPROGRAM
                #pragma vertex CombinedShapeLightVertex
                #pragma fragment CombinedShapeLightFragment
                #pragma multi_compile USE_SHAPE_LIGHT_TYPE_0 __
                #pragma multi_compile USE_SHAPE_LIGHT_TYPE_1 __
                #pragma multi_compile USE_SHAPE_LIGHT_TYPE_2 __
                #pragma multi_compile USE_SHAPE_LIGHT_TYPE_3 __
    
                struct Attributes
                {
                    float3 positionOS   : POSITION;
                    float4 color        : COLOR;
                    float2  uv           : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };
    
                struct Varyings
                {
                    float4  positionCS  : SV_POSITION;
                    half4   color       : COLOR;
                    float2  uv          : TEXCOORD0;
                    half2   lightingUV  : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };
    
                #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/LightingUtility.hlsl"
    
                TEXTURE2D(_MainTex);
                SAMPLER(sampler_MainTex);
                TEXTURE2D(_MaskTex);
                SAMPLER(sampler_MaskTex);
                half4 _MainTex_ST;
    
                #if USE_SHAPE_LIGHT_TYPE_0
                SHAPE_LIGHT(0)
                #endif
    
                #if USE_SHAPE_LIGHT_TYPE_1
                SHAPE_LIGHT(1)
                #endif
    
                #if USE_SHAPE_LIGHT_TYPE_2
                SHAPE_LIGHT(2)
                #endif
    
                #if USE_SHAPE_LIGHT_TYPE_3
                SHAPE_LIGHT(3)
                #endif
    
                Varyings CombinedShapeLightVertex(Attributes v)
                {
                    Varyings o = (Varyings)0;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    
                    o.positionCS = TransformObjectToHClip(v.positionOS);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    o.lightingUV = ComputeNormalizedDeviceCoordinates(o.positionCS);
                    o.color = v.color;
                    return o;
                }
    
                #include "CombinedShapeLightSharedHidden.hlsl"
    
                half4 CombinedShapeLightFragment(Varyings i) : SV_Target
                {
                    half4 main = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
                    half4 mask = SAMPLE_TEXTURE2D(_MaskTex, sampler_MaskTex, i.uv);
    
                    return CombinedShapeLightShared(main, mask, i.lightingUV);
                }
                ENDHLSL
            }
    
            Pass
            {
                Tags { "LightMode" = "NormalsRendering"}
                HLSLPROGRAM
                #pragma vertex NormalsRenderingVertex
                #pragma fragment NormalsRenderingFragment
    
                struct Attributes
                {
                    float3 positionOS   : POSITION;
                    float4 color        : COLOR;
                    float2 uv           : TEXCOORD0;
                    float4 tangent      : TANGENT;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };
    
                struct Varyings
                {
                    float4  positionCS      : SV_POSITION;
                    half4   color           : COLOR;
                    float2  uv              : TEXCOORD0;
                    half3   normalWS        : TEXCOORD1;
                    half3   tangentWS       : TEXCOORD2;
                    half3   bitangentWS     : TEXCOORD3;
                    UNITY_VERTEX_OUTPUT_STEREO
                };
    
                TEXTURE2D(_MainTex);
                SAMPLER(sampler_MainTex);
                TEXTURE2D(_NormalMap);
                SAMPLER(sampler_NormalMap);
                half4 _NormalMap_ST;  // Is this the right way to do this?
    
                Varyings NormalsRenderingVertex(Attributes attributes)
                {
                    Varyings o = (Varyings)0;
                    UNITY_SETUP_INSTANCE_ID(attributes);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    
                    o.positionCS = TransformObjectToHClip(attributes.positionOS);
                    o.uv = TRANSFORM_TEX(attributes.uv, _NormalMap);
                    o.color = attributes.color;
                    o.normalWS = TransformObjectToWorldDir(float3(0, 0, -1));
                    o.tangentWS = TransformObjectToWorldDir(attributes.tangent.xyz);
                    o.bitangentWS = cross(o.normalWS, o.tangentWS) * attributes.tangent.w;
                    return o;
                }
    
                #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/NormalsRenderingShared.hlsl"
    
                half4 NormalsRenderingFragment(Varyings i) : SV_Target
                {
                    half4 mainTex = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
                    half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, i.uv));
                    return NormalsRenderingShared(mainTex, normalTS, i.tangentWS.xyz, i.bitangentWS.xyz, i.normalWS.xyz);
                }
                ENDHLSL
            }
            Pass
            {
                Tags { "LightMode" = "UniversalForward" "Queue"="Transparent" "RenderType"="Transparent"}
    
                HLSLPROGRAM
                #pragma vertex UnlitVertex
                #pragma fragment UnlitFragment
    
                struct Attributes
                {
                    float3 positionOS   : POSITION;
                    float4 color        : COLOR;
                    float2 uv           : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };
    
                struct Varyings
                {
                    float4  positionCS      : SV_POSITION;
                    float4  color           : COLOR;
                    float2  uv              : TEXCOORD0;
                    UNITY_VERTEX_OUTPUT_STEREO
                };
    
                TEXTURE2D(_MainTex);
                SAMPLER(sampler_MainTex);
                float4 _MainTex_ST;
    
                Varyings UnlitVertex(Attributes attributes)
                {
                    Varyings o = (Varyings)0;
                    UNITY_SETUP_INSTANCE_ID(attributes);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    
                    o.positionCS = TransformObjectToHClip(attributes.positionOS);
                    o.uv = TRANSFORM_TEX(attributes.uv, _MainTex);
                    o.color = attributes.color;
                    return o;
                }
    
                float4 UnlitFragment(Varyings i) : SV_Target
                {
                    float4 mainTex = i.color * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
                    return mainTex;
                }
                ENDHLSL
            }
        }
    
        Fallback "Sprites/Default"
    }
    

    File name CombinedShapeLightSharedHidden.hlsl

    #if !defined(COMBINED_SHAPE_LIGHT_PASS)
    #define COMBINED_SHAPE_LIGHT_PASS
    
    half _HDREmulationScale;
    half _UseSceneLighting;
    half4 _RendererColor;
    
    half4 CombinedShapeLightShared(half4 color, half4 mask, half2 lightingUV)
    {
        if (color.a == 0.0)
            discard;
    
        color = color * _RendererColor; // This is needed for sprite shape
    
    #if USE_SHAPE_LIGHT_TYPE_0
        half4 shapeLight0 = SAMPLE_TEXTURE2D(_ShapeLightTexture0, sampler_ShapeLightTexture0, lightingUV);
    
        if (any(_ShapeLightMaskFilter0))
        {
            half4 processedMask = (1 - _ShapeLightInvertedFilter0) * mask + _ShapeLightInvertedFilter0 * (1 - mask);
            shapeLight0 *= dot(processedMask, _ShapeLightMaskFilter0);
        }
    
        half4 shapeLight0Modulate = shapeLight0 * _ShapeLightBlendFactors0.x;
        half4 shapeLight0Additive = shapeLight0 * _ShapeLightBlendFactors0.y;
    #else
        half4 shapeLight0Modulate = 0;
        half4 shapeLight0Additive = 0;
    #endif
    
    #if USE_SHAPE_LIGHT_TYPE_1
        half4 shapeLight1 = SAMPLE_TEXTURE2D(_ShapeLightTexture1, sampler_ShapeLightTexture1, lightingUV);
    
        if (any(_ShapeLightMaskFilter1))
        {
            half4 processedMask = (1 - _ShapeLightInvertedFilter1) * mask + _ShapeLightInvertedFilter1 * (1 - mask);
            shapeLight1 *= dot(processedMask, _ShapeLightMaskFilter1);
        }
    
        half4 shapeLight1Modulate = shapeLight1 * _ShapeLightBlendFactors1.x;
        half4 shapeLight1Additive = shapeLight1 * _ShapeLightBlendFactors1.y;
    #else
        half4 shapeLight1Modulate = 0;
        half4 shapeLight1Additive = 0;
    #endif
    
    #if USE_SHAPE_LIGHT_TYPE_2
        half4 shapeLight2 = SAMPLE_TEXTURE2D(_ShapeLightTexture2, sampler_ShapeLightTexture2, lightingUV);
    
        if (any(_ShapeLightMaskFilter2))
        {
            half4 processedMask = (1 - _ShapeLightInvertedFilter2) * mask + _ShapeLightInvertedFilter2 * (1 - mask);
            shapeLight2 *= dot(processedMask, _ShapeLightMaskFilter2);
        }
    
        half4 shapeLight2Modulate = shapeLight2 * _ShapeLightBlendFactors2.x;
        half4 shapeLight2Additive = shapeLight2 * _ShapeLightBlendFactors2.y;
    #else
        half4 shapeLight2Modulate = 0;
        half4 shapeLight2Additive = 0;
    #endif
    
    #if USE_SHAPE_LIGHT_TYPE_3
        half4 shapeLight3 = SAMPLE_TEXTURE2D(_ShapeLightTexture3, sampler_ShapeLightTexture3, lightingUV);
    
        if (any(_ShapeLightMaskFilter3))
        {
            half4 processedMask = (1 - _ShapeLightInvertedFilter3) * mask + _ShapeLightInvertedFilter3 * (1 - mask);
            shapeLight3 *= dot(processedMask, _ShapeLightMaskFilter3);
        }
    
        half4 shapeLight3Modulate = shapeLight3 * _ShapeLightBlendFactors3.x;
        half4 shapeLight3Additive = shapeLight3 * _ShapeLightBlendFactors3.y;
    #else
        half4 shapeLight3Modulate = 0;
        half4 shapeLight3Additive = 0;
    #endif
    
        half4 finalOutput;
    #if !USE_SHAPE_LIGHT_TYPE_0 && !USE_SHAPE_LIGHT_TYPE_1 && !USE_SHAPE_LIGHT_TYPE_2 && ! USE_SHAPE_LIGHT_TYPE_3
        finalOutput = color;
    #else
        half4 finalModulate = shapeLight0Modulate + shapeLight1Modulate + shapeLight2Modulate + shapeLight3Modulate;
        half4 finalAdditve = shapeLight0Additive + shapeLight1Additive + shapeLight2Additive + shapeLight3Additive;
        finalOutput = _HDREmulationScale * (color * finalModulate + finalAdditve);
    
        half luminance = max(finalModulate.r, max(finalModulate.g, finalModulate.b));
        finalOutput.a = min(luminance * 2, color.a);
    #endif
    
        finalOutput = finalOutput *_UseSceneLighting + (1 - _UseSceneLighting)*color;
        return max(0, finalOutput);
    }
    #endif
    

    The part that he added is that last part where it starts at half luminance....

    After copying them into your own files, you can then use the .shader file to add it to a material and then apply it to the objects you want to disappear in shadows. So if the light hits the object it will appear, but it will appear gradually as the light source gets closer to that object, the only downside to this is there must be a light source in the game so that it can use to calculate the luminance, otherwise the object will just default back to a luminance of 1, meaning completely visible. But, I think if you want to make an object disappear in the shadows then you're most likely using a light source. This is how it looks like:

    Gif

    Of course, you play with the light source and the intensity to get the result you want.

    If you have any questions I guess you can comment or something and I will help once I see the comment.