Hollow Boat Effect

I'm creating a thread to discuss the Hollow Boat Effect as described in

Card: https://open.codecks.io/unity-open-project-1/decks/16-3d-shading/card/18x-hollow-boat-effect

The problem consists of hiding the water inside the boat while still rendering the water outside the boat.


@cirocontinisio suggested we use the Renderer Features to solve this problem and as I have no experience with it, I'm interested in working on this problem.

I could be way off since I'm new to this too, but maybe some variation of the character occlusion solution might fix the problem? Like make a layer for the water separate from objects in the water and play around with the opaqueness/transparency of each layer?

1 Like

As I imagine a cutscene with our protagonist inside the boat while arriving at the island, my objective is to remove the water from this picture:


The outside of the boat should still appear under the water while should exist no water inside the boat.

I have little experience with graphics. After some research, the only way I could think of solving this problem was creating some kind of mask plane that specifies that the water shouldn't be drawn in some pixels.

(I'll continue the post explaining what I did so far)


I decided to use Stencil Shaders inspired by these posts, I decided to use Stencil Shaders. Also, I never used nothing like this, so it was a good opportunity to learn. (Although, I'm not sure this is the best approach. But seems okay...)



First I created a temp geometry that will work as a mask


Then I created a new layer "HideWaterMask" for it:


I also put the Water GameObject in the "Water" layer

Then, in the ToonRendererData file, I removed the "HideWaterMask" layer from OpaqueLayerMask and the "Water" layer from the Transparent Layer Mask.


Then I created two new Renderer Features

The first one writes on the Stencil Buffer using the layer "HideWaterMask" as... as a mask.


(I will continue on the next post)


The second Renderer Feature renders the Water but ignores the pixels previously marked on the Stencil Buffer.


Lastly, I had to create a shader to make the mask geometry "invisible". I really tried to come up with a way of doing so without using any code. But I think it's not possible doing this in shadergraph and any other attempt I did, failed. If anyone knows how to do this, please comment here.

With no idea of how to make it very friendly to everybody, I wrote a very small shader (I know very little about writing shaders, so comment if I'm making some bizarre mistake)

Shader "UOP1/DepthMask"

        // Don't draw in the RGBA channels
        ColorMask 0
        // Don't write in the Depth Buffer
        ZWrite Off

        // Do nothing specific in the pass
        Pass {}

Finally, I created a material, turned off the shadow for the mask geometry and this is the result I got so far.


It seems fair :)

I'll wait for the community feedback. What do you think? If I didn't make any mistake or overcomplicated this problem, I'll clean the project and I'll create a final mask geometry for the boat. Then, I'll open a PR.




I could achieve the same effect using the Depth Buffer instead of the Stencil Buffer.
I don't know which approach is the best fit for this problem or which one has better performance. I chose to use the Stencil Buffer only because I didn't know anything about it and it looked cool.

Hi @erizzoalbuquerque , great job! I think your approach is great because water is still properly reacting around the boat creating foam. I think it is perfectly aligned with what @cirocontinisio was suggesting during the livestream.

No, it's great. But you don't need a new shader for it, you should be able to do it all with the Render passes.

In the HideWaterMask, the compare function is Always. This means it will always return true, and thus, draw entirely. Then you override this by providing an invisible shader.

The way you could do it is to make it fail always, so it never draws. But then in the "Fail" option below it, you set the stencyl value to Replace (rather than Keep as it is now).
Don't take me too literally because I haven't tried it in the editor, but it's something along these lines.

Also, keep in mind that I've integrated @shuttle127 's occlusion solution, so we have some extra render passes in there. So I suggest you pull the latest main or art-assets before making these modifications, or you'll (I will, really) get a conflict when we merge.

Thanks, @treivize ! Yeah, it took some time to come up with a solution that would draw the foam, draw the boat underwater, and would allow having the protagonist correctly drawn inside the boat. I'm glad it worked :)

Hey, @cirocontinisio ! Thanks for the directions. I tried doing something like this yesterday but it didn't work. Now, I tried again and it worked. Yesterday, I was using a random white material that was already in the project to make this test. It seems the material was using (the material used for line rendering) was the culprit. Using a URP unlit material made it work. Actually, I don't even need a material set in the Mesh Renderer and it still works. Thanks for the tips! I'll remove the shader I coded from the project. We don't need it anymore.

This is the new config


I also made it not write on the depth buffer because there's no need I think.

Yeah, it's true. @shuttle127 's PR was merged after I pulled the project. I forgot. I will update and merge everything before opening the PR. Thanks everyone for your support!

1 Like

I fixed everything and I opened the PR:


1 Like

I'm having issues with lighting (and shadows) passing through the masking layer, why is this?