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