Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Per-Pixel 2D Shadows

Discussion in '2D Experimental Preview' started by NeoReelMac, Feb 3, 2020.

  1. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    15
    Hello all,

    I'm testing to what extent I may use the new 2D lighting renderer for my project.
    I'm wondering if I can get 2D shadows to be evaluated per-pixel rather than per sprite/shape. Would it be possible to modify the Shadow 2d Caster script or use a custom shader to test for occlusion with the alpha channel of a texture?

    The game is a roguelike topdown with a large plane as the floor and a dynamic texture for the walls. I'd like the 2D shadows and 2D point light to test for an opaque pixel rather than an intersection with a shape...

    Any feedback would be appreciated.

    Thank you!
     
  2. JoKr

    JoKr

    Joined:
    Jul 12, 2018
    Posts:
    1
    Hi. Did you find any solution to this?
     
  3. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    15
    Hello JoKr,
    Not a perfect solution, no... Still investigating. At this point, I'm rendering a Texture2D shadow mask based on this solution: https://www.gamasutra.com/blogs/Rob..._shadows_in_Unity_using_1D_shadow_mapping.php
    But this is not exactly what I want. I end up with a CustomRenderTexture that I can place on top of my scene, but it doesn't affect the lighting. Copying the texture to a sprite on each frame to connect it to the Light2D Sprite cookie is slow...
    My next attempt will be to override the Light2D to use a texture2D instead of a sprite but I don't know if this could work.
     
    JoKr likes this.
  4. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    15
    Hello,

    So I got this work pretty well and fast, but I'm having a lag between the update of the Light2D and cookie sprite. Basically, when I drag the light around, the cookie is a frame late. So this got me thinking that perhaps the sprite is updating with the previous frame information which would do exactly that. I made everything happen in LateUpdate, but no luck...

    Here is the basic of what I'm doing:
    - A camera is set up to render the scene to a RenderTexture.
    - I manually call camera.Render() for that camera in LateUpdate()
    - I call a CustomRenderTexture.Initialize() to perform the 1D shadow shader pass.
    - I update the light2D.sprite.texture with the output of the CustomRenderTexture
    - I call the light2D.sprite.texture.Apply() to make sure it is up to date.
    - I called Debug.Break() when the scene is moving to stop at a frame with an offset.
    - I can view the RenderTextures are they show the current frame.
    - I can view the updated sprite texture and it is indeed from the previous frame.
    - So in the end, the light2D renders with the sprite from the previous frame.

    Can anyone help with this? How can I move the code I use from LateUpdate() to another location so the texture gets updated just before the light2D renders?

    It seams like this code is the problem:
    Code (CSharp):
    1. public void LateUpdate(){
    2. camera.Render(); //renders to a RenderTexture
    3. myCustomRenderTexture.Initialize(); //uses the above RenderTexture in a shader
    4.  
    5. RenderTexture.active = myCustomRenderTexture;
    6. convertTexture.ReadPixels(new Rect(0, 0, myCustomRenderTexture.width, myCustomRenderTexture.height), 0, 0);
    7. convertTexture.Apply();
    8. // <= Here myCustomRenderTextureshows the current frame, but the convertTexture shows the previous frame
    9. }
    Thank you!
     
    Last edited: Feb 17, 2020
  5. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    15
    More on my investigation:

    If I do this:
    Call Render() for a camera with render target as a RenderTexture
    Set its RenderTexture as Active
    Call ReadPixels()
    It will be the current frame.

    If I do this:
    Initialize()/Update() a CustomRenderTexture using the above RenderTexture
    Set that CustomRenderTexture as Active
    Call ReadPixels()
    It will be the previous frame.

    Why? How?

    is it because the shader needs to have
    Code (CSharp):
    1. #include "UnityCustomRenderTexture.cginc"
     
  6. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    15
    Solution found!
    Cheap workaround, set the position of my light to be the previous frame position...
    This 2D shadow based on a texture is now working like a charm!
    Cheers,
     
  7. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    406
    Hey! Do you have any gif to show the result? :)
     
  8. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    15
    Sure thing!
    Here is a test I did earlier on:
     
  9. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    406
    Wow, looking great! I’m wondering if this is possible in a top down game, with a perspective like Stardew Valley..?
     
  10. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    15
    From my personal experience, as soon as I tilt the camera, the entire 2D Render Pipeline doesn't know what to do with the angle, so everything flies off. It is normal since it is not meant to be 3D. I can personally fake the slant of the camera using wall shifting/scaling (as you can especially see in the top of my video), but that is as much as the renderer will take. Cheers!
     
    Last edited: Aug 28, 2020