Unity version: 2021.3.5f1
URP: 12.1.7
Rain World's 2D screen-space shadow effect implemented in Unity Universal Rendering Pipeline. This implementation is based on the tweets made by Rain World's developer Joar Jakobsson.
Result
Diver_Fan_DyanmicBG.mp4
All the assets/code that are relevant to the effect are located in these folders:
- Assets/Settings/URP-Peformant.asset & Assets/Settings/URP-Peformant-Renderer.asset
- Assets/GameData/Rendering
- Assets/Scripts/Rendering
- Assets/Shaders
- Assets/Materials
As mentioned in the previous section, most of the implementation details are the same as the one described in the tweets. For the original implementation, Joar has already explained it perfectly in his original tweets. Therefore I recommend that you read the tweets directly :P
However here is my understandings anyways:
- First of all, you need to prepare a "depth texture" for the background. It will be used to calculate the "sample point" in the 2nd step later.
- Render foreground (dynamic) objects to the frame buffer. Foreground objects are the ones that could cast shadows.
- Render background objects' color to the frame buffer. But instead of just render the color/texture directly, the background shader gets how deep the background pixel is in the background using the depth texture mentioned earlier. Using the depth as the offset, we calculate a sample point to sample the frame buffer pixel. If the sampled pixel is not transparent, it means there is something in the foreground that should cast shadow on the background pixel. If that's the case, we multiple the background color with 0.5f so it looks darker.
And here is my implementation:
- Render foreground objects to the frame buffer. Foreground objects are the ones that could cast shadows. In my implementation, instead of rendering the colors directly to the frame buffer, these objects are rendered to an external render texture.
- Render background objects' depth to a depth texture.
- Render background objects' color to the frame buffer like how it's done in the original implementation.
The benefit of my implementation are that the background obejcts could also be dynamic (As shown in my result video, the rotating background fan). The downside is that there is an extra pass in the process.
Other than the differences between the rendering passes, I've also implemented semi-transparent shadows with this system.
- Since this is a screen-space effects, Off-screen foreground objects cannot cast shadows. Due to how the levels are designed in Rain World (static camera + one-screen-sized level), this was not a problem in the game. The possible solution is to render the foreground color buffer in a slightly bigger size.
- Depth texture is generated with the geometry of the object, the alpha value is not taken into consideration. This could possibly be solved by adding an extra pass that renders the alpha value buffer and uses that to cutoff depth texture.
- No anti-aliasing on the shadows, which is actually perfect if it's a pixel art game.
LowRes.mp4
The art assets are from one of my school projects at CGL. I also implemented the procedural character animation. The relevant code is located under Assets/Scripts/Character/Animation
- Joar Jakobsson:
For sharing the implementation details in RW. - Maria Eom:
For making the ghost character sprites.