This purpose of this post is to give some insight in to my 2d water effect. The effect was made in unity and consist of one C# script and two shaders. Additionally this effect can be be broken down in to two parts. The reflection and the distortion. First we will look at the reflection
Reflection
A lot of the work of the reflection is done through the C# script. The script creates a new game object with the sprite renderer of the original object, mirrors it, and puts our water reflection shader on it. You can see it below.
A couple things to note with this technique. First, everything done in this script could be done manually through the editor. It is only for convenience and does not have to be done at run time. You might want to add [ExecuteInEditMode] to this script if you plan on using it. Second, this is kind of a crude way of doing things. Animations, lighting, or anything else that effect your sprite will not get reflected. For animations you could use the script to copy the animator. Then use it as a wrapper for your animator to keep the two animators in sync. That is what I do in the gif below. But if you have a lot going on this could get messy quick. For any more advance scenarios you may be better off using multiple cameras and render textures.
A couple things to note with this technique. First, everything done in this script could be done manually through the editor. It is only for convenience and does not have to be done at run time. You might want to add [ExecuteInEditMode] to this script if you plan on using it. Second, this is kind of a crude way of doing things. Animations, lighting, or anything else that effect your sprite will not get reflected. For animations you could use the script to copy the animator. Then use it as a wrapper for your animator to keep the two animators in sync. That is what I do in the gif below. But if you have a lot going on this could get messy quick. For any more advance scenarios you may be better off using multiple cameras and render textures.
Animation |
Now the sprite is reflected but it will always be there. Independent of water.
Reflection with out our soon to be explained shaders |
To make the reflection only show up on water we will use the stencil buffer, We will have to make two separate shaders to do this. This water surface shader and the reflection shader. Both of these shaders will be based off the default unity sprite shader. Any thing that you want the reflection to show up on should use the surface shader. Any thing that you want to show up as a reflection should use the reflection shader.
Add this to the water surface shader
Add this to the water reflection shader
The stencil buffer basically sets a flag when the water surface shader passes. Then when the reflection shader passes it throws out all pixels that don't have the flag set. Resulting in reflections only on top of water. The stencil buffer is a little more complex than that but that is all we need to know for this effect. Finally since the water reflection shader uses the default unity sprite shader we can lower the alpha a bit in the editor to blend it better. Reflection complete!
Distortion
The distortion effect is done completely through the reflection shader. The basic idea is that we sample from different positions of a distortion texture over time to displace the pixels in our sprite texture. The distortion texture could be any thing but since it is a water effect a water normal map works well.
In our reflection shader we will add properties for our distortion texture, the scroll speed, and the magnitude of the effect.
Water Normal Map |
We will also want to get world pos in our vertex shader. We will use this for the position to sample our distortion texture so that it will move as the game object does.
Finally, in our fragment shader we will sample from the distortion texture and use it to displace pixels in our sprites texture.
Finally, in our fragment shader we will sample from the distortion texture and use it to displace pixels in our sprites texture.
Here is the full code for the surface and reflection shaders
Hey, can I email you on how to get this working?
ReplyDeleteThis comment has been removed by the author.
DeleteHi! This is pretty cool and almost exactly what I was looking for; any chance you could clue me in how make the distortion snap to pixel int locations? I've been trying for days and may have been on the right path somewhere along the way but at this point I'm pulling my hair out. Thanks for this regardless!
ReplyDeleteHi,
DeleteIts been awhile since I looked at this but, my initial solution would be the following.
In the fragment shader we are using "IN.worldPos.xy * .1" I think we want to get rid of the point 1 part and round the x and y values of worldPos to ints. Hopefully that does the trick.
I've been trying to follow this tutorial for 3 days now and every time I hit a hurdle. The distortion seems to only offset the sprite diagonally downwards, affecting every part of the sprite the same amount. Since the animation is a sliced up sprite sheet, if I push the distortion slider far enough I can actually see other frames of the animation due to the offset.
ReplyDeleteAny advice? I haven't been able to perceive the wobbly distortion affect at all. It's the fragment section that's really throwing me.
Thanks!
Hi, sorry for the late reply. It sounds like it could be a scaling issue. In the below line in the fragment shader the .1 really should have been a parameter. My suggestion would be to toy around with that value.
Deletefloat2 disp = tex2D(_DisplaceTex, (IN.worldPos.xy * .1) + frac(_Time.x * float2(_SpeedX, _SpeedY))).xy;
Another issue could be the import settings you are using for your normal map. I forget the exact settings that I used but you might want to look in to those.
Hello. Could you please contact me on my email petrlik142@gmail.com ? I may need help if you have a second :) Thank you
ReplyDeleteHi thank you for this post. It helped me to get my reflections done. While iam using the technique with more animators, like an own object which gets animated.
ReplyDeleteThe rendertexture approach wasnt working for me because of camera movement. so it was never aligned.
Could you post a way using this with a rendertexture ? Would be greatly appreciated. While i dont think that after three years you are still interested in doing this.
Hi, and if you reply, could you send a mail to justin.heinen@live.de ?
DeleteIam not using my google account at all.
Thank you!
Best bk8 Casinos of 2021 | vntopbet.com
ReplyDeleteThe 더킹카지노 best BK8 casinos for you and why you should not 우리카지노 join our list. Betshoot.com · Online Casino · Slot Games bk8 · Slot Machines · Table Games · Games.