8
\$\begingroup\$

I am trying to create a waterfall that looks similar to the first image below (please check this video for a better idea of what I want to achieve) with physics properties so it can move or around objects with colliders on its course (similar to the second image below). Though the linked waterfall is 3d I am interested in a 2d implementation.

Note that the objects are dynamic and some move frequently hence the waterfall has to reshape every frame. The objects move slowly either moving to different positions or rotating. How can this be done?

enter image description here

enter image description here

\$\endgroup\$
13
\$\begingroup\$

I wanted to see if I could pull this off without dynamically regenerating the mesh for the waterfall every frame. It turns out, there is a way. :D

Animation of cascading waterfalls

Each object that can block the waterfall (objects bearing a WaterCatcher script in my prototype) has an outline mesh wrapping around its perimeter. (This can be auto-generated in advance using the collider's shape)

This outline mesh renders the water flowing along the object. I use a shader to clip out the part that's beneath the object. I also track a left and right "catch" point where a waterfall lands on the object and flows left or right respectively, so I can clip out the part that's to the left of the right waterfall and to the right of the left waterfall.

Animation showing water skin mesh

Then the vertical falls are just basic quad primitives, stretched to the appropriate length. I use another shader to scroll the waterfall texture over the falls and fade it out at the top & bottom ends. Then I layer on a foam particle system at the impact point to help cover the blend.

Here's a close-up so you can see the component parts.

Close up still of waterfall effect

At the top I have a "root" waterfall to kick things off. Each frame, after all the Update() scripts have run to move things around, it fires a CircleCast downward, to see if its water hits anything. If it hits a WaterCatcher, it tells it to show its water skin downstream of the hit point.

I determine "downstream" using the hit normal - if it's very close to vertical, or if the incoming waterfall spans edges that slope in both directions, then we spill both left and right.

Each WaterCatcher has a left and a right waterfall of its own, which it enables and positions on its far edge if it's spilling in that direction - otherwise they stay hidden. These waterfalls in turn fire CircleCasts downward to find what they spill onto, and so on...

The prototype still has a few visual glitches that could be improved - the water flow along an object pops on all at once instead of animating, and the flow rules could use a little extra tolerance or hysteresis so it doesn't cut off as easily on rotating objects. I think these should be fairly solvable issues though.

Background,rocks, and rotating platform textures via Kenney


Here are the tricks I use in my water catcher fragment shader:

// My wraparound geometry is build so the "x+" UV direction
// points "outward" from the object.
// Using derivatives, I can turn this into a vector in screen space.
// We'll use this below to clip out water hanging off the bottom.
float2 outward = float2(ddx(i.uv.x), ddy(i.uv.x));

// i.worldX is the worldspace x position of this fragment
// (interpolated from the vertex shader)

// _LeftX is a material property representing the worldspace x coordinate
// of the rightmost water flow that's spilling left,
// and _RightX the wold x of the leftmost water flow that's spilling right.
float left = _LeftX - i.worldX;   // +ve if we're to the left of a left spill.
float right = i.worldX - _RightX; // +ve if we're to the right of a right spill.

float limit = max(left, right); // +ve if we're in the path of either flow.

// If the "outward" vector is pointing down, make this negative.
limit = min(limit, outward.y + 0.001f);

// If any of the conditions above make limit <= 0, abort this fragment.
clip(limit);

// Otherwise, scroll the water texture!
// Counter-clockwise if we're in the left flow, clockwise otherwise.
i.uv.y -= sign(left) * _Time.y;
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ This is great. Do you have any plans of providing the code here or on GitHub so the community can contribute to making improvements? If not, please consider doing so. \$\endgroup\$ – Containment Mar 27 '18 at 21:00
  • \$\begingroup\$ No such plans at present. The images above were created with a quick & hacky proof of concept, not a standalone package that would be suitable for that kind of use. Let me know if you need a hand replicating any of it and I can walk you through what's needed. \$\endgroup\$ – DMGregory Mar 28 '18 at 2:07
  • \$\begingroup\$ I have been able to make the foam particle but I need help creating the water shader and secondly, clipping the water shader around the shapes (WaterCatcher). \$\endgroup\$ – Containment Mar 28 '18 at 5:33
  • \$\begingroup\$ I've added a snippet of shader code showing how the clipping shader works. \$\endgroup\$ – DMGregory Mar 28 '18 at 23:14
  • \$\begingroup\$ @DMGregory WoooW!!! Sorry, haven't been here for a while. Was working on some other stuff. I am now going through your solution \$\endgroup\$ – OnlyCodeMatters Apr 4 '18 at 11:32
1
\$\begingroup\$
  1. You can deform the waterfall mesh on object collision to match the required collider pattern.

  2. The easier one and more accurate, but more performance heavy - use particle system - create a particle system with colliders and use every particle as a drop of water. But that looks a bit odd if you have default sprite and if the particle count is small and they are too big. But it's performance heavy, so you don't want molecule simulation in you game.

I would go with 1.

  • But Mesh deformation CPU - it's slow, but may work for you.
  • I would use shaders to achieve this - Water shader example - mesh has effects like it collides with other meshes and it's way faster than methods before. I guess it's possible to make mesh to stop the render in some projected shape - that is the change you would need to make to normal water shader, it's complicated to achieve if you are not familliar with shaders.

No easy solution with good performance.

Result of particle system: (To change the values I had to wait about 3-4s, it's slow) Particle System Collision Result

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Alternative to point 1 but might be a bit more complex is having your own "particle" system that drops points/sphere colliders and abides by physics. And then "simply" generate a mesh based on those points. pretty much how line renderer works. The only thing that needs to be solved is how you'd split points to go 2 directions when hitting a collider like it does in your screenshot. But don't think it needs to be hard. OR you could just place the points statically and draw a line between them. Ya just need a pretty shader. \$\endgroup\$ – Sidar Mar 16 '18 at 9:53
  • \$\begingroup\$ @Sidar Yeah, interesting approach. One of the methods would also be to create multiple trail renderers and move them from the split point relatively. And check for collisions via some collider component - but that is just workaround. \$\endgroup\$ – Candid Moon _Max_ Mar 16 '18 at 10:07
  • \$\begingroup\$ Eitherway if OP doesn't want to set it up manually the data needs to be dynamic or statically baked. I wonder if you could use a normal particle system with collision enabled and then proximate between points to draw a quad strip. Particle count doesn't have to be high for this. \$\endgroup\$ – Sidar Mar 16 '18 at 10:24
  • \$\begingroup\$ @Sidar I think Unity's particle system now supports particle ribbons/trails out of the box \$\endgroup\$ – DMGregory Mar 16 '18 at 11:12
  • \$\begingroup\$ Isn't that per particle tho? I guess depending on the style you're going for that could working pretty well. \$\endgroup\$ – Sidar Mar 16 '18 at 11:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.