Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Help with Mesh based top-down 2D shadows like Rimworld and Prison Architect

Discussion in 'Shaders' started by brainmulator, Mar 6, 2020.

  1. brainmulator

    brainmulator

    Joined:
    Apr 8, 2019
    Posts:
    2
    Hey there,

    I'm trying to implement a 2D top down shadow using meshes. The image below explains it better. It's from the game Rimworld.

    rimworld-shadows.PNG

    Rimwold creator explaining how he did it: https://ludeon.com/blog/2013/08/sun-shadows/#comment-1945.

    I gave my try on it, but I'm still trying to learn shaders. I managed to change a cube's "front" face (grabbing all the vertices that composes it) and moving them using an oscillation formula. I squashed all the cubes vertices in the Z dimension to the same value.

    As the example (image) above, I want them to blend with the ground, but I don't want them to blend with each other. Is that possible?

    I'm using a simple unlit transparent shader:

    Code (CSharp):
    1. Shader "Unlit/ShadowShader"
    2. {
    3.     Properties
    4.     {
    5.         _Color("Color", Color) = (1, 1, 1, 1)
    6.         _MoveAmount("Move Amount", Float) = 0
    7.         _Length("Length", Float) = 1
    8.         _BottomVerticesYOffset("Bottom Vertices Y Offset", Float) = 1.5
    9.     }
    10.     SubShader
    11.     {
    12.         Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
    13.         LOD 100
    14.  
    15.         ZWrite Off
    16.         Blend SrcAlpha OneMinusSrcAlpha
    17.  
    18.         Pass
    19.         {
    20.             CGPROGRAM
    21.             #pragma vertex vert
    22.             #pragma fragment frag
    23.  
    24.             #include "UnityCG.cginc"
    25.  
    26.             struct appdata
    27.             {
    28.                 float4 vertex : POSITION;
    29.             };
    30.  
    31.             struct v2f
    32.             {
    33.                 float4 vertex : SV_POSITION;
    34.             };
    35.  
    36.             float _MoveAmount;
    37.             float _Length;
    38.             float _BottomVerticesYOffset;
    39.             float4 _Color;
    40.  
    41.             v2f vert (appdata v, uint vertexId : SV_VertexID)
    42.             {
    43.                 v2f o;
    44.  
    45.                 if (vertexId != 2 && vertexId != 3 && vertexId != 0 && vertexId != 1 && vertexId != 8 && vertexId != 9 && vertexId != 13 && vertexId != 14 && vertexId != 16 && vertexId != 17 && vertexId != 22 && vertexId != 23)
    46.                     o.vertex = UnityObjectToClipPos(float4(v.vertex.x + sin(_MoveAmount) * _Length, (v.vertex.y + _BottomVerticesYOffset) - cos(_MoveAmount) * _Length, 0, 0));
    47.                 else
    48.                     o.vertex = UnityObjectToClipPos(float4(v.vertex.x, v.vertex.y, 0, 0));
    49.  
    50.                 return o;
    51.             }
    52.  
    53.             fixed4 frag (v2f i) : SV_Target
    54.             {
    55.                 return _Color;
    56.             }
    57.             ENDCG
    58.         }
    59.     }
    60. }
    61.  
    Results in this (in a cube):

    my-shadows.PNG

    This is still a messy code. The important part, I believe is it being transparent and the blend mode strategy.

    To recap I would like the "shadows" not to blend with each other, just with the "ground".

    I want to understand why this won't work, if that's the case, and what concepts should I look into that could give the same result.

    If it's possible to follow what I'm already doing, what am I missing?

    Best regards!
     
  2. brainmulator

    brainmulator

    Joined:
    Apr 8, 2019
    Posts:
    2
    So, I was searching for the same problem that someone could have had before, but I was having a bad time in finding the words. After some time I stumbled upon this one:

    https://computergraphics.stackexcha...tween-two-overlapping-semi-transparent-shapes

    It talks about Stencil buffer to check pixels on subsequent draw calls.

    It solves the problem, but I don't get it. I want to understand.

    The stencil is declared like this:

    Code (CSharp):
    1. Stencil {
    2.     Ref 0
    3.     Comp Equal
    4.     Pass IncrSat
    5.     Fail IncrSat
    6. }
    I understood that the Ref is just assigning a reference, but for what I didn't understand.
    The Comp for me made sense, sort of. What I understood is that it checks the buffer that was rendered in the "opaque" render queue. If it is not (already blended a "shadow" in a previous draw call), then it discards, if it is Equal then run the pass.

    The IncrSat for Pass and Fail I don't get it.

    (I read https://docs.unity3d.com/Manual/SL-Stencil.html)
     
  3. dterbeest

    dterbeest

    Joined:
    Mar 23, 2012
    Posts:
    389
    i was looking into the same thing, and i think what they did is modify the unity standard 2d sprite shadows to fit their needs. Check out the sourcecode for ShadowCaster2D