您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
386 行
14 KiB
386 行
14 KiB
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<MainCameraAlign>();
|
|
camAlign.hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector;
|
|
}
|
|
|
|
if (!gameObject.TryGetComponent(out planarReflections))
|
|
{
|
|
planarReflections = gameObject.AddComponent<PlanarReflections>();
|
|
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<Renderer>().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<Camera>();
|
|
}
|
|
|
|
if (_depthCam.GetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>() == null)
|
|
{
|
|
UnityEngine.Rendering.Universal.UniversalAdditionalCameraData additionalCamData = _depthCam.gameObject.AddComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>();
|
|
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 };
|
|
}
|
|
}
|