[RELEASE] Calm Water

Humm to me it looks like some depth buffer precision issue due to a large scene, but I did not manage to reproduce the problem on my side even with a 4000x4000 plane.

I have redone how the depth calculation is done in the shader, I will send you an update in private to see if it helps

Hi!
It is suitable for 2.5D water cubes (2.5D tile based game)?

Hey,
I don’t thinks this would be the best, depending on the look you want. I think you would rather need something like this shader I made a while ago.

https://www.youtube.com/watch?v=fht9uqlwV5U

1 Like

It took me a while, but in the end it was simple. On my water plane I forgot to switch off “cast shadows”. Now it works fine, even with a 2000x2000 plane.

I still have to do some tweaking, but I’m really happy so far. Rated it 5/5 in asset store. Again, thanks for your response.

ho I see!
well, glad you like it!

Hey YanVerde, for some reason I cannot update your asset to 1.52. Actually, when I try to re download it, I can only do version 1…0 which shows up in the asset panel. Any ideas, I think I have 5.2.3 installed.

Hey,
Could it be because I submitted version 1.0 with 5.2.3 and other versions were submitted using version 5.3 and up?
I didn’t know you could not download it with an earlier version of unity. I will resubmit using a lower version then, in the meantime you can update your unity version I guess.

Thanks for the feedback!

That makes sense, I’ll look at updating to 5.3. Cheers for the reply.

Hello YanVerde,

I’ve found a little bug.
Stand Unity terrain is not selectable in scene whilst Mirror Reflection script is enabled on water.
Only happens in Unity 5.3.3 and 5.3.3p1

Kind Regards
Brity

Hi,
Long story short, I think this is a bug on unity’s side, but they updated their water reflection script to work around that as well. So that makes me wonder if this is a known issue and that they won’t fix it.

I still updated my script to match unity’s behavior, but that means it won’t be possible anymore to have realtime reflections in scene view when not in play mode anymore.

I will try to submit a bug report and see what they tell me.
Here’s the updated script for now.

using System;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
[RequireComponent(typeof(MeshRenderer))]
public class MirrorReflection : MonoBehaviour
{

    //private RenderTexture rt;
    public LayerMask reflectionMask = -1;
    [Tooltip("Color used instead of skybox if you choose to not render it.")]
    public Color clearColor = Color.grey;
    public bool reflectSkybox = true;
    public bool m_DisablePixelLights = false;
    [Tooltip("You won't be able to select objects in the scene when thi is active.")]
    public bool UpdateSceneView = false;
    public float clipPlaneOffset = 0.07F;


    private String reflectionSampler = "_ReflectionTex";



    Vector3 m_Oldpos;
    Camera m_ReflectionCamera;
    Material m_SharedMaterial;
    Dictionary<Camera, bool> m_HelperCameras;


    void OnEnable(){
        setMaterial();
    }

    void Start(){
        setMaterial ();
    }

    public void setMaterial(){
        m_SharedMaterial = GetComponent<Renderer>().sharedMaterial;
    }


    Camera CreateReflectionCameraFor(Camera cam)
    {
        String reflName = gameObject.name + "Reflection" + cam.name;
        GameObject go = GameObject.Find(reflName);

        if (!go)
        {
            go = new GameObject(reflName, typeof(Camera));
            go.hideFlags = HideFlags.HideAndDontSave;
        }
        if (!go.GetComponent(typeof(Camera)))
        {
            go.AddComponent(typeof(Camera));
        }
        Camera reflectCamera = go.GetComponent<Camera>();

        reflectCamera.backgroundColor = clearColor;
        reflectCamera.clearFlags = reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;

        SetStandardCameraParameter(reflectCamera, reflectionMask);

        if (!reflectCamera.targetTexture)
        {
            reflectCamera.targetTexture = CreateTextureFor(cam);
        }

        return reflectCamera;
    }

    void SetStandardCameraParameter(Camera cam, LayerMask mask)
    {
        cam.cullingMask = mask & ~(1 << LayerMask.NameToLayer("Water"));
        cam.backgroundColor = Color.black;
        cam.enabled = false;
    }


