[Updated for 4.20.1] Let it rain! (But not indoors!)


Thanks for your tutoral that’s work good.

What if you don’t have a roof but just a one-sided ceiling?

@DamirH - Hey what happen to the write up on angled rain?

Is this still the best solution now in 2018, or is there a better way to handle this now?

Hey @wilberolive - I’ve never found time to do the angled writeup and I lost that code since in a hard drive failure. There are several solutions these days that are better than this although this is still a viable solution in theory, but as far as practical implementation goes, many techniques used here have more practical and streamlined approaches. For example, scene capture can now capture orthographic projection, as well as capture depth directly so no conversion or effects are needed for those steps. I will need this soon in a project so eventually I will write up a new method for this, can’t really promise any timeframe for it.

Looking forward to your new write up on this. Will be needing a similar effect on a project in the future. =)

EDIT: I accidentally left the RT_RainDepth texture at 1024x1024. Please reduce that back down before using this. You will have to play around to get the size that suits you. I had very little artifacts with sizes as low as 64x64 but 128x128 seems to work almost flawlessly. Obviously smaller size = less precision and granularity, but faster performance.

Well, I went ahead and spent some time to re-create this code in 4.20.1. After a bit I’ve managed to get it working. I’m presenting the Blueprint “as is” for now and if anyone has any questions as to how it works let me know and I’ll explain to the best of my abilities. To get this to work just drop the RainOcclusion folder into your project’s Content folder and you should be set. Put a BP_AttachedRain actor into your level and it should work automatically. Right now the rain covers a range of 20 meters around the player and the process of expanding it requires to change the following things:

  1. The Orthographic Width on the SceneCaptureComponent in BP_AttachedRain
  2. The TextureSize parameter in MF_RainDepthOpacity
  3. The Initial Location and Boundary of the particle system

You can substitute the rain particle with any particle you want. The only important part of the whole setup (both particle and particle material) is the call to MF_RainDepthOpacity in the material. As long as you add that to your own rain you can put that into the BP instead of the placeholder one I’ve set up.

To give a brief overview on how it works - the SceneCaptureComponent captures the scene depth into a render target. The render target is set to R32f meaning it only has a red channel and it’s 32 bits long. This means that the depth stores the actual centimeter distance from the SceneCaptureComponent instead of a black-to-white mask. This makes the math in MF_RainDepthOpacity quite a bit simpler.

The first part is to calculate the “depth” of the current particle. In order to do this we can’t just do PixelPosition - ParticlePosition because that’s a diagonal line that is only accurate for the particles along the central axis. This means that we have to project the location we’re testing onto the particle’s central axis. That’s what this bit is about:

It now becomes trivial to check if this depth (i.e. the vector length) is smaller than the depth. Since the depth is already stored in centimeters we don’t have to do any conversions at all. The tricky part is getting which pixel of the depth texture we’re testing against, i.e. calculating the UV. The math I have for this is actually finicky and seems to produce wrong results at certain angles so if anyone has any idea how to improve it I’d love to hear it.

The gist of it is this:

  1. Take the previously projected vector and subtract it from the pixel position (i.e. moving it “up” to the origin plane of the rain).
  2. Transform it into local space, filter out only X and Y.
  3. Normalize this to be between -0.5 and 0.5 in both axis.
  4. Add 0.5 to offset it to 0-1

It works at what seems +/- 30 degrees Pitch and Roll which should be more than enough for all rain scenarios, but again if someone has a better way to do this math bit please share!

Good stuff. Thanks for sharing! We will be testing this in our project soon.

Oh wow, this is great. I just tried it out and works really well.

I moved the components over to my character blueprint so I don’t need to have a separate actor following the character around and it works fine. There is a number of rendering features that can be turned off on the scene capture as well to improve performance further. Pretty much everything except for bsp, meshes, landscape and foliage really.

I also set up a rain particle effect in Niagara instead of Cascade, since that is the way of the future and all works fine with that too. This looks better and runs a lot faster than using CPU particles with collision, which is what I was doing before.

The only downside of the GPU particles is you lose the trace collision that the CPU particles had, which I was using to then spawn little splash particles where the rain hits the ground. However, I’ve come up with an idea that might work, just not 100% sure how to do it. Perhaps you can offer some advice.

I’m thinking of creating a splash particle effect that just spawns splash mesh particles at random (in the x,y plane) around the player up to a certain radius and let the material control the z. So in the splash mesh particle material, I could perhaps sample the same scene render target and use it to set the z position to render the splash at in world space. That way the splash will always render where the rain stops (i.e. on the ground or roof, etc…) if that makes sense. Do you think this would work? Any pointers on how I could specify the z position of a pixel for a splash mesh particle from the scene render target?

There’s actually a way to simplify the splash even further. Make the splash a separate emitter on the rain particle that spawns identically to the rain but modify its material calculation to not just do “if below depth, set opacity to 0” but rather have it be 0 below depth as well as above “depth - SplashThickness”. That way you only get it a few centimetres above the surfaces. Of course this has the disadvantage of rendering effectively invisible particles all around you at all times.

Edit: Actually this can be optimized. Have an emitter that spawns splashes and move them down to whatever the depth is using world position offset. I will try hacking this together.

Thanks for the share…

That is exactly what I’m trying to do, just explained it a bit different in my previous post.

I was thinking that the splashes could be spawned on the x,y plane around the player at z height zero, with a very short life time. Then just sample the scene depth capture texture to adjust the world position offset up/down to match the value in the render target texture. It is this bit I’m not quite sure about. Since the splash is an actual 3D mesh, you don’t want all it’s pixels to be rendered at the same height, which will flatten it. So each pixel needs to take into account the height and is z value of the mesh, so the splash mesh retains its shape.

Well I gave it a try. Forgot that the World Position Offset, is exactly that, an offset. So my previous concern about the mesh being flattened is of no concern. Ran into another problem though with splashes stretching along edges, where part of the splash reads one height and the rest reads a different height, so the whole splash mesh get stretched vertically.

I found a work around by using Particle Position instead of Absolute World Position. This way only a single depth is read for the entire particle. But for this to work right, the render target resolution has to be much higher, basically 1:1 with the capture size. Obviously not the best for performance.

The only other way I could think to get around this would be to take multiple depth samples around the particle’s position and use the lowest value. Should allow a lower resolution render target, but would result in a more expensive material, so bit of a toss up there for performance.

Unless you have some other ideas on how to solve this?

Hm, that would indeed be an issue… The only thing I can think of is to make the particles smaller but that’s not really a solution. It might be possible with Niagara, maybe… I’ll try and think of something as well.

I think you posted this in the wrong thread.

Indeed I did!

Hi DamirH and Team

Firstly I would like to thank you for this excellent rain solution, it is exactly what I was after.

My problem is that I cannot figure out how to make the splashes work even with the advice given in the last few posts.
I have tried to copy in the particle splash from SoulCave and SoulCity but it doesn’t work as expected (due to my inexperience I figured that would be the case).

I don’t suppose a kind soul could give some “noob” advice on how to get the splashes to work.

Thanks in advance.

Hi Guys

I don’t suppose anyone managed to get a working “splash” system working with this solution.
Everything is working well and I have managed to change the look of the rain etc, but still cannot figure out how to stop the splashes from happening inside buildings and under overhangs.

Does anyone have an example of a working solution they would be kind enough to share?

Thanks in advance.

Thanks for this excellent write up and sample files.

I hope it’s still something you’re contemplating. I found the original approach fascinating, and I would very much like to hear about more optimized and advanced ways of doing this.

I’ve updated it and posted the project link just after that post.