using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Rendering; using UnityEngine.Rendering.LWRP; using WaterSystem.Data; namespace WaterSystem { [ExecuteAlways] public class Water : MonoBehaviour { // Singleton private static Water _Instance = null; public static Water Instance { get { if (_Instance == null) _Instance = (Water)FindObjectOfType(typeof(Water)); return _Instance; } } // Script references private MainCameraAlign camAlign; private PlanarReflections planarReflections; private bool useComputeBuffer; public bool computeOverride; RenderTexture _depthTex; public Texture _bakedDepthTex; private Camera _depthCam; [SerializeField] private Texture2D _rampTexture; [SerializeField] public Wave[] _waves; [SerializeField] private ComputeBuffer _waveBuffer; private float _maxWaveHeight; [SerializeField] public WaterSettingsData settingsData; [SerializeField] public WaterSurfaceData surfaceData; [SerializeField] private WaterResources resources; private float waterTime = 0; private void OnValidate() { Init(); } void OnEnable() { if (!computeOverride) useComputeBuffer = SystemInfo.supportsComputeShaders && Application.platform != RuntimePlatform.WebGLPlayer && Application.platform != RuntimePlatform.Android; else useComputeBuffer = false; Init(); RenderPipelineManager.beginCameraRendering += BeginCameraRendering; if(resources == null) { resources = Resources.Load("WaterResources") as WaterResources; } } private void OnDisable() { Cleanup(); } void Cleanup() { if(Application.isPlaying) GerstnerWavesJobs.Cleanup(); RenderPipelineManager.beginCameraRendering -= BeginCameraRendering; if (_depthCam) { _depthCam.targetTexture = null; SafeDestroy(_depthCam.gameObject); } if (_depthTex) { SafeDestroy(_depthTex); } if(_waveBuffer != null) _waveBuffer.Dispose(); } private void BeginCameraRendering(ScriptableRenderContext src, Camera cam) { Vector3 fwd = cam.transform.forward; fwd.y = 0; float roll = cam.transform.localEulerAngles.z; Shader.SetGlobalFloat("_CameraRoll", roll); Shader.SetGlobalMatrix("_InvViewProjection", (GL.GetGPUProjectionMatrix(cam.projectionMatrix, false) * cam.worldToCameraMatrix).inverse); foreach (var mesh in resources.defaultWaterMeshes) { Graphics.DrawMesh(mesh, transform.localToWorldMatrix, resources.defaultSeaMaterial, gameObject.layer, cam); } } private void SafeDestroy(Object o) { if(Application.isPlaying) Destroy(o); else DestroyImmediate(o); } public void Init() { SetWaves(); GenerateColorRamp(); if (_bakedDepthTex) { Shader.SetGlobalTexture("_WaterDepthMap", _bakedDepthTex); } if (!gameObject.TryGetComponent(out camAlign)) { camAlign = gameObject.AddComponent(); camAlign.hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector; } if (!gameObject.TryGetComponent(out planarReflections)) { planarReflections = gameObject.AddComponent(); planarReflections.hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector; } planarReflections.m_settings = settingsData.planarSettings; planarReflections.enabled = settingsData.refType == ReflectionType.PlanarReflection; if(resources == null) { resources = Resources.Load("WaterResources") as WaterResources; } //CaptureDepthMap(); } void Update() { waterTime = Time.time; Shader.SetGlobalFloat("_GlobalTime", waterTime); GerstnerWavesJobs.UpdateHeights(); } public void FragWaveNormals(bool toggle) { Material mat = GetComponent().sharedMaterial; if (toggle) mat.EnableKeyword("GERSTNER_WAVES"); else mat.DisableKeyword("GERSTNER_WAVES"); } void SetWaves() { SetupWaves(surfaceData._customWaves); // set default resources Shader.SetGlobalTexture("_FoamMap", resources.defaultFoamMap); Shader.SetGlobalTexture("_SurfaceMap", resources.defaultSurfaceMap); _maxWaveHeight = 0f; foreach (Wave w in _waves) { _maxWaveHeight += w.amplitude; } _maxWaveHeight /= (float)_waves.Length; Shader.SetGlobalFloat("_MaxWaveHeight", _maxWaveHeight); Shader.SetGlobalFloat("_MaxDepth", surfaceData._waterMaxVisibility); switch(settingsData.refType) { case ReflectionType.Cubemap: Shader.EnableKeyword("_REFLECTION_CUBEMAP"); Shader.DisableKeyword("_REFLECTION_PROBES"); Shader.DisableKeyword("_REFLECTION_PLANARREFLECTION"); Shader.SetGlobalTexture("_CubemapTexture", settingsData.cubemapRefType); break; case ReflectionType.ReflectionProbe: Shader.DisableKeyword("_REFLECTION_CUBEMAP"); Shader.EnableKeyword("_REFLECTION_PROBES"); Shader.DisableKeyword("_REFLECTION_PLANARREFLECTION"); break; case ReflectionType.PlanarReflection: Shader.DisableKeyword("_REFLECTION_CUBEMAP"); Shader.DisableKeyword("_REFLECTION_PROBES"); Shader.EnableKeyword("_REFLECTION_PLANARREFLECTION"); break; } Shader.SetGlobalInt("_WaveCount", _waves.Length); //GPU side if (useComputeBuffer) { Shader.EnableKeyword("USE_STRUCTURED_BUFFER"); if (_waveBuffer == null) _waveBuffer = new ComputeBuffer(10, (sizeof(float) * 6)); _waveBuffer.SetData(_waves); Shader.SetGlobalBuffer("_WaveDataBuffer", _waveBuffer); } else { Shader.DisableKeyword("USE_STRUCTURED_BUFFER"); Shader.SetGlobalVectorArray("waveData", GetWaveData()); } //CPU side if(GerstnerWavesJobs.init == false && Application.isPlaying) GerstnerWavesJobs.Init(); } public Vector4[] GetWaveData() { Vector4[] waveData = new Vector4[20]; for (int i = 0; i < _waves.Length; i++) { waveData[i] = new Vector4(_waves[i].amplitude, _waves[i].direction, _waves[i].wavelength, _waves[i].onmiDir); waveData[i+10] = new Vector4(_waves[i].origin.x, _waves[i].origin.y, 0, 0); } return waveData; } public void SetupWaves(bool custom) { if(!custom) { //create basic waves based off basic wave settings Random.State backupSeed = Random.state; Random.InitState(surfaceData.randomSeed); BasicWaves basicWaves = surfaceData._basicWaveSettings; float a = basicWaves.amplitude; float d = basicWaves.direction; float l = basicWaves.wavelength; int numWave = basicWaves.numWaves; _waves = new Wave[numWave]; float r = 1f / numWave; for (int i = 0; i < numWave; i++) { float p = Mathf.Lerp(0.5f, 1.5f, (float)i * r); float amp = a * p * Random.Range(0.8f, 1.2f); float dir = d + Random.Range(-45f, 45f); float len = l * p * Random.Range(0.6f, 1.4f); _waves[i] = new Wave(amp, dir, len, Vector2.zero, false); Random.InitState(surfaceData.randomSeed + i + 1); } Random.state = backupSeed; } else { _waves = surfaceData._waves.ToArray(); } } public void GenerateColorRamp() { if(_rampTexture == null) _rampTexture = new Texture2D(128, 4, TextureFormat.ARGB32, false, false); _rampTexture.wrapMode = TextureWrapMode.Clamp; Texture2D _defaultFoamRamp = resources.defaultFoamRamp; Color[] cols = new Color[512]; for (int i = 0; i < 128; i++) { cols[i] = surfaceData._absorptionRamp.Evaluate((float)i / 128f); } for (int i = 0; i < 128; i++) { cols[i + 128] = surfaceData._scatterRamp.Evaluate((float)i / 128f); } for (int i = 0; i < 128; i++) { switch(surfaceData._foamSettings.foamType) { case 0: { // default cols[i + 256] = _defaultFoamRamp.GetPixelBilinear((float)i / 128f , 0.5f); }break; case 1: { // simple cols[i + 256] = _defaultFoamRamp.GetPixelBilinear(surfaceData._foamSettings.basicFoam.Evaluate((float)i / 128f) , 0.5f); }break; case 2: { // custom cols[i + 256] = Color.black; }break; } } _rampTexture.SetPixels(cols); _rampTexture.Apply(); Shader.SetGlobalTexture("_AbsorptionScatteringRamp", _rampTexture); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////Shoreline Depth Texture///////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////// [ContextMenu("Capture Depth")] public void CaptureDepthMap() { //Generate the camera if(_depthCam == null) { GameObject go = new GameObject("depthCamera");//create the cameraObject go.hideFlags = HideFlags.HideAndDontSave; _depthCam = go.AddComponent(); } if (_depthCam.GetComponent() == null) { UnityEngine.Rendering.Universal.UniversalAdditionalCameraData additionalCamData = _depthCam.gameObject.AddComponent(); additionalCamData.renderShadows = false; additionalCamData.requiresColorOption = UnityEngine.Rendering.Universal.CameraOverrideOption.Off; additionalCamData.requiresDepthOption = UnityEngine.Rendering.Universal.CameraOverrideOption.Off; } _depthCam.transform.position = Vector3.up * 4f;//center the camera on this water plane _depthCam.transform.up = Vector3.forward;//face teh camera down _depthCam.enabled = true; _depthCam.orthographic = true; _depthCam.orthographicSize = 250;//hardcoded = 1k area - TODO //_depthCam.depthTextureMode = DepthTextureMode.Depth; _depthCam.nearClipPlane =0.1f; _depthCam.farClipPlane = surfaceData._waterMaxVisibility; _depthCam.allowHDR = false; _depthCam.allowMSAA = false; _depthCam.cullingMask = (1 << 10); //Generate RT if (!_depthTex) _depthTex = new RenderTexture(1024, 1024, 24, RenderTextureFormat.Depth, RenderTextureReadWrite.Linear); if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3) { _depthTex.filterMode = FilterMode.Point; _depthTex.wrapMode = TextureWrapMode.Clamp; } _depthTex.name = "WaterDepthMap"; //do depth capture _depthCam.targetTexture = _depthTex; _depthCam.Render(); //Shader.SetGlobalTexture("_WaterDepthMap", _depthTex); // set depthbufferParams for depth cam(since it doesnt exist and only temporary) float n = _depthCam.nearClipPlane; float f = _depthCam.farClipPlane; Vector4 zParams = new Vector4(n, f, f / (f-n), f * n / (n-f)); //Vector4 zParams = new Vector4(1-f/n, f/n, (1-f/n)/f, (f/n)/f);//2015 Shader.SetGlobalVector("_depthCamZParams", zParams); /* #if UNITY_EDITOR Texture2D tex2D = new Texture2D(1024, 1024, TextureFormat.Alpha8, false); Graphics.CopyTexture(_depthTex, tex2D); byte[] image = tex2D.EncodeToPNG(); System.IO.File.WriteAllBytes(Application.dataPath + "/WaterDepth.png", image); #endif*/ _depthCam.enabled = false; _depthCam.targetTexture = null; } private void OnDrawGizmos() { if(!Application.isPlaying) { #if UNITY_EDITOR waterTime = (float)UnityEditor.EditorApplication.timeSinceStartup; #endif Shader.SetGlobalFloat("_GlobalTime", waterTime); } } [System.Serializable] public enum DebugMode { none, stationary, screen }; } }