Search Unity

How do you make lights affect 2D sprites in a 3D environment using Unity's URP render pipeline?

Discussion in 'Universal Render Pipeline' started by VEWO, May 19, 2020.

  1. VEWO

    VEWO

    Joined:
    Sep 30, 2014
    Posts:
    8
    I'm using Unity, URP and have 2d sprites characters in a 3D environment. I want 2D sprites to be affected by lights/shadows like the rest of the environment.

    -I know if I use a 2D renderer I can use 2D lights but then I lose the projected shadows from both 3D objects and 2D Sprites. -If I use the standard render pipeline i can make it work but I don't want to miss on all the tools that URP brings to the table.

    In summary, is it possible to use 2D lights or any light that affects 2D sprites in URP ?

    Thanks!
     
  2. Ruskul

    Ruskul

    Joined:
    Aug 7, 2014
    Posts:
    72
    You might have to actually jump into the code and Frankenstein it - I don't even have a clue though, how that would look or work. I tried doing this a little and realized it wasn't worth the time for my game. I spent a day or so tinkering around trying to add specular lighting to the 2d renderer...

    For me, I would simply put simple-lit material on your sprite and use 3d lights. I would probably use a normal map with the sprites though - even if it is a simple up pointing normal so they are lit better by lights from above. You would have to play with it. I wanted something similar, but decided I wanted 2d lights and their "fog" more than 3d. I'm actually still using 3d objects, but I'm using the 2d render asset. Its fine so long as you don't rotate the camera.
     
  3. kappadev

    kappadev

    Joined:
    May 29, 2019
    Posts:
    2
    Hi @VEWO , did you find a solution about this?
    I have the same question.
     
  4. dahiyabunty1

    dahiyabunty1

    Joined:
    Jan 22, 2020
    Posts:
    68
    convert sprite into mesh and then use material
     
  5. ShadowLairGames

    ShadowLairGames

    Joined:
    Jan 17, 2019
    Posts:
    45
    So I was able to find a hack to get this kind of thing to work using Stencil on copies of some built-in shaders.

    upload_2020-7-10_10-50-40.png

    The idea is to tackle this in 2 passes:
    1. Use the Sprites-Default shader to draw the sprite in the world, while adding a stencil to it to mark each pixel shaded by it.
    2. Use the Lit shader from URP, but only filter it to operate on pixels that were drawn into by the sprite shader in the previous pass.

    The sprite is drawn normally, unshadowed. The magic happens in the 2nd step.
    What this does is basically put up a plane on top of your sprite, which receives shadows via the lit shader, but only draws the pixels that the sprite occupies.


    The full details for achieving this:

    Create a Shadowed Sprite shader:
    Code (CSharp):
    1. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    2.  
    3. Shader "Sprites/Shadowed"
    4. {
    5.     Properties
    6.     {
    7.         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
    8.         _Color ("Tint", Color) = (1,1,1,1)
    9.         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    10.         [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
    11.         [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
    12.         [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
    13.         [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
    14.     }
    15.  
    16.     SubShader
    17.     {
    18.         Tags
    19.         {
    20.             "Queue"="Transparent"
    21.             "IgnoreProjector"="True"
    22.             "RenderType"="Transparent"
    23.             "PreviewType"="Plane"
    24.             "CanUseSpriteAtlas"="True"
    25.         }
    26.  
    27.         Cull Off
    28.         Lighting Off
    29.         ZWrite Off
    30.         Blend One OneMinusSrcAlpha
    31.  
    32.         Stencil
    33.         {
    34.             Ref 584
    35.             Comp Always
    36.             Pass Replace
    37.             Fail Keep
    38.             ZFail Keep
    39.         }
    40.  
    41.         Pass
    42.         {
    43.         CGPROGRAM
    44.             #pragma vertex SpriteVert
    45.             #pragma fragment frag
    46.             #pragma target 2.0
    47.             #pragma multi_compile_instancing
    48.             #pragma multi_compile_local _ PIXELSNAP_ON
    49.             #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
    50.             #include "UnitySprites.cginc"
    51.            
    52.             fixed4 frag(v2f IN) : SV_Target
    53.             {
    54.                 fixed4 c = SpriteFrag(IN);
    55.  
    56.                 if (c.a == 0)
    57.                 {
    58.                     discard;
    59.                 }
    60.  
    61.                 return c;
    62.             }
    63.         ENDCG
    64.         }
    65.     }
    66. }
    67.  
    This is just the Sprite-Default shader with the following 2 changes:
    1. Added the stencil which will let other shaders know which pixels on the screen were drawn into by this shader.
    2. Added an intermediate step to the fragment shader which will discard any pixels that draw transparent pixels (The shader runs on every pixel of the sprite, including transparent ones, so unless this is called, the shadow will be applied to the full rect of the sprite instead of just the visible parts of it) (Note: This works because the 'discard' keyword also discards Stencil information of the pixel and not just the color. This was an annoyance to me many a-time in the past, but this time it actually works in our favor).

    Next, we need to create a ShadowOnly variant of the Lit shader from the URP package:

    Code (CSharp):
    1. Shader "Universal Render Pipeline/Lit - Shadows Only"
    2. {
    3.     Properties
    4.     {
    5.         // Specular vs Metallic workflow
    6.         [HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0
    7.  
    8.         [MainColor] _BaseColor("Color", Color) = (1,1,1,1)
    9.         [MainTexture] _BaseMap("Albedo", 2D) = "white" {}
    10.  
    11.         _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
    12.  
    13.         _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
    14.         _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
    15.         _SmoothnessTextureChannel("Smoothness texture channel", Float) = 0
    16.  
    17.         [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
    18.         _MetallicGlossMap("Metallic", 2D) = "white" {}
    19.  
    20.         _SpecColor("Specular", Color) = (0.2, 0.2, 0.2)
    21.         _SpecGlossMap("Specular", 2D) = "white" {}
    22.  
    23.         [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
    24.         [ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0
    25.  
    26.         _BumpScale("Scale", Float) = 1.0
    27.         _BumpMap("Normal Map", 2D) = "bump" {}
    28.  
    29.         _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
    30.         _OcclusionMap("Occlusion", 2D) = "white" {}
    31.  
    32.         _EmissionColor("Color", Color) = (0,0,0)
    33.         _EmissionMap("Emission", 2D) = "white" {}
    34.  
    35.         // Blending state
    36.         [HideInInspector] _Surface("__surface", Float) = 0.0
    37.         [HideInInspector] _Blend("__blend", Float) = 0.0
    38.         [HideInInspector] _AlphaClip("__clip", Float) = 0.0
    39.         [HideInInspector] _SrcBlend("__src", Float) = 1.0
    40.         [HideInInspector] _DstBlend("__dst", Float) = 0.0
    41.         [HideInInspector] _ZWrite("__zw", Float) = 1.0
    42.         [HideInInspector] _Cull("__cull", Float) = 2.0
    43.  
    44.         _ReceiveShadows("Receive Shadows", Float) = 1.0
    45.         // Editmode props
    46.         [HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
    47.  
    48.         // ObsoleteProperties
    49.         [HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
    50.         [HideInInspector] _Color("Base Color", Color) = (1, 1, 1, 1)
    51.         [HideInInspector] _GlossMapScale("Smoothness", Float) = 0.0
    52.         [HideInInspector] _Glossiness("Smoothness", Float) = 0.0
    53.         [HideInInspector] _GlossyReflections("EnvironmentReflections", Float) = 0.0
    54.     }
    55.  
    56.     SubShader
    57.     {
    58.         // Universal Pipeline tag is required. If Universal render pipeline is not set in the graphics settings
    59.         // this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this
    60.         // material work with both Universal Render Pipeline and Builtin Unity Pipeline
    61.         Tags{"RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True" "Queue"="Transparent"}
    62.         LOD 300
    63.  
    64.         Stencil
    65.         {
    66.             Ref 584
    67.             Comp Equal
    68.             Pass Keep
    69.             Fail Zero
    70.         }
    71.  
    72.         // ------------------------------------------------------------------
    73.         //  Forward pass. Shades all light in a single pass. GI + emission + Fog
    74.         Pass
    75.         {
    76.             // Lightmode matches the ShaderPassName set in UniversalRenderPipeline.cs. SRPDefaultUnlit and passes with
    77.             // no LightMode tag are also rendered by Universal Render Pipeline
    78.             Name "ForwardLit"
    79.             Tags{"LightMode" = "UniversalForward"}
    80.  
    81.             Blend[_SrcBlend][_DstBlend]
    82.             ZWrite[_ZWrite]
    83.             Cull[_Cull]
    84.  
    85.             HLSLPROGRAM
    86.             // Required to compile gles 2.0 with standard SRP library
    87.             // All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default
    88.             #pragma prefer_hlslcc gles
    89.             #pragma exclude_renderers d3d11_9x
    90.             #pragma target 2.0
    91.  
    92.             // -------------------------------------
    93.             // Material Keywords
    94.             #pragma shader_feature _NORMALMAP
    95.             #pragma shader_feature _ALPHATEST_ON
    96.             #pragma shader_feature _ALPHAPREMULTIPLY_ON
    97.             #pragma shader_feature _EMISSION
    98.             #pragma shader_feature _METALLICSPECGLOSSMAP
    99.             #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    100.             #pragma shader_feature _OCCLUSIONMAP
    101.  
    102.             #pragma shader_feature _SPECULARHIGHLIGHTS_OFF
    103.             #pragma shader_feature _ENVIRONMENTREFLECTIONS_OFF
    104.             #pragma shader_feature _SPECULAR_SETUP
    105.             #pragma shader_feature _RECEIVE_SHADOWS_OFF
    106.  
    107.             // -------------------------------------
    108.             // Universal Pipeline keywords
    109.             #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
    110.             #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
    111.             #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
    112.             #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
    113.             #pragma multi_compile _ _SHADOWS_SOFT
    114.             #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE
    115.  
    116.             // -------------------------------------
    117.             // Unity defined keywords
    118.             #pragma multi_compile _ DIRLIGHTMAP_COMBINED
    119.             #pragma multi_compile _ LIGHTMAP_ON
    120.             #pragma multi_compile_fog
    121.  
    122.             //--------------------------------------
    123.             // GPU Instancing
    124.             #pragma multi_compile_instancing
    125.  
    126.             #pragma vertex LitPassVertex
    127.             #pragma fragment LitPassFragment
    128.  
    129.             #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
    130.             #include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
    131.             ENDHLSL
    132.         }
    133.  
    134.         Pass
    135.         {
    136.             Name "ShadowCaster"
    137.             Tags{"LightMode" = "ShadowCaster"}
    138.  
    139.             ZWrite On
    140.             ZTest LEqual
    141.             Cull[_Cull]
    142.  
    143.             HLSLPROGRAM
    144.             // Required to compile gles 2.0 with standard srp library
    145.             #pragma prefer_hlslcc gles
    146.             #pragma exclude_renderers d3d11_9x
    147.             #pragma target 2.0
    148.  
    149.             // -------------------------------------
    150.             // Material Keywords
    151.             #pragma shader_feature _ALPHATEST_ON
    152.  
    153.             //--------------------------------------
    154.             // GPU Instancing
    155.             #pragma multi_compile_instancing
    156.             #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    157.  
    158.             #pragma vertex ShadowPassVertex
    159.             #pragma fragment ShadowPassFragment
    160.  
    161.             #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
    162.             #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
    163.             ENDHLSL
    164.         }
    165.  
    166.         Pass
    167.         {
    168.             Name "DepthOnly"
    169.             Tags{"LightMode" = "DepthOnly"}
    170.  
    171.             ZWrite On
    172.             ColorMask 0
    173.             Cull[_Cull]
    174.  
    175.             HLSLPROGRAM
    176.             // Required to compile gles 2.0 with standard srp library
    177.             #pragma prefer_hlslcc gles
    178.             #pragma exclude_renderers d3d11_9x
    179.             #pragma target 2.0
    180.  
    181.             #pragma vertex DepthOnlyVertex
    182.             #pragma fragment DepthOnlyFragment
    183.  
    184.             // -------------------------------------
    185.             // Material Keywords
    186.             #pragma shader_feature _ALPHATEST_ON
    187.             #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    188.  
    189.             //--------------------------------------
    190.             // GPU Instancing
    191.             #pragma multi_compile_instancing
    192.  
    193.             #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
    194.             #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
    195.             ENDHLSL
    196.         }
    197.  
    198.         // This pass it not used during regular rendering, only for lightmap baking.
    199.         Pass
    200.         {
    201.             Name "Meta"
    202.             Tags{"LightMode" = "Meta"}
    203.  
    204.             Cull Off
    205.  
    206.             HLSLPROGRAM
    207.             // Required to compile gles 2.0 with standard srp library
    208.             #pragma prefer_hlslcc gles
    209.             #pragma exclude_renderers d3d11_9x
    210.  
    211.             #pragma vertex UniversalVertexMeta
    212.             #pragma fragment UniversalFragmentMeta
    213.  
    214.             #pragma shader_feature _SPECULAR_SETUP
    215.             #pragma shader_feature _EMISSION
    216.             #pragma shader_feature _METALLICSPECGLOSSMAP
    217.             #pragma shader_feature _ALPHATEST_ON
    218.             #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    219.  
    220.             #pragma shader_feature _SPECGLOSSMAP
    221.  
    222.             #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
    223.             #include "Packages/com.unity.render-pipelines.universal/Shaders/LitMetaPass.hlsl"
    224.  
    225.             ENDHLSL
    226.         }
    227.         Pass
    228.         {
    229.             Name "Universal2D"
    230.             Tags{ "LightMode" = "Universal2D" }
    231.  
    232.             Blend[_SrcBlend][_DstBlend]
    233.             ZWrite[_ZWrite]
    234.             Cull[_Cull]
    235.  
    236.             HLSLPROGRAM
    237.             // Required to compile gles 2.0 with standard srp library
    238.             #pragma prefer_hlslcc gles
    239.             #pragma exclude_renderers d3d11_9x
    240.  
    241.             #pragma vertex vert
    242.             #pragma fragment frag
    243.             #pragma shader_feature _ALPHATEST_ON
    244.             #pragma shader_feature _ALPHAPREMULTIPLY_ON
    245.  
    246.             #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
    247.             #include "Packages/com.unity.render-pipelines.universal/Shaders/Utils/Universal2D.hlsl"
    248.             ENDHLSL
    249.         }
    250.  
    251.  
    252.     }
    253.     FallBack "Hidden/Universal Render Pipeline/FallbackError"
    254.     CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.LitShader"
    255. }
    256.  
    The only change here is the addition of the stencil that will filter out any pixels that weren't rendered by the sprite.

    Then, all we need is to create materials for the 2 shaders, making sure that the Lit-ShadowsOnly material uses the Transparent surface type, Multiply Blending mode and a priority that has it take effect AFTER the sprite shader.
    upload_2020-7-10_10-42-12.png
    (Important note: Up to URP version 7.4.0 there was a bug where the Priority wouldn't be added correctly to offset the rendering queue of the shader, and would have the (-50, 50) priority range in the inspector translated to the (3000, 3100) rendering queue instead of (2950, 3050). The tooltip for priority still seems incorrect as it claims that higher priority values get rendered first, which is the opposite of what happens when increasing the queue. So just a heads up that this screenshot is from a project using 7.4.0 and this might be different for you if you're on a lower version)

    To apply the shadows, you need to use the shader on a quad that will cover the sprite you want shadowed:
    upload_2020-7-10_10-47-8.png
    Note: You can also place this across the entire screen and it'll process all sprites on the screen that use the Sprite-Shadowed shader above. It doesn't necessarily need to be placed on top of a specific sprite.

    This approach isn't ideal in terms of performance, as it requires either a screen-wide shader, or an extra 5-pass shader for every sprite in the scene.
    But I guess it's at least a way to achieve the effect for now, until more proper support is provided officially.
     
  6. jimmiewalker653

    jimmiewalker653

    Joined:
    Jan 30, 2013
    Posts:
    3
    This is how I did it, without using scripts... You'll need 2 cameras for this and you'll need to get the Light Weight Render Pipeline. Once you've setup your Render pipeline, you can add both renders to the pipeline you've created. Next, you'll need two cameras. Camera "A" will render all of your sprites and Camera "B" will render your 3D assets. Camera "A" will need to be an overlay camera and Camera "B" will need to be your base camera. Once you've setup everything up, simply hit the plus button on the camera in the section for "Stack", then add your overlay camera to it. You should now have both 3D and 2D lights interacting with 3D objects and Sprites.
     
    zzf18676456441 likes this.
  7. kpdavid02

    kpdavid02

    Joined:
    May 17, 2015
    Posts:
    1
  8. rubberjoelzilla

    rubberjoelzilla

    Joined:
    Aug 3, 2019
    Posts:
    1
  9. tiny-ant

    tiny-ant

    Joined:
    Feb 6, 2013
    Posts:
    2
    I realize this is quite old. But it is exactly what I am looking for! Can you elaborate on how to "setup your render pipeline"?
     
  10. luxichor

    luxichor

    Joined:
    May 24, 2023
    Posts:
    2
    if that is your question, you need to start by reading unity's docs related to rendering, and maybe watch some tutorials on using the URP, too. in general, when programming, always scour the docs thoroughly before asking your question. they are your first and most important resource. they will answer all of your basic questions.

    in the age of google this is extremely easy. just look up "unity [whatever thing you are confused about]" and find a page whose url starts with "docs.unity3d.com".

    if you're confused about anything you read, open unity and F*** around with the mechanic until you understand how it works. use the docs as your roadmap. then come to the forums when you're ready for more. most programming advice in general, including game dev advice, will not be useful until you have that foundational knowledge.