    RenderTexture CreateTextureFor(Camera cam)
    {
        RenderTexture rt = new RenderTexture(Mathf.FloorToInt(cam.pixelWidth * 0.5F),
            Mathf.FloorToInt(cam.pixelHeight * 0.5F), 24);
        rt.hideFlags = HideFlags.DontSave;
        return rt;
    }


    public void RenderHelpCameras(Camera currentCam)
    {
        if (null == m_HelperCameras)
        {
            m_HelperCameras = new Dictionary<Camera, bool>();
        }

        if (!m_HelperCameras.ContainsKey(currentCam))
        {
            m_HelperCameras.Add(currentCam, false);
        }

        if (m_HelperCameras[currentCam] && !UpdateSceneView)
        {
            return;
        }

        if (!m_ReflectionCamera)
        {
            m_ReflectionCamera = CreateReflectionCameraFor(currentCam);
        }

        RenderReflectionFor(currentCam, m_ReflectionCamera);

        m_HelperCameras[currentCam] = true;
    }


    public void LateUpdate()
    {
        if (null != m_HelperCameras)
        {
            m_HelperCameras.Clear();
        }
    }


    public void WaterTileBeingRendered(Transform tr, Camera currentCam)
    {
        RenderHelpCameras(currentCam);

        if (m_ReflectionCamera && m_SharedMaterial)
        {
            m_SharedMaterial.SetTexture(reflectionSampler, m_ReflectionCamera.targetTexture);
        }
    }


    public void OnWillRenderObject()
    {
        WaterTileBeingRendered(transform, Camera.current);

    }


    void RenderReflectionFor(Camera cam, Camera reflectCamera)
    {
        if (!reflectCamera)
        {
            return;
        }

        if (m_SharedMaterial && !m_SharedMaterial.HasProperty(reflectionSampler))
        {
            return;
        }

        // Optionally disable pixel lights for reflection
        int oldPixelLightCount = QualitySettings.pixelLightCount;
        if (m_DisablePixelLights)
        {
            QualitySettings.pixelLightCount = 0;
        }

        //reflectCamera.cullingMask = reflectionMask & ~(1 << LayerMask.NameToLayer("Water"));
        reflectCamera.cullingMask = ~(1<<4) & reflectionMask.value; // never render water layer

        SaneCameraSettings(reflectCamera);

        reflectCamera.backgroundColor = clearColor;
        reflectCamera.clearFlags = reflectSkybox ? CameraClearFlags.Skybox : CameraClearFlags.SolidColor;
        if (reflectSkybox)
        {
            if (cam.gameObject.GetComponent(typeof(Skybox)))
            {
                Skybox sb = (Skybox)reflectCamera.gameObject.GetComponent(typeof(Skybox));
                if (!sb)
                {
                    sb = (Skybox)reflectCamera.gameObject.AddComponent(typeof(Skybox));
                }
                sb.material = ((Skybox)cam.GetComponent(typeof(Skybox))).material;
            }
        }

        GL.invertCulling = true;

        Transform reflectiveSurface = transform; //waterHeight;

        Vector3 eulerA = cam.transform.eulerAngles;

        reflectCamera.transform.eulerAngles = new Vector3(-eulerA.x, eulerA.y, eulerA.z);
        reflectCamera.transform.position = cam.transform.position;

        Vector3 pos = reflectiveSurface.transform.position;
        pos.y = reflectiveSurface.position.y;
        Vector3 normal = reflectiveSurface.transform.up;
        float d = -Vector3.Dot(normal, pos) - clipPlaneOffset;
        Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);

        Matrix4x4 reflection = Matrix4x4.zero;
        reflection = CalculateReflectionMatrix(reflection, reflectionPlane);
        m_Oldpos = cam.transform.position;
        Vector3 newpos = reflection.MultiplyPoint(m_Oldpos);

        reflectCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;

        Vector4 clipPlane = CameraSpacePlane(reflectCamera, pos, normal, 1.0f);

        Matrix4x4 projection = cam.projectionMatrix;
        projection = CalculateObliqueMatrix(projection, clipPlane);
        reflectCamera.projectionMatrix = projection;

