I am working on masking/removing parts of a plane where it intersects with other objects. The goal is to be able to remove water from inside a submarine with a big window and the submarine is surfaced, like this:
I’ll work on the underwater part later, but right now I’m just trying to keep the water plane out of objects intersecting with it. So far, I have a Shader Graph that alpha masks objects except it seems to be doing it at any level and not during intersection. This gives the look that everything is always rendered on top of the plane, but I’m pretty sure it is masking (see shader graph below). Sorry if my terminology is off - I’m pretty new at shaders/shader speak.
Unfortunately it’s not really quite that easy. There are a few ways to go about this, but most of the options are not particularly LWRP Shader Graph friendly as a warning.
I’ll start off by saying the camera depth buffer texture is not useful in accomplishing this effect. So we’ll skip that for now.
The first method I’d suggest requires careful ordering of multiple passes. You draw your “boat”, then draw an invisible cap on top of the boat, and then draw the water. This video shows off someone doing it using the built in forward renderer: Direct Youtube Link since at the time I posted this the embed was broken.
This only really works if your camera is always above the water plane and outside of the boat. If you can get your camera in a spot that’s inside the boat and underwater, this technique totally fails. So the first example image would not be possible to reproduce with this technique.
The second option would be to use signed distance fields. This requires using either simple shapes (spheres, capsules, boxes, etc.) to create 3D volumes with that you can cut into the water plane with, or a 3D texture that holds the shape you want. Spheres are pretty easy and cheap to do, but Shader Graph doesn’t make this too easy since presumably you’d want multiples of these, and Shader Graph doesn’t have any support creating node graphics of dynamic loops. So this would require a custom node.
Wow, thank you bgolus for pointing me in the right direction. I switched over to the HDRP and I am reading up on signed distance fields now. I think I’ll probably go with the second approach since it will be in a submarine, so the player could be underwater.
Question - If I just had one big sphere for the submarine cockpit, would I be able to get away without having to do dynamic loops?
Sure, if you just always assume a single sphere, that’s easy enough. Pass in a float4 parameter to your water shader using a script to define a world position and radius. Subtract the parameter’s XYZ position from the world position, get the length, subtract the radius, and set that as your alpha with a alpha clip threshold of 0.0
I don’t think you could have made that any easier for me! Thank you so much for your help bgolus! I got the effect I was looking for. Posting what I have for others to use:
And for the _domeparams property, set the position and radius of the sphere by calling this in a script attached to the sphere:
_waterMat.SetVector("_domeparams", new Vector4(transform.position.x, transform.position.y, transform.position.z, radius));
The distance node does length(vectorA - vectorB).
Length gets you the length of a vector, but the output of the distance node is a float. The length of a float is equivalent to the abs() of the value, and the distance node is already guaranteed to be outputting a positive value, so it’s just wasted cycles. Best case it’s an extra abs(), which is cheap. Worse case it’s a extra square root, which is not.
Alright, so now I have the surface water look that I want. I might spend some more time adding detail, but for now it’s working and looking alright (I think?).
Now I would like to start focusing on the underwater setup. What is considered the best way to do this, knowing that the camera could show a split view of above and below the water surface from inside the glass sphere? I’m looking to achieve underwater fog (what’s the proper name for this, btw?) and volumetric lighting at the very least. For the volumetric lighting, in case I’m using the wrong term, I would like the surroundings to get darker the deeper you go.
Also, since I am so new to shaders, I feel like I would do better if I learned a bit more instead of just jumping into things like this. Is there some good resources that anyone knows of that helped you learn? Or is it better to just experiment as I go?
First thing is dealing with the water “edge” on the glass. The easiest solution is to do nothing, but some games have used either the geometry off the glass itself, or some other proxy geometry just behind the glass, to draw the water edge on. This same plane is then also used to handle the underwater fog & volumetric effects. See this presentation on water tech in Uncharted 3, starting around page 103: https://www.gdcvault.com/play/1015309/Water-Technology-of
There are a number of links to beginner stuff on the forums that may help you wrap your head around the ins and outs of basic shaders. Then there’s a giant empty void of confusion. And then there’s various talks like those I linked above as well as others of similar ilk and ShaderToy.com.
I don’t have any good answers for getting over that hump in the middle. Usually someone coming into shaders says “I have this simple effect I’d like to do that’s like this:” and then they go on to show some screenshots and describe an effect from a AAA game that likely took a couple of years of trial and error by senior graphics programmers and effects artists to produce. What you’re trying to do is not too far off of that. The underwater rendering is many very complex effects all being layered together, and it should be noted that Uncharted 3 faked all of it and still produced something that looks better that many games made recently.
Yeah, I figure I’ll just break this down into bite size chunks as best as I can.
Using the geometry of the glass itself sounds like the direction I want to go. I found this depth shader tutorial a couple of days ago that (I’m guessing) will help with this:
Thank you for the gdcvault links! This looks like a really good resource for learning different techniques. As for ShaderToy, I just came across this the other day when going through a beginner shader tutorial! Very helpful and very fun!
Hahaha! I feel like this is me…right here in this thread lol. Water in games is just so interesting to me. I know this will be hard though - last year I wanted to make a submarine game and so I bought up a few of the top water assets and only one got close to having a good volume subtracted underwater effect (Playway, now deprecated). Good thing this is a hobby haha. Anyways, thank you for all your help thus far!
Sorry to jump around, but I’m working on rendering the water depth and I think it’s almost done. My issue is that the depth seems to change when the camera is moved up and down. I tried subtracting camera position from the water vertical position, but that didn’t work. The depth also seems to change when the camera direction changes.
That should keep the fog consistent on when the view is rotating in place or changing its distance from the glass, but this is calculating the distance between the polygon surface and the scene depth behind it in view space, not depth of the water in world space like you might want for having it get darker as it gets deeper, vs just distance fog. For that you have to reconstruct the world space position from the scene depth. That’s a bit more tricky to do, though still possible.
Hello, I have seen your discussion is very good, I would like to ask if you can teach me how to use the geometry behind the glass to make the water volume in detail, especially how to track the uneven waves with shader
How would you invert this? im trying to use this to fade alpha out as the tiled texture on a giant plane gets farther away. Right now its fades away everything close to the camera and i figured removing the one minus at the end of your chain would do it but… nope.
I am trying to replicate this effect in my own project. I tried in HRDP and URP but I can’t seem to get it working. I was wondering if you have since updated this to HDRP and maybe there is some checkbox I am missing. I have a thread open here, any insight would be appreciated.
using UnityEngine;
[ExecuteInEditMode]
public class CullMe : MonoBehaviour
{
public Transform culler;
public float radius = 1;
void Update()
{
if (!culler)
return;
var renderer = GetComponent<Renderer>();
if (!renderer)
return;
var material = renderer.sharedMaterial;
material.SetVector("_CullerParams", new Vector4 (culler.transform.position.x, culler.transform.position.y, culler.transform.position.z, radius));
}
}