using UnityEngine; namespace AQUAS { public class AQUAS_UnderWaterEffect : MonoBehaviour { public enum QUALITY { low = 0, medium = 1, high = 2 } [Header("Underwater Settings")] public LayerMask cullingMask = -1; public QUALITY backFaceQuality = QUALITY.medium; [Range(0.1f, 1f)] public float fogFade = 1f; [Range(0f, 0.1f)] public float distortionStrength = 0.025f; [Range(0f, 0.002f)] public float blurSize = 0.001f; [Range(0f, 10f)] public int blurSmoothness = 5; public bool enableWetLensEffect = true; [Range(0.5f, 3f)] public float wetLensDuration = 1f; [Space(10f)] [Header("Underwater Audio Settings")] public AudioClip underwaterAmbientSound; [Range(0f, 1f)] public float underwaterAmbientVolume = 0.5f; public AudioClip diveSplashSound; [Range(0f, 1f)] public float diveSplashVolume = 1f; public AudioClip surfaceSplashSound; [Range(0f, 1f)] public float surfaceSplashVolume = 0.2f; [Space(10f)] [Header("Bubble Spawn Settings")] [Range(5f, 50f)] public int maximumBubbleCount = 10; public bool spawnBubbles; private float fogDensity = 1f; private float deepFogDensity = 1.5f; private float maxFogDepth = 10f; private float adjustedFogDensity; private Color fogColor; private Color deepFogColor; private Color adjustedFogColor; private GameObject container; private GameObject boundaryMask; private GameObject volumeMask; private GameObject frontFaceMask; private GameObject background; private GameObject waterPlane; private GameObject[] waterObjects; private GameObject[] staticBoundaries; [HideInInspector] public GameObject dynamicBoundary; private Material fogMat; private Material blurMat; private Material dropletMaskMat; [HideInInspector] public RenderTexture mask; private RenderTexture buffer1; private RenderTexture buffer2; private RenderTexture fogBuffer; private RenderTexture dropletBuffer; private RenderTexture dropletMask; [HideInInspector] public bool underwater; private GameObject audioObject; private AudioSource underwaterAmbient; private AudioSource diveSplash; private AudioSource surfaceSplash; private bool diveSplashPlayed = true; private bool surfaceSplashPlayed = true; private GameObject bubbleContainer; private GameObject bubble; private AQUAS_BubbleBehaviour bubbleBehaviour; private float t2; private float bubbleSpawnTimer; private int maxBubbleCount; private int bubbleCount; [HideInInspector] public float waterLevel; private void Start() { container = GameObject.Find("AQUAS Container"); if (container == null) { base.enabled = false; return; } waterObjects = new GameObject[container.transform.childCount]; staticBoundaries = new GameObject[container.transform.childCount]; for (int i = 0; i < container.transform.childCount; i++) { waterObjects[i] = container.transform.GetChild(i).gameObject; if (waterObjects[i].transform.Find("Static Boundary") != null) { staticBoundaries[i] = waterObjects[i].transform.Find("Static Boundary").gameObject; } else { staticBoundaries[i] = dynamicBoundary; } } boundaryMask = new GameObject("Boundary Mask"); volumeMask = new GameObject("Volume Mask"); frontFaceMask = new GameObject("Front Face Mask"); background = new GameObject("Background"); boundaryMask.hideFlags = HideFlags.HideAndDontSave; volumeMask.hideFlags = HideFlags.HideAndDontSave; frontFaceMask.hideFlags = HideFlags.HideAndDontSave; background.hideFlags = HideFlags.HideAndDontSave; boundaryMask.transform.SetParent(base.transform); boundaryMask.AddComponent().CopyFrom(GetComponent()); boundaryMask.GetComponent().cullingMask &= -2 << LayerMask.NameToLayer("Everything"); boundaryMask.GetComponent().cullingMask ^= 1 << LayerMask.NameToLayer("Water"); boundaryMask.GetComponent().depth = GetComponent().depth - 4f; boundaryMask.AddComponent(); boundaryMask.GetComponent().nextCam = volumeMask; boundaryMask.AddComponent(); volumeMask.transform.SetParent(base.transform); volumeMask.AddComponent().CopyFrom(GetComponent()); volumeMask.GetComponent().cullingMask &= -2 << LayerMask.NameToLayer("Everything"); volumeMask.GetComponent().cullingMask ^= 1 << LayerMask.NameToLayer("Water"); volumeMask.GetComponent().depth = GetComponent().depth - 3f; volumeMask.AddComponent(); volumeMask.GetComponent().nextCam = frontFaceMask; volumeMask.AddComponent(); frontFaceMask.transform.SetParent(base.transform); frontFaceMask.AddComponent().CopyFrom(GetComponent()); frontFaceMask.GetComponent().cullingMask &= -2 << LayerMask.NameToLayer("Everything"); frontFaceMask.GetComponent().cullingMask ^= 1 << LayerMask.NameToLayer("Water"); frontFaceMask.GetComponent().depth = GetComponent().depth - 2f; frontFaceMask.AddComponent(); frontFaceMask.GetComponent().nextCam = base.gameObject; frontFaceMask.AddComponent(); background.transform.SetParent(base.transform); background.AddComponent().CopyFrom(GetComponent()); background.GetComponent().cullingMask = cullingMask; background.GetComponent().cullingMask &= ~(1 << LayerMask.NameToLayer("Water")); background.GetComponent().depth = GetComponent().depth - 1f; background.AddComponent(); background.AddComponent(); switch (backFaceQuality) { case QUALITY.low: background.GetComponent().quality = 3; break; case QUALITY.medium: background.GetComponent().quality = 2; break; case QUALITY.high: background.GetComponent().quality = 0; break; } fogMat = new Material(Shader.Find("Hidden/AQUAS/Underwater/Fog")); blurMat = new Material(Shader.Find("Hidden/AQUAS/Underwater/Blur")); dropletMaskMat = new Material(Shader.Find("Hidden/AQUAS/Underwater/Droplet Mask")); float x = (float)Screen.width / (float)Screen.height; fogMat.SetTexture("_DistortionLens", (Texture2D)Resources.Load("distortion_ellipse", typeof(Texture2D))); fogMat.SetTexture("_DropletNormals", (Texture2D)Resources.Load("wet_lens_normal", typeof(Texture2D))); fogMat.SetTextureScale("_DropletNormals", new Vector2(x, 1f)); fogMat.SetTexture("_DropletCutout", (Texture2D)Resources.Load("wet_lens_cutout", typeof(Texture2D))); fogMat.SetTextureScale("_DropletCutout", new Vector2(x, 1f)); mask = new RenderTexture(Screen.height, Screen.width, 8, RenderTextureFormat.ARGB32); buffer1 = new RenderTexture(Screen.width, Screen.height, 32, RenderTextureFormat.ARGB32); buffer2 = new RenderTexture(Screen.width, Screen.height, 32, RenderTextureFormat.ARGB32); fogBuffer = new RenderTexture(Screen.width, Screen.height, 32, RenderTextureFormat.ARGB32); dropletBuffer = new RenderTexture(Screen.width, Screen.height, 32, RenderTextureFormat.ARGB32); dropletMask = new RenderTexture(Screen.width, Screen.height, 32, RenderTextureFormat.ARGB32); audioObject = new GameObject("Underwater Audio"); audioObject.transform.parent = base.transform; audioObject.hideFlags = HideFlags.HideAndDontSave; if (underwaterAmbientSound == null) { underwaterAmbientSound = (AudioClip)Resources.Load("underwater"); } if (diveSplashSound == null) { diveSplashSound = (AudioClip)Resources.Load("dive-splash"); } if (surfaceSplashSound == null) { surfaceSplashSound = (AudioClip)Resources.Load("surfacing-splash"); } underwaterAmbient = audioObject.AddComponent(); diveSplash = audioObject.AddComponent(); surfaceSplash = audioObject.AddComponent(); underwaterAmbient.clip = underwaterAmbientSound; diveSplash.clip = diveSplashSound; surfaceSplash.clip = surfaceSplashSound; diveSplash.loop = false; surfaceSplash.loop = false; bubble = (GameObject)Resources.Load("Bubble", typeof(GameObject)); bubbleBehaviour = bubble.GetComponent(); if (spawnBubbles) { bubbleContainer = new GameObject("Bubble Container"); bubbleContainer.hideFlags = HideFlags.HideAndDontSave; } } private void OnTriggerEnter(Collider other) { if (other.gameObject.name == "Static Boundary") { if (other.transform.parent == null) { waterPlane = GetComponent().waterplane; } else { waterPlane = other.transform.parent.gameObject; } other.GetComponent().enabled = true; fogColor = waterPlane.GetComponent().mainFogColor; deepFogColor = waterPlane.GetComponent().deepFogColor; fogDensity = waterPlane.GetComponent().mainFogDensity; deepFogDensity = waterPlane.GetComponent().deepFogDensity; maxFogDepth = waterPlane.GetComponent().maxFogDepth; for (int i = 0; i < waterObjects.Length; i++) { waterObjects[i].layer = LayerMask.NameToLayer("Default"); staticBoundaries[i].layer = LayerMask.NameToLayer("Default"); } boundaryMask.GetComponent().boundaryObj = other.gameObject; volumeMask.GetComponent().waterObj = waterPlane; volumeMask.GetComponent().rend = waterPlane.GetComponent(); volumeMask.GetComponent().waterShaders[0] = waterPlane.GetComponent().sharedMaterials[0].shader; volumeMask.GetComponent().waterShaders[1] = waterPlane.GetComponent().sharedMaterials[1].shader; frontFaceMask.GetComponent().waterObj = waterPlane; frontFaceMask.GetComponent().rend = waterPlane.GetComponent(); frontFaceMask.GetComponent().waterShaders[0] = waterPlane.GetComponent().sharedMaterials[0].shader; frontFaceMask.GetComponent().waterShaders[1] = waterPlane.GetComponent().sharedMaterials[1].shader; background.GetComponent().waterObj = waterPlane; background.GetComponent().layerMask = cullingMask; } } private void OnTriggerExit(Collider other) { if (other.gameObject.name == "Static Boundary") { other.GetComponent().enabled = false; waterPlane = null; for (int i = 0; i < waterObjects.Length; i++) { waterObjects[i].layer = LayerMask.NameToLayer("Water"); staticBoundaries[i].layer = LayerMask.NameToLayer("Water"); } boundaryMask.GetComponent().boundaryObj = null; volumeMask.GetComponent().waterObj = null; frontFaceMask.GetComponent().waterObj = null; background.GetComponent().waterObj = null; } } private void OnRenderImage(RenderTexture source, RenderTexture destination) { dropletMaskMat.SetTexture("_DropletMask", dropletMask); dropletMaskMat.SetFloat("_Duration", wetLensDuration); Graphics.Blit(mask, dropletBuffer, dropletMaskMat); Graphics.Blit(dropletBuffer, dropletMask); fogMat.SetTexture("_DropletMask", dropletMask); if (blurSmoothness > 0) { fogMat.SetTexture("_DepthMask", mask); fogMat.SetFloat("_Density", adjustedFogDensity); fogMat.SetColor("_FogColor", adjustedFogColor); fogMat.SetFloat("_Distortion", distortionStrength); fogMat.SetFloat("_Fade", fogFade); if (enableWetLensEffect) { fogMat.SetFloat("_EnableWetLens", 1f); } else { fogMat.SetFloat("_EnableWetLens", 0f); } Graphics.Blit(source, fogBuffer, fogMat); blurMat.SetTexture("_DepthMask", mask); blurMat.SetFloat("_BlurSize", blurSize); if (blurSmoothness == 1) { Graphics.Blit(fogBuffer, destination, blurMat); } else if (blurSmoothness == 2) { Graphics.Blit(fogBuffer, buffer1, blurMat); Graphics.Blit(buffer1, destination, blurMat); buffer1.Release(); } else { if (blurSmoothness <= 2) { return; } for (int i = 0; i < blurSmoothness; i++) { if (i == 0) { Graphics.Blit(fogBuffer, buffer1, blurMat); } if (i == blurSmoothness - 1) { if (i % 2 == 1) { Graphics.Blit(buffer1, destination, blurMat); buffer1.Release(); } else { Graphics.Blit(buffer2, destination, blurMat); buffer2.Release(); } } if (i > 0 && i < blurSmoothness - 1) { if (i % 2 == 1) { Graphics.Blit(buffer1, buffer2, blurMat); buffer1.Release(); } else { Graphics.Blit(buffer2, buffer1, blurMat); buffer2.Release(); } } } } } else { fogMat.SetTexture("_DepthMask", mask); fogMat.SetFloat("_Density", adjustedFogDensity); fogMat.SetColor("_FogColor", adjustedFogColor); fogMat.SetFloat("_Distortion", distortionStrength); fogMat.SetFloat("_Fade", fogFade); Graphics.Blit(source, destination, fogMat); } } private void Update() { if (waterPlane != null) { if (waterPlane.GetComponent() != null && waterPlane.GetComponent().useDynamicMesh) { if (base.transform.position.y < waterLevel) { underwater = true; } else { underwater = false; } } else if (base.transform.position.y < waterPlane.transform.position.y) { underwater = true; } else { underwater = false; } adjustedFogColor = Color.Lerp(fogColor, deepFogColor, (waterPlane.transform.position.y - base.transform.position.y) / maxFogDepth); } else { underwater = false; } underwaterAmbient.volume = underwaterAmbientVolume; diveSplash.volume = diveSplashVolume; surfaceSplash.volume = surfaceSplashVolume; if (underwater) { surfaceSplashPlayed = false; if (!underwaterAmbient.isPlaying) { underwaterAmbient.Play(); } if (!diveSplashPlayed) { diveSplash.Play(); diveSplashPlayed = true; } if (spawnBubbles) { if (bubbleContainer == null) { bubbleContainer = new GameObject("Bubble Container"); bubbleContainer.hideFlags = HideFlags.HideAndDontSave; } t2 += Time.deltaTime; BubbleSpawner(); } } else { diveSplashPlayed = false; if (underwaterAmbient.isPlaying) { underwaterAmbient.Stop(); } if (!surfaceSplashPlayed) { surfaceSplash.Play(); surfaceSplashPlayed = true; } t2 = 0f; bubbleSpawnTimer = 0f; maxBubbleCount = Random.Range(maximumBubbleCount / 2, maximumBubbleCount); bubbleCount = 0; } if (waterPlane != null) { adjustedFogDensity = Mathf.Lerp(fogDensity, deepFogDensity, (waterPlane.transform.position.y - base.transform.position.y) / maxFogDepth) * GetComponent().farClipPlane / 10f; adjustedFogColor = Color.Lerp(fogColor, deepFogColor, (waterPlane.transform.position.y - base.transform.position.y) / maxFogDepth); } } private void BubbleSpawner() { if (t2 > bubbleSpawnTimer && maxBubbleCount > bubbleCount) { float num = Random.Range(0f, 0.06f); bubbleBehaviour.mainCamera = base.gameObject; if (waterPlane.GetComponent() != null && waterPlane.GetComponent().useDynamicMesh) { bubbleBehaviour.waterLevel = waterLevel; } else { bubbleBehaviour.waterLevel = waterPlane.transform.position.y; } bubbleBehaviour.averageUpdrift = 1f + Random.Range(-0.3f, 0.3f); bubble.transform.localScale += new Vector3(num, num, num); Object.Instantiate(bubble, new Vector3(base.transform.position.x + Random.Range(-3f, 3f), base.transform.position.y - 0.4f, base.transform.position.z + Random.Range(-3f, 3f)), Quaternion.identity).transform.SetParent(bubbleContainer.transform); bubbleSpawnTimer += Random.Range(0.02f, 0.1f); bubbleCount++; bubble.transform.localScale = new Vector3(0.06f, 0.06f, 0.06f); } else if (t2 > bubbleSpawnTimer && maxBubbleCount <= bubbleCount) { float num2 = Random.Range(0f, 0.06f); bubbleBehaviour.mainCamera = base.gameObject; if (waterPlane.GetComponent() != null && waterPlane.GetComponent().useDynamicMesh) { bubbleBehaviour.waterLevel = waterLevel; } else { bubbleBehaviour.waterLevel = waterPlane.transform.position.y; } bubbleBehaviour.averageUpdrift = 1f + Random.Range(-0.3f, 0.3f); bubble.transform.localScale += new Vector3(num2, num2, num2); Object.Instantiate(bubble, new Vector3(base.transform.position.x + Random.Range(-0.5f, 0.5f), base.transform.position.y - 0.4f, base.transform.position.z + Random.Range(-0.5f, 0.5f)), Quaternion.identity).transform.SetParent(bubbleContainer.transform); bubbleSpawnTimer += Random.Range(0.02f, 0.2f); bubble.transform.localScale = new Vector3(0.06f, 0.06f, 0.06f); } } } }