        reflectCamera.transform.position = newpos;
        Vector3 euler = cam.transform.eulerAngles;
        reflectCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);

        reflectCamera.Render();

        GL.invertCulling = false;

        // Restore pixel light count
        if (m_DisablePixelLights)
        {
            QualitySettings.pixelLightCount = oldPixelLightCount;
        }
    }


    void SaneCameraSettings(Camera helperCam)
    {
        helperCam.depthTextureMode     = DepthTextureMode.None;
        helperCam.backgroundColor     = Color.black;
        helperCam.clearFlags         = CameraClearFlags.SolidColor;
        helperCam.renderingPath     = RenderingPath.Forward;
    }


    static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane)
    {
        Vector4 q = projection.inverse * new Vector4(
            Sgn(clipPlane.x),
            Sgn(clipPlane.y),
            1.0F,
            1.0F
        );
        Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));
        // third row = clip plane - fourth row
        projection[2] = c.x - projection[3];
        projection[6] = c.y - projection[7];
        projection[10] = c.z - projection[11];
        projection[14] = c.w - projection[15];

        return projection;
    }


    static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, Vector4 plane)
    {
        reflectionMat.m00 = (1.0F - 2.0F * plane[0] * plane[0]);
        reflectionMat.m01 = (- 2.0F * plane[0] * plane[1]);
        reflectionMat.m02 = (- 2.0F * plane[0] * plane[2]);
        reflectionMat.m03 = (- 2.0F * plane[3] * plane[0]);

        reflectionMat.m10 = (- 2.0F * plane[1] * plane[0]);
        reflectionMat.m11 = (1.0F - 2.0F * plane[1] * plane[1]);
        reflectionMat.m12 = (- 2.0F * plane[1] * plane[2]);
        reflectionMat.m13 = (- 2.0F * plane[3] * plane[1]);

        reflectionMat.m20 = (- 2.0F * plane[2] * plane[0]);
        reflectionMat.m21 = (- 2.0F * plane[2] * plane[1]);
        reflectionMat.m22 = (1.0F - 2.0F * plane[2] * plane[2]);
        reflectionMat.m23 = (- 2.0F * plane[3] * plane[2]);

        reflectionMat.m30 = 0.0F;
        reflectionMat.m31 = 0.0F;
        reflectionMat.m32 = 0.0F;
        reflectionMat.m33 = 1.0F;

        return reflectionMat;
    }


    static float Sgn(float a)
    {
        if (a > 0.0F)
        {
            return 1.0F;
        }
        if (a < 0.0F)
        {
            return -1.0F;
        }
        return 0.0F;
    }


    Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
    {
        Vector3 offsetPos = pos + normal * clipPlaneOffset;
        Matrix4x4 m = cam.worldToCameraMatrix;
        Vector3 cpos = m.MultiplyPoint(offsetPos);
        Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;

        return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
    }
}

Thanks!

Hey @Phantom_X

Potentially stupid question. Just wondering how this plugin goes with Orthographics cameras? Not a rendering/shader guy and was wondering if there was an easy way to get the same result in ortho mode as in perspective? Thanks!

Beautiful! Can you make the water splash (particle or whatever) when a character walk/run in it? it’s the only thing left I need XD

Hey,
Unfortunately this won’t work in ortho at the moment, not sure if it is possible, will have a look!

Thanks

Hi,
I have more urgent things to do for it at the moment. This doesn’t mean I will never do some, but at the moment I won’t no.

Thanks!

Hello, I’m having a problem.
I deleted calmwater from my project and then imported the updated version.
Now I keep getting error with InControl asset…
Assets/ADDONS/Physics Platformer Kit/Scripts/PlayerMove.cs(297,37): error CS0117: InputManager' does not contain a definition for Devices’

I have no idea why this asset is causing problems with that one.
Any ideas?

Do you have any other errors? maybe one of it’s class has the same name as one of mine? I do have an InputManager.cs in the demo scene, try deleting this file. My demo scenes won’t work properly, but it might fix your problem.

Are you serious… you named your class InputManager… Are you stoned?
oh dear… I lost an hour for this…

I guess we could say the same from Physics Platformer Kit who also name it that way apparently, but I will change that for next version of course

no it wasn’t Physics Platformer Kit, it was conflicting with InControl which is an asset that specializes in Input controls so you would expect them to call it that, because you wouldn’t have two input control systems in your project. Yours isn’t…

Alright my bad, it will be changed.