Search Unity

Per-Pixel 2D Shadows

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

  1. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    5
    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:
    5
    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:
    5
    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 at 7:01 PM
  5. NeoReelMac

    NeoReelMac

    Joined:
    Feb 3, 2020
    Posts:
    5
    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"
     
unityunity