Creating Underwater effects in Unity3D 🤿

11k+ views Dec 25, 2017

So I started using Unity to create a simulation environment for a college team project. I had to create a swimming pool like structure where I could navigate a Capsule Solid RigidBody inside the water. It took me quite some time to implement, so hoping this helps someone out.


Setting up a pool by joining the cuboids appropriately didn’t take much time.

Making a pool with cuboids-1

I have used a purple plane like cuboid as the base/floor for the pool for consistency reasons.

Making a pool with cuboids-2


Next step would be to add a water surface. Water prefabs like WaterProDaytime, Water4Simple etc use an oval-shaped mesh for the water. If we want to use a different mesh, we can change it in the Mesh Filter of the water GameObject.

I have used WaterProDaytime for the environment.

WaterProDaytime prefab for pool water

The look and feel of the water surface can be modified from the inspector panel where we can choose the Shaders, colors, wave speed etc. for the water.

Now that we have added, the water surface, lets add a Capsule GameObject that we will be controlling.

Now as we can see, when the capsule is underwater, it doesn’t seem like it. Because till now, the prefab acts like a surface of water and not a water body.

Also, from below the water, the surface is not visible. Now to be able to do that, we need to add one more Prefab (copy-paste the original one), and rotate it 180° along the X axis.

We need to realise that two primary things simulate an underwater effect-

  1. A foggy effect with fog color and a fog density values.
  2. A caustics generated texture.


To be able to get the visual effect, we need to add a C# script to the Camera GameObject that has been positioned underwater. The script is pretty self explanatory-

using UnityEngine;
using System.Collections;
public class Underwater: MonoBehaviour {
	public float waterHeight;
	private bool isUnderwater;
	private Color normalColor;
	private Color underwaterColor;
 	// Use this for initialization
    void Start () {
		normalColor = new Color (0.5f, 0.5f, 0.5f, 0.5f);
		underwaterColor = new Color (0.22f, 0.65f, 0.77f, 0.5f);
	// Update is called once per frame
	void Update () {
		if ((transform.position.y < waterHeight) != isUnderwater) {
			isUnderwater = transform.position.y < waterHeight;
 			if (isUnderwater) SetUnderwater ();
 			if (!isUnderwater) SetNormal ();
 	void SetNormal () {
		RenderSettings.fogColor = normalColor;
		RenderSettings.fogDensity = 0.01f;
 	void SetUnderwater () {
		RenderSettings.fogColor = underwaterColor;
 		RenderSettings.fogDensity = 0.1f;


The effect generated by caustics can be seen as an array of similar images, displayed one after another, one per frame. We can generate these images from applications like this.

Now we need a GameObject that can project these images on the water. We can use a LightProjector to do so and attach a script that takes input of images in an array and renders them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WaterEffect: MonoBehaviour {
	public float fps=30.0f;         //footage fps
	public Texture2D[] frames;      //caustics images
 	private int frameIndex;
 	private Projector projector;    //Projector GameObject
 	void Start(){
 		projector = GetComponent<Projector> ();
        	InvokeRepeating ("NextFrame", 1 / fps, 1 / fps);
 	void NextFrame(){
     		projector.material.SetTexture ("_ShadowTex", frames [frameIndex]);
     		frameIndex = (frameIndex + 1) % frames.Length;
Loading images into the array

Now all we need to do is to place the projector appropriately so that the caustics can be projected across the whole water body.

Final underwater appearance

I had very a limited time frame to complete this task and certain links and videos on the internet helped a lot. They are-

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.