Water Pro: Reflection appears/disappears

Hello, I’m having an issue where the standard water pro (nighttime water in this case) looks great but the reflections of trees (and other landscape/objects) seems to pop in and out. If you go too far, it disappears even though it’s still visible (and the object itself is visible).

Is there a way to fix this? I can’t seem to toggle anything in the standard script to make it work properly.

Thanks!!

VIDEO EXAMPLE: http://www.sfbaystudios.com/reflectionissue.mp4

Attaching this script to the water pro instead of the “Water” script fixed the problem for me:

https://drive.google.com/file/d/0B7oyaEpzytHbVU5mY0w1NVVTWUk/view?usp=sharing

It disables occlusion though, unfortunately.
Found this solution from Unity Issue Tracker - Water reflection disappears if occlusion culling is baked in the scene

Google drive link doesn’t work, here’s good one: https://forum.unity.com/threads/unity-5-occlusion-culling-water-reflection-flicker.347407/

using System;
using System.Collections.Generic;
using UnityEngine;
 
namespace UnityStandardAssets.Water
{
    [ExecuteInEditMode] // Make water live-update even when not in play mode
    public class Water : MonoBehaviour
    {
        public enum WaterMode {
            Simple = 0,
            Reflective = 1,
            Refractive = 2,
        };
        public WaterMode m_WaterMode = WaterMode.Refractive;
        public bool m_DisablePixelLights = true;
        public int m_TextureSize = 256;
        public float m_ClipPlaneOffset = 0.07f;
 
        public bool m_TurnOffWaterOcclusion = true;
 
        public LayerMask m_ReflectLayers = -1;
        public LayerMask m_RefractLayers = -1;
 
        private Dictionary<Camera, Camera> m_ReflectionCameras = new Dictionary<Camera, Camera>(); // Camera -> Camera table
        private Dictionary<Camera, Camera> m_RefractionCameras = new Dictionary<Camera, Camera>(); // Camera -> Camera table
 
        private RenderTexture m_ReflectionTexture = null;
        private RenderTexture m_RefractionTexture = null;
        private WaterMode m_HardwareWaterSupport = WaterMode.Refractive;
        private int m_OldReflectionTextureSize = 0;
        private int m_OldRefractionTextureSize = 0;
 
        private static bool s_InsideWater = false;
 
        // This is called when it's known that the object will be rendered by some
        // camera. We render reflections / refractions and do other updates here.
        // Because the script executes in edit mode, reflections for the scene view
        // camera will just work!
        public void OnWillRenderObject()
        {
            if( !enabled || !GetComponent<Renderer>() || !GetComponent<Renderer>().sharedMaterial || !GetComponent<Renderer>().enabled )
                return;
 
            Camera cam = Camera.current;
            if( !cam )
                return;
 
            // Safeguard from recursive water reflections.      
            if( s_InsideWater )
                return;
            s_InsideWater = true;
 
            // Actual water rendering mode depends on both the current setting AND
            // the hardware support. There's no point in rendering refraction textures
            // if they won't be visible in the end.
            m_HardwareWaterSupport = FindHardwareWaterSupport();
            WaterMode mode = GetWaterMode();
 
            Camera reflectionCamera, refractionCamera;
            CreateWaterObjects( cam, out reflectionCamera, out refractionCamera );
 
            // find out the reflection plane: position and normal in world space
            Vector3 pos = transform.position;
            Vector3 normal = transform.up;
 
            // Optionally disable pixel lights for reflection/refraction
            int oldPixelLightCount = QualitySettings.pixelLightCount;
            if( m_DisablePixelLights )
                QualitySettings.pixelLightCount = 0;
 
            UpdateCameraModes( cam, reflectionCamera );
            UpdateCameraModes( cam, refractionCamera );
 
            // Render reflection if needed
            if( mode >= WaterMode.Reflective )
            {
                // Reflect camera around reflection plane
                float d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset;
                Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d);
 
                Matrix4x4 reflection = Matrix4x4.zero;
                CalculateReflectionMatrix (ref reflection, reflectionPlane);
                Vector3 oldpos = cam.transform.position;
                Vector3 newpos = reflection.MultiplyPoint( oldpos );
                reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
 
                // Setup oblique projection matrix so that near plane is our reflection
                // plane. This way we clip everything below/above it for free.
                Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f );
                reflectionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
 
                reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layer
                reflectionCamera.targetTexture = m_ReflectionTexture;
                GL.SetRevertBackfacing (true);
                reflectionCamera.transform.position = newpos;
                Vector3 euler = cam.transform.eulerAngles;
                reflectionCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);
                reflectionCamera.Render();
                reflectionCamera.transform.position = oldpos;
                GL.SetRevertBackfacing (false);
                GetComponent<Renderer>().sharedMaterial.SetTexture( "_ReflectionTex", m_ReflectionTexture );
            }
 
            // Render refraction
            if( mode >= WaterMode.Refractive )
            {
                refractionCamera.worldToCameraMatrix = cam.worldToCameraMatrix;
 
                // Setup oblique projection matrix so that near plane is our reflection
                // plane. This way we clip everything below/above it for free.
                Vector4 clipPlane = CameraSpacePlane( refractionCamera, pos, normal, -1.0f );
                refractionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
 
                refractionCamera.cullingMask = ~(1<<4) & m_RefractLayers.value; // never render water layer
                refractionCamera.targetTexture = m_RefractionTexture;
                refractionCamera.transform.position = cam.transform.position;
                refractionCamera.transform.rotation = cam.transform.rotation;
                refractionCamera.Render();
                GetComponent<Renderer>().sharedMaterial.SetTexture( "_RefractionTex", m_RefractionTexture );
            }
 
            // Restore pixel light count
            if( m_DisablePixelLights )
                QualitySettings.pixelLightCount = oldPixelLightCount;
 
            // Setup shader keywords based on water mode
            switch( mode )
            {
            case WaterMode.Simple:
                Shader.EnableKeyword( "WATER_SIMPLE" );
                Shader.DisableKeyword( "WATER_REFLECTIVE" );
                Shader.DisableKeyword( "WATER_REFRACTIVE" );
                break;
            case WaterMode.Reflective:
                Shader.DisableKeyword( "WATER_SIMPLE" );
                Shader.EnableKeyword( "WATER_REFLECTIVE" );
                Shader.DisableKeyword( "WATER_REFRACTIVE" );
                break;
            case WaterMode.Refractive:
                Shader.DisableKeyword( "WATER_SIMPLE" );
                Shader.DisableKeyword( "WATER_REFLECTIVE" );
                Shader.EnableKeyword( "WATER_REFRACTIVE" );
                break;
            }
 
            s_InsideWater = false;
        }
 
 
        // Cleanup all the objects we possibly have created
        void OnDisable()
        {
            if( m_ReflectionTexture ) {
                DestroyImmediate( m_ReflectionTexture );
                m_ReflectionTexture = null;
            }
            if( m_RefractionTexture ) {
                DestroyImmediate( m_RefractionTexture );
                m_RefractionTexture = null;
            }
            foreach (KeyValuePair<Camera, Camera> kvp in m_ReflectionCameras)
                DestroyImmediate( (kvp.Value).gameObject );
            m_ReflectionCameras.Clear();
            foreach (KeyValuePair<Camera, Camera> kvp in m_RefractionCameras)
                DestroyImmediate( (kvp.Value).gameObject );
            m_RefractionCameras.Clear();
        }
 
 
        // This just sets up some matrices in the material; for really
        // old cards to make water texture scroll.
        void Update()
        {
            if( !GetComponent<Renderer>() )
                return;
            Material mat = GetComponent<Renderer>().sharedMaterial;
            if( !mat )
                return;
 
            Vector4 waveSpeed = mat.GetVector( "WaveSpeed" );
            float waveScale = mat.GetFloat( "_WaveScale" );
            Vector4 waveScale4 = new Vector4(waveScale, waveScale, waveScale * 0.4f, waveScale * 0.45f);
 
            // Time since level load, and do intermediate calculations with doubles
            double t = Time.timeSinceLevelLoad / 20.0;
            Vector4 offsetClamped = new Vector4(
                (float)System.Math.IEEERemainder(waveSpeed.x * waveScale4.x * t, 1.0),
                (float)System.Math.IEEERemainder(waveSpeed.y * waveScale4.y * t, 1.0),
                (float)System.Math.IEEERemainder(waveSpeed.z * waveScale4.z * t, 1.0),
                (float)System.Math.IEEERemainder(waveSpeed.w * waveScale4.w * t, 1.0)
            );
 
            mat.SetVector( "_WaveOffset", offsetClamped );
            mat.SetVector( "_WaveScale4", waveScale4 );
 
            Vector3 waterSize = GetComponent<Renderer>().bounds.size;      
            Vector3 scale = new Vector3( waterSize.x*waveScale4.x, waterSize.z*waveScale4.y, 1 );
            Matrix4x4 scrollMatrix = Matrix4x4.TRS( new Vector3(offsetClamped.x,offsetClamped.y,0), Quaternion.identity, scale );
            mat.SetMatrix( "_WaveMatrix", scrollMatrix );
 
            scale = new Vector3( waterSize.x*waveScale4.z, waterSize.z*waveScale4.w, 1 );
            scrollMatrix = Matrix4x4.TRS( new Vector3(offsetClamped.z,offsetClamped.w,0), Quaternion.identity, scale );
            mat.SetMatrix( "_WaveMatrix2", scrollMatrix );
        }
 
        private void UpdateCameraModes( Camera src, Camera dest )
        {
            if( dest == null )
                return;
            // set water camera to clear the same way as current camera
            dest.clearFlags = src.clearFlags;
            dest.backgroundColor = src.backgroundColor;      
            if( src.clearFlags == CameraClearFlags.Skybox )
            {
                Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
                Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
                if( !sky || !sky.material )
                {
                    mysky.enabled = false;
                }
                else
                {
                    mysky.enabled = true;
                    mysky.material = sky.material;
                }
            }
            // update other values to match current camera.
            // even if we are supplying custom camera&projection matrices,
            // some of values are used elsewhere (e.g. skybox uses far plane)
            dest.farClipPlane = src.farClipPlane;
            dest.nearClipPlane = src.nearClipPlane;
            dest.orthographic = src.orthographic;
            dest.fieldOfView = src.fieldOfView;
            dest.aspect = src.aspect;
            dest.orthographicSize = src.orthographicSize;
        }
 
        // On-demand create any objects we need for water
        private void CreateWaterObjects( Camera currentCamera, out Camera reflectionCamera, out Camera refractionCamera )
        {
            WaterMode mode = GetWaterMode();
 
            reflectionCamera = null;
            refractionCamera = null;
 
            if( mode >= WaterMode.Reflective )
            {
                // Reflection render texture
                if( !m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize )
                {
                    if( m_ReflectionTexture )
                        DestroyImmediate( m_ReflectionTexture );
                    m_ReflectionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );
                    m_ReflectionTexture.name = "__WaterReflection" + GetInstanceID();
                    m_ReflectionTexture.isPowerOfTwo = true;
                    m_ReflectionTexture.hideFlags = HideFlags.DontSave;
                    m_OldReflectionTextureSize = m_TextureSize;
 
                }
 
                // Camera for reflection
                m_ReflectionCameras.TryGetValue(currentCamera, out reflectionCamera);
                if (!reflectionCamera) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
                {
                    GameObject go = new GameObject( "Water Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );
                    reflectionCamera = go.GetComponent<Camera>();
                    reflectionCamera.enabled = false;
                    reflectionCamera.transform.position = transform.position;
                    reflectionCamera.transform.rotation = transform.rotation;
                    reflectionCamera.gameObject.AddComponent<FlareLayer>();
                    go.hideFlags = HideFlags.HideAndDontSave;
                    m_ReflectionCameras[currentCamera] = reflectionCamera;
 
                    if (m_TurnOffWaterOcclusion)
                        reflectionCamera.useOcclusionCulling = false;
                }
            }
 
            if( mode >= WaterMode.Refractive )
            {
                // Refraction render texture
                if( !m_RefractionTexture || m_OldRefractionTextureSize != m_TextureSize )
                {
                    if( m_RefractionTexture )
                        DestroyImmediate( m_RefractionTexture );
                    m_RefractionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );
                    m_RefractionTexture.name = "__WaterRefraction" + GetInstanceID();
                    m_RefractionTexture.isPowerOfTwo = true;
                    m_RefractionTexture.hideFlags = HideFlags.DontSave;
                    m_OldRefractionTextureSize = m_TextureSize;
                }
 
                // Camera for refraction
                m_RefractionCameras.TryGetValue(currentCamera, out refractionCamera);
                if (!refractionCamera) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
                {
                    GameObject go = new GameObject( "Water Refr Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );
                    refractionCamera = go.GetComponent<Camera>();
                    refractionCamera.enabled = false;
                    refractionCamera.transform.position = transform.position;
                    refractionCamera.transform.rotation = transform.rotation;
                    refractionCamera.gameObject.AddComponent<FlareLayer>();
                    go.hideFlags = HideFlags.HideAndDontSave;
                    m_RefractionCameras[currentCamera] = refractionCamera;
 
                    if (m_TurnOffWaterOcclusion)
                        refractionCamera.useOcclusionCulling = false;
                }
            }
        }
 
        private WaterMode GetWaterMode()
        {
            if( m_HardwareWaterSupport < m_WaterMode )
                return m_HardwareWaterSupport;
            else
                return m_WaterMode;
        }
 
        private WaterMode FindHardwareWaterSupport()
        {
            if( !SystemInfo.supportsRenderTextures || !GetComponent<Renderer>() )
                return WaterMode.Simple;
 
            Material mat = GetComponent<Renderer>().sharedMaterial;
            if( !mat )
                return WaterMode.Simple;
 
            string mode = mat.GetTag("WATERMODE", false);
            if( mode == "Refractive" )
                return WaterMode.Refractive;
            if( mode == "Reflective" )
                return WaterMode.Reflective;
 
            return WaterMode.Simple;
        }
 
        // Extended sign: returns -1, 0 or 1 based on sign of a
        private static float sgn(float a)
        {
            if (a > 0.0f) return 1.0f;
            if (a < 0.0f) return -1.0f;
            return 0.0f;
        }
 
        // Given position/normal of the plane, calculates plane in camera space.
        private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign)
        {
            Vector3 offsetPos = pos + normal * m_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) );
        }
 
        // Calculates reflection matrix around the given plane
        private static void CalculateReflectionMatrix (ref Matrix4x4 reflectionMat, Vector4 plane)
        {
            reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]);
            reflectionMat.m01 = (   - 2F*plane[0]*plane[1]);
            reflectionMat.m02 = (   - 2F*plane[0]*plane[2]);
            reflectionMat.m03 = (   - 2F*plane[3]*plane[0]);
 
            reflectionMat.m10 = (   - 2F*plane[1]*plane[0]);
            reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]);
            reflectionMat.m12 = (   - 2F*plane[1]*plane[2]);
            reflectionMat.m13 = (   - 2F*plane[3]*plane[1]);
 
            reflectionMat.m20 = (   - 2F*plane[2]*plane[0]);
            reflectionMat.m21 = (   - 2F*plane[2]*plane[1]);
            reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]);
            reflectionMat.m23 = (   - 2F*plane[3]*plane[2]);
 
            reflectionMat.m30 = 0F;
            reflectionMat.m31 = 0F;
            reflectionMat.m32 = 0F;
            reflectionMat.m33 = 1F;
        }
 
    }
}