798 lines
36 KiB
C#
798 lines
36 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Rendering;
|
||
using UnityEngine.XR;
|
||
|
||
namespace LuxWater {
|
||
|
||
[RequireComponent(typeof(Camera))]
|
||
public class LuxWater_UnderWaterRendering : MonoBehaviour {
|
||
|
||
public static LuxWater_UnderWaterRendering instance;
|
||
|
||
[Space(6)]
|
||
[LuxWater_HelpBtn("h.d0q6uguuxpy")]
|
||
public Transform Sun;
|
||
[Space(4)]
|
||
public bool FindSunOnEnable = false;
|
||
public string SunGoName = "";
|
||
public string SunTagName = "";
|
||
private Light SunLight;
|
||
|
||
[Space(2)]
|
||
[Header("Deep Water Lighting")]
|
||
[Space(4)]
|
||
public bool EnableDeepwaterLighting = false;
|
||
public float DefaultWaterSurfacePosition = 0.0f;
|
||
public float DirectionalLightingFadeRange = 64.0f;
|
||
public float FogLightingFadeRange = 64.0f;
|
||
|
||
[Space(2)]
|
||
[Header("Advanced Deferred Fog")]
|
||
[Space(4)]
|
||
public bool EnableAdvancedDeferredFog = false;
|
||
public float FogDepthShift = 1.0f;
|
||
public float FogEdgeBlending = 0.125f;
|
||
|
||
|
||
[Space(8)]
|
||
[NonSerialized]
|
||
public int activeWaterVolume = -1;
|
||
|
||
[NonSerialized]
|
||
public List<Camera> activeWaterVolumeCameras = new List<Camera>();
|
||
|
||
[NonSerialized]
|
||
public float activeGridSize = 0.0f;
|
||
|
||
[NonSerialized]
|
||
public float WaterSurfacePos = 0.0f;
|
||
|
||
[Space(8)]
|
||
[NonSerialized]
|
||
public List<int> RegisteredWaterVolumesIDs = new List<int>();
|
||
[NonSerialized]
|
||
public List<LuxWater_WaterVolume> RegisteredWaterVolumes = new List<LuxWater_WaterVolume>();
|
||
private List<Mesh> WaterMeshes = new List<Mesh>();
|
||
private List<Transform> WaterTransforms = new List<Transform>();
|
||
private List<Material> WaterMaterials = new List<Material>();
|
||
private List<bool> WaterIsOnScreen = new List<bool>();
|
||
private List<bool> WaterUsesSlidingVolume = new List<bool>();
|
||
|
||
private RenderTexture UnderWaterMask;
|
||
|
||
[Space(2)]
|
||
[Header("Managed transparent Materials")]
|
||
[Space(4)]
|
||
public List<Material> m_aboveWatersurface = new List<Material>();
|
||
public List<Material> m_belowWatersurface = new List<Material>();
|
||
|
||
[Space(2)]
|
||
[Header("Optimize")]
|
||
[Space(4)]
|
||
public ShaderVariantCollection PrewarmedShaders;
|
||
public int ListCapacity = 10;
|
||
|
||
[Space(2)]
|
||
[Header("Debug")]
|
||
[Space(4)]
|
||
public bool enableDebug = false;
|
||
[Space(8)]
|
||
|
||
private Material mat;
|
||
private Material blurMaterial;
|
||
private Material blitMaterial;
|
||
|
||
//private RenderTexture UnderwaterTex;
|
||
|
||
private Camera cam;
|
||
private bool UnderwaterIsSetUp = false;
|
||
|
||
private static CommandBuffer cb_Mask;
|
||
private CameraEvent cameraEvent = CameraEvent.AfterSkybox; //.BeforeSkybox; // This works for both deferred an forward
|
||
|
||
private Transform camTransform;
|
||
private Matrix4x4 frustumCornersArray = Matrix4x4.identity;
|
||
private Matrix4x4 frustumCornersArray2nd = Matrix4x4.identity;
|
||
|
||
private SphericalHarmonicsL2 ambientProbe;
|
||
private Vector3[] directions = new Vector3[] { new Vector3(0.0f, 1.0f, 0.0f) };
|
||
private Color[] AmbientLightingSamples = new Color[1];
|
||
|
||
|
||
// Metal Support
|
||
// We have to manually grab the depth texture
|
||
//private CommandBuffer cb_DepthGrab;
|
||
//private CommandBuffer cb_AfterFinalPass;
|
||
|
||
private bool DoUnderWaterRendering = false;
|
||
private Matrix4x4 camProj;
|
||
private Vector3[] frustumCorners = new Vector3[4];
|
||
|
||
private float Projection;
|
||
private bool islinear = false;
|
||
|
||
private Matrix4x4 WatervolumeMatrix;
|
||
|
||
|
||
private static readonly int UnderWaterMaskPID = Shader.PropertyToID("_UnderWaterMask");
|
||
private static readonly int Lux_FrustumCornersWSPID = Shader.PropertyToID("_Lux_FrustumCornersWS");
|
||
private static readonly int Lux_FrustumCornersWS2ndPID = Shader.PropertyToID("_Lux_FrustumCornersWS2ndEye");
|
||
private static readonly int Lux_CameraWSPID = Shader.PropertyToID("_Lux_CameraWS");
|
||
|
||
private static readonly int GerstnerEnabledPID = Shader.PropertyToID("_GerstnerEnabled");
|
||
|
||
private static readonly int LuxWaterMask_GerstnerVertexIntensityPID = Shader.PropertyToID("_LuxWaterMask_GerstnerVertexIntensity");
|
||
private static readonly int GerstnerVertexIntensityPID = Shader.PropertyToID("_GerstnerVertexIntensity");
|
||
private static readonly int LuxWaterMask_GAmplitudePID = Shader.PropertyToID("_LuxWaterMask_GAmplitude");
|
||
private static readonly int GAmplitudePID = Shader.PropertyToID("_GAmplitude");
|
||
private static readonly int LuxWaterMask_GFinalFrequencyPID = Shader.PropertyToID("_LuxWaterMask_GFinalFrequency");
|
||
private static readonly int GFinalFrequencyPID = Shader.PropertyToID("_GFinalFrequency");
|
||
private static readonly int LuxWaterMask_GSteepnessPID = Shader.PropertyToID("_LuxWaterMask_GSteepness");
|
||
private static readonly int GSteepnessPID = Shader.PropertyToID("_GSteepness");
|
||
private static readonly int LuxWaterMask_GFinalSpeedPID = Shader.PropertyToID("_LuxWaterMask_GFinalSpeed");
|
||
private static readonly int GFinalSpeedPID = Shader.PropertyToID("_GFinalSpeed");
|
||
private static readonly int LuxWaterMask_GDirectionABPID = Shader.PropertyToID("_LuxWaterMask_GDirectionAB");
|
||
private static readonly int GDirectionABPID = Shader.PropertyToID("_GDirectionAB");
|
||
private static readonly int LuxWaterMask_GDirectionCDPID = Shader.PropertyToID("_LuxWaterMask_GDirectionCD");
|
||
private static readonly int GDirectionCDPID = Shader.PropertyToID("_GDirectionCD");
|
||
private static readonly int LuxWaterMask_GerstnerSecondaryWaves = Shader.PropertyToID("_LuxWaterMask_GerstnerSecondaryWaves");
|
||
private static readonly int GerstnerSecondaryWaves = Shader.PropertyToID("_GerstnerSecondaryWaves");
|
||
|
||
private static readonly int Lux_UnderWaterAmbientSkyLightPID = Shader.PropertyToID("_Lux_UnderWaterAmbientSkyLight");
|
||
|
||
private static readonly int Lux_UnderWaterSunColorPID = Shader.PropertyToID("_Lux_UnderWaterSunColor");
|
||
private static readonly int Lux_UnderWaterSunDirPID = Shader.PropertyToID("_Lux_UnderWaterSunDir");
|
||
private static readonly int Lux_UnderWaterSunDirViewSpacePID = Shader.PropertyToID("_Lux_UnderWaterSunDirViewSpace");
|
||
|
||
private static readonly int Lux_EdgeLengthPID = Shader.PropertyToID("_LuxWater_EdgeLength");
|
||
//private static readonly int Lux_WaterMeshScalePID = Shader.PropertyToID("_LuxWater_MeshScale");
|
||
private static readonly int Lux_ExtrusionPID = Shader.PropertyToID("_LuxWater_Extrusion");
|
||
private static readonly int LuxMask_ExtrusionPID = Shader.PropertyToID("_LuxWaterMask_Extrusion");
|
||
|
||
private static readonly int Lux_MaxDirLightDepthPID = Shader.PropertyToID("_MaxDirLightDepth");
|
||
private static readonly int Lux_MaxFogLightDepthPID = Shader.PropertyToID("_MaxFogLightDepth");
|
||
|
||
private static readonly int Lux_CausticsEnabledPID = Shader.PropertyToID("_CausticsEnabled");
|
||
private static readonly int Lux_CausticModePID = Shader.PropertyToID("_CausticMode");
|
||
private static readonly int Lux_UnderWaterFogColorPID = Shader.PropertyToID("_Lux_UnderWaterFogColor");
|
||
private static readonly int Lux_UnderWaterFogDensityPID = Shader.PropertyToID("_Lux_UnderWaterFogDensity");
|
||
private static readonly int Lux_UnderWaterFogAbsorptionCancellationPID = Shader.PropertyToID("_Lux_UnderWaterFogAbsorptionCancellation");
|
||
private static readonly int Lux_UnderWaterAbsorptionHeightPID = Shader.PropertyToID("_Lux_UnderWaterAbsorptionHeight");
|
||
private static readonly int Lux_UnderWaterAbsorptionMaxHeightPID = Shader.PropertyToID("_Lux_UnderWaterAbsorptionMaxHeight");
|
||
private static readonly int Lux_UnderWaterAbsorptionDepthPID = Shader.PropertyToID("_Lux_UnderWaterAbsorptionDepth");
|
||
private static readonly int Lux_UnderWaterAbsorptionColorStrengthPID = Shader.PropertyToID("_Lux_UnderWaterAbsorptionColorStrength");
|
||
private static readonly int Lux_UnderWaterAbsorptionStrengthPID = Shader.PropertyToID("_Lux_UnderWaterAbsorptionStrength");
|
||
private static readonly int Lux_UnderWaterUnderwaterScatteringPowerPID = Shader.PropertyToID("_Lux_UnderWaterUnderwaterScatteringPower");
|
||
private static readonly int Lux_UnderWaterUnderwaterScatteringIntensityPID = Shader.PropertyToID("_Lux_UnderWaterUnderwaterScatteringIntensity");
|
||
private static readonly int Lux_UnderWaterUnderwaterScatteringColorPID = Shader.PropertyToID("_Lux_UnderWaterUnderwaterScatteringColor");
|
||
private static readonly int Lux_UnderWaterUnderwaterScatteringOcclusionPID = Shader.PropertyToID("_Lux_UnderwaterScatteringOcclusion");
|
||
private static readonly int Lux_UnderWaterCausticsPID = Shader.PropertyToID("_Lux_UnderWaterCaustics");
|
||
|
||
private static readonly int Lux_UnderWaterDeferredFogParams = Shader.PropertyToID("_LuxUnderWaterDeferredFogParams");
|
||
private static readonly int CausticTexPID = Shader.PropertyToID("_CausticTex");
|
||
private static readonly int Lux_UnderWaterCausticsTilingPID = Shader.PropertyToID("_Lux_UnderWaterCausticsTiling");
|
||
private static readonly int Lux_UnderWaterCausticsScalePID = Shader.PropertyToID("_Lux_UnderWaterCausticsScale");
|
||
private static readonly int Lux_UnderWaterCausticsSpeedPID = Shader.PropertyToID("_Lux_UnderWaterCausticsSpeed");
|
||
private static readonly int Lux_UnderWaterCausticsSelfDistortionPID = Shader.PropertyToID("_Lux_UnderWaterCausticsSelfDistortion");
|
||
private static readonly int Lux_UnderWaterFinalBumpSpeed01PID = Shader.PropertyToID("_Lux_UnderWaterFinalBumpSpeed01");
|
||
private static readonly int Lux_UnderWaterFogDepthAttenPID = Shader.PropertyToID("_Lux_UnderWaterFogDepthAtten");
|
||
|
||
void OnEnable () {
|
||
if(instance != null) {
|
||
Destroy(this);
|
||
}
|
||
else {
|
||
instance = this;
|
||
}
|
||
// Set up materials
|
||
mat = new Material(Shader.Find("Hidden/Lux Water/WaterMask"));
|
||
blurMaterial = new Material(Shader.Find("Hidden/Lux Water/BlurEffectConeTap"));
|
||
blitMaterial = new Material(Shader.Find("Hidden/Lux Water/UnderWaterPost"));
|
||
// Get camera
|
||
if(cam == null) {
|
||
cam = GetComponent<Camera>();
|
||
}
|
||
// Metal support – make sure the camera actually renders a depth texture
|
||
cam.depthTextureMode |= DepthTextureMode.Depth;
|
||
camTransform = cam.transform;
|
||
// Set up Commandbufer
|
||
cb_Mask = new CommandBuffer();
|
||
cb_Mask.name = "Lux Water: Underwater Mask";
|
||
cam.AddCommandBuffer(cameraEvent, cb_Mask);
|
||
// Find the sun dynmically
|
||
if (FindSunOnEnable) {
|
||
if (SunGoName != "")
|
||
Sun = GameObject.Find(SunGoName).transform;
|
||
else if (SunTagName != "")
|
||
Sun = GameObject.FindWithTag(SunTagName).transform;
|
||
}
|
||
// Set Pojection (if flipped or not)
|
||
if(SystemInfo.usesReversedZBuffer) {
|
||
Projection = -1.0f;
|
||
}
|
||
else {
|
||
Projection = 1.0f;
|
||
}
|
||
islinear = (QualitySettings.desiredColorSpace == ColorSpace.Linear) ? true : false;
|
||
// Warm up needed shaders
|
||
if (PrewarmedShaders != null) {
|
||
if(!PrewarmedShaders.isWarmedUp) {
|
||
PrewarmedShaders.WarmUp();
|
||
}
|
||
}
|
||
// Get SunLight
|
||
if(Sun != null) {
|
||
SunLight = Sun.GetComponent<Light>();
|
||
}
|
||
// Set up Lists
|
||
RegisteredWaterVolumesIDs.Capacity = ListCapacity;
|
||
RegisteredWaterVolumes.Capacity = ListCapacity;
|
||
WaterMeshes.Capacity = ListCapacity;
|
||
WaterTransforms.Capacity = ListCapacity;
|
||
WaterMaterials.Capacity = ListCapacity;
|
||
WaterIsOnScreen.Capacity = ListCapacity;
|
||
WaterUsesSlidingVolume.Capacity = ListCapacity;
|
||
activeWaterVolumeCameras.Capacity = 2; // we consider 2 splitscreen cameras max
|
||
// Deep Water Lighting and advanced deferred Fog
|
||
SetDeepwaterLighting ();
|
||
SetDeferredFogParams ();
|
||
}
|
||
|
||
void CleanUp () {
|
||
instance = null;
|
||
if(UnderWaterMask != null) {
|
||
UnderWaterMask.Release();
|
||
Destroy(UnderWaterMask);
|
||
}
|
||
if(mat)
|
||
Destroy(mat);
|
||
if(blurMaterial)
|
||
Destroy(blurMaterial);
|
||
if(blitMaterial)
|
||
Destroy(blitMaterial);
|
||
|
||
Shader.DisableKeyword("LUXWATER_DEEPWATERLIGHTING");
|
||
Shader.DisableKeyword("LUXWATER_DEFERREDFOG");
|
||
cam.RemoveCommandBuffer(cameraEvent, cb_Mask);
|
||
}
|
||
|
||
void OnDisable () {
|
||
CleanUp ();
|
||
}
|
||
|
||
// Is also called when the scene gets unloaded.
|
||
void OnDestroy() {
|
||
CleanUp ();
|
||
}
|
||
|
||
void OnValidate () {
|
||
SetDeepwaterLighting ();
|
||
SetDeferredFogParams ();
|
||
}
|
||
|
||
public void SetDeferredFogParams() {
|
||
if(EnableAdvancedDeferredFog) {
|
||
Shader.EnableKeyword("LUXWATER_DEFERREDFOG");
|
||
Vector4 fogParams = new Vector4( (DoUnderWaterRendering) ? 1 : 0, FogDepthShift, FogEdgeBlending, 0);
|
||
Shader.SetGlobalVector(Lux_UnderWaterDeferredFogParams, fogParams);
|
||
}
|
||
else {
|
||
Shader.DisableKeyword("LUXWATER_DEFERREDFOG");
|
||
}
|
||
}
|
||
|
||
public void SetDeepwaterLighting () {
|
||
if(EnableDeepwaterLighting) {
|
||
Shader.EnableKeyword("LUXWATER_DEEPWATERLIGHTING");
|
||
if(activeWaterVolume != -1) {
|
||
Shader.SetGlobalFloat("_Lux_UnderWaterWaterSurfacePos", WaterSurfacePos);
|
||
}
|
||
else {
|
||
Shader.SetGlobalFloat("_Lux_UnderWaterWaterSurfacePos", DefaultWaterSurfacePosition);
|
||
}
|
||
Shader.SetGlobalFloat("_Lux_UnderWaterDirLightingDepth", DirectionalLightingFadeRange);
|
||
Shader.SetGlobalFloat("_Lux_UnderWaterFogLightingDepth", FogLightingFadeRange);
|
||
}
|
||
else {
|
||
Shader.DisableKeyword("LUXWATER_DEEPWATERLIGHTING");
|
||
}
|
||
}
|
||
|
||
public void RegisterWaterVolume(LuxWater_WaterVolume item, int ID, bool visible, bool SlidingVolume) {
|
||
RegisteredWaterVolumesIDs.Add(ID);
|
||
RegisteredWaterVolumes.Add(item);
|
||
WaterMeshes.Add(item.WaterVolumeMesh);
|
||
WaterMaterials.Add(item.transform.GetComponent<Renderer>().sharedMaterial);
|
||
WaterTransforms.Add(item.transform);
|
||
WaterIsOnScreen.Add(visible);
|
||
WaterUsesSlidingVolume.Add(SlidingVolume);
|
||
// Dummy: Trigger GetTexture() and SetGerstnerWaves() on register to prevent garbage in the OnRenderImage function.
|
||
var i = WaterMaterials.Count - 1;
|
||
Shader.SetGlobalTexture(Lux_UnderWaterCausticsPID, WaterMaterials[i].GetTexture(CausticTexPID) );
|
||
SetGerstnerWaves(i);
|
||
//mat.SetPass(0);
|
||
//Graphics.DrawMeshNow( WaterMeshes[i], WaterTransforms[i].localToWorldMatrix, 0); // submesh 0 = Water box volume
|
||
}
|
||
|
||
public void DeRegisterWaterVolume(LuxWater_WaterVolume item, int ID) {
|
||
int index = RegisteredWaterVolumesIDs.IndexOf(ID);
|
||
if (activeWaterVolume == index) {
|
||
activeWaterVolume = -1;
|
||
}
|
||
RegisteredWaterVolumesIDs.RemoveAt(index);
|
||
RegisteredWaterVolumes.RemoveAt(index);
|
||
WaterMeshes.RemoveAt(index);
|
||
WaterMaterials.RemoveAt(index);
|
||
WaterTransforms.RemoveAt(index);
|
||
WaterIsOnScreen.RemoveAt(index);
|
||
WaterUsesSlidingVolume.RemoveAt(index);
|
||
}
|
||
|
||
public void SetWaterVisible(int ID) {
|
||
int index = RegisteredWaterVolumesIDs.IndexOf(ID);
|
||
WaterIsOnScreen[index] = true;
|
||
}
|
||
public void SetWaterInvisible(int ID) {
|
||
int index = RegisteredWaterVolumesIDs.IndexOf(ID);
|
||
WaterIsOnScreen[index] = false;
|
||
}
|
||
|
||
public void EnteredWaterVolume (LuxWater_WaterVolume item, int ID, Camera triggerCam, float GridSize) {
|
||
DoUnderWaterRendering = true;
|
||
int index = RegisteredWaterVolumesIDs.IndexOf(ID);
|
||
if(index != activeWaterVolume) {
|
||
activeWaterVolume = index;
|
||
activeGridSize = GridSize;
|
||
|
||
WaterSurfacePos = WaterTransforms[activeWaterVolume].position.y;
|
||
|
||
// Update Transparents
|
||
for (int i = 0; i < m_aboveWatersurface.Count; i++) {
|
||
m_aboveWatersurface[i].renderQueue = 2997; // 2998
|
||
}
|
||
for (int i = 0; i < m_belowWatersurface.Count; i++) {
|
||
m_belowWatersurface[i].renderQueue = 3001;
|
||
}
|
||
}
|
||
|
||
if (!activeWaterVolumeCameras.Contains(triggerCam)) {
|
||
activeWaterVolumeCameras.Add(triggerCam);
|
||
}
|
||
}
|
||
|
||
public void LeftWaterVolume (LuxWater_WaterVolume item, int ID, Camera triggerCam) {
|
||
DoUnderWaterRendering = false;
|
||
int index = RegisteredWaterVolumesIDs.IndexOf(ID);
|
||
if (activeWaterVolume == index) {
|
||
activeWaterVolume = -1;
|
||
// Update Transparents
|
||
for (int i = 0; i < m_aboveWatersurface.Count; i++) {
|
||
m_aboveWatersurface[i].renderQueue = 3000;
|
||
}
|
||
for (int i = 0; i < m_belowWatersurface.Count; i++) {
|
||
m_belowWatersurface[i].renderQueue = 2997; // 2998
|
||
}
|
||
}
|
||
|
||
if (activeWaterVolumeCameras.Contains(triggerCam)) {
|
||
activeWaterVolumeCameras.Remove(triggerCam);
|
||
}
|
||
}
|
||
|
||
|
||
// void OnPreCull () {
|
||
// Here we have to use OnPreRender to make XR Multipass work
|
||
void OnPreRender () {
|
||
SetDeferredFogParams();
|
||
RenderWaterMask( cam, false, cb_Mask );
|
||
}
|
||
|
||
[ImageEffectOpaque]
|
||
void OnRenderImage(RenderTexture src, RenderTexture dest) {
|
||
RenderUnderWater( src, dest, cam, false);
|
||
}
|
||
|
||
|
||
// ---------------------------------------------------------
|
||
|
||
|
||
public void SetGerstnerWaves (int index) {
|
||
if (WaterMaterials[index].GetFloat(GerstnerEnabledPID) == 1.0f) {
|
||
mat.EnableKeyword("GERSTNERENABLED");
|
||
mat.SetVector(LuxWaterMask_GerstnerVertexIntensityPID, WaterMaterials[index].GetVector(GerstnerVertexIntensityPID) );
|
||
mat.SetVector(LuxWaterMask_GAmplitudePID, WaterMaterials[index].GetVector(GAmplitudePID) );
|
||
mat.SetVector(LuxWaterMask_GFinalFrequencyPID, WaterMaterials[index].GetVector(GFinalFrequencyPID) );
|
||
mat.SetVector(LuxWaterMask_GSteepnessPID, WaterMaterials[index].GetVector(GSteepnessPID) );
|
||
mat.SetVector(LuxWaterMask_GFinalSpeedPID, WaterMaterials[index].GetVector(GFinalSpeedPID) );
|
||
mat.SetVector(LuxWaterMask_GDirectionABPID, WaterMaterials[index].GetVector(GDirectionABPID) );
|
||
mat.SetVector(LuxWaterMask_GDirectionCDPID, WaterMaterials[index].GetVector(GDirectionCDPID) );
|
||
mat.SetVector(LuxWaterMask_GerstnerSecondaryWaves, WaterMaterials[index].GetVector(GerstnerSecondaryWaves) );
|
||
}
|
||
else {
|
||
mat.DisableKeyword("GERSTNERENABLED");
|
||
}
|
||
}
|
||
|
||
|
||
public void RenderWaterMask(Camera currentCamera, bool SecondaryCameraRendering, CommandBuffer cmb) {
|
||
|
||
// As we need _Time when drawing the Underwatermask (Gerstner Waves) we have to use a custom _Lux_Time as _Time will not be updated OnPreCull
|
||
Shader.SetGlobalFloat("_Lux_Time", Time.timeSinceLevelLoad);
|
||
|
||
currentCamera.depthTextureMode |= DepthTextureMode.Depth;
|
||
camTransform = currentCamera.transform;
|
||
|
||
// XR support
|
||
var xrSinglePassInstanced = false;
|
||
var dim = TextureDimension.Tex2D;
|
||
if (currentCamera.stereoEnabled) {
|
||
if( XRSettings.stereoRenderingMode == XRSettings.StereoRenderingMode.SinglePassInstanced) {
|
||
xrSinglePassInstanced = true;
|
||
dim = TextureDimension.Tex2DArray;
|
||
}
|
||
}
|
||
|
||
/* // Check if the CommandBuffer already exists
|
||
if (cb_DepthGrab == null) {
|
||
//var commandBuffers = cam.GetCommandBuffers(CameraEvent.BeforeForwardAlpha);
|
||
var commandBuffers = cam.GetCommandBuffers(CameraEvent.AfterSkybox); //AfterImageEffectsOpaque); // this is where deferred fog gets rendered
|
||
for(int i = 0; i < commandBuffers.Length; i++) {
|
||
if(commandBuffers[i].name == "Lux Water Grab Depth") {
|
||
cb_DepthGrab = commandBuffers[i];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (cb_DepthGrab == null) {
|
||
cb_DepthGrab = new CommandBuffer();
|
||
cb_DepthGrab.name = "Lux Water Grab Depth";
|
||
cam.AddCommandBuffer(CameraEvent.AfterSkybox, cb_DepthGrab);
|
||
}
|
||
cb_DepthGrab.Clear();
|
||
int depthGrabID = Shader.PropertyToID("_Lux_GrabbedDepth");
|
||
cb_DepthGrab.GetTemporaryRT(depthGrabID, -1, -1, 0, FilterMode.Point, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
|
||
cb_DepthGrab.Blit(BuiltinRenderTextureType.CameraTarget, depthGrabID);
|
||
*/
|
||
|
||
// Setup UnderWaterMask and SetRenderTarget upfront to prevent spikes.
|
||
UnityEngine.Profiling.Profiler.BeginSample("Create UnderWaterMask Tex");
|
||
if (!UnderWaterMask) {
|
||
UnderWaterMask = new RenderTexture(currentCamera.pixelWidth, currentCamera.pixelHeight, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||
UnderWaterMask.dimension = dim;
|
||
if(xrSinglePassInstanced) {
|
||
UnderWaterMask.volumeDepth = 2;
|
||
}
|
||
}
|
||
// if SecondaryCameraRendering = true (Splitscreen) do not resize UnderWaterMask
|
||
// as the secondary camera might have a smaller frame which would be quite expensive...
|
||
else if (UnderWaterMask.width != currentCamera.pixelWidth && !SecondaryCameraRendering) {
|
||
DestroyImmediate(UnderWaterMask);
|
||
UnderWaterMask = new RenderTexture(currentCamera.pixelWidth, currentCamera.pixelHeight, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||
UnderWaterMask.dimension = dim;
|
||
if(xrSinglePassInstanced) {
|
||
UnderWaterMask.volumeDepth = 2;
|
||
}
|
||
}
|
||
|
||
Shader.SetGlobalTexture(UnderWaterMaskPID, UnderWaterMask);
|
||
//Graphics.SetRenderTarget(UnderWaterMask);
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
|
||
// Set frustum corners – which are needed to reconstruct the world position in the underwater post shader.
|
||
// As this spikes as hell when a volume gets active we simply do it all the time.
|
||
UnityEngine.Profiling.Profiler.BeginSample("Set up Frustum Corners");
|
||
|
||
// https://forum.unity.com/threads/stereo-frustum-corners-seem-incorrect.588268/
|
||
currentCamera.CalculateFrustumCorners(new Rect(0, 0, 1, 1), currentCamera.farClipPlane, currentCamera.stereoActiveEye, frustumCorners);
|
||
var bottomLeft = camTransform.TransformVector(frustumCorners[0]);
|
||
var topLeft = camTransform.TransformVector(frustumCorners[1]);
|
||
var topRight = camTransform.TransformVector(frustumCorners[2]);
|
||
var bottomRight = camTransform.TransformVector(frustumCorners[3]);
|
||
|
||
frustumCornersArray.SetRow(0, bottomLeft);
|
||
frustumCornersArray.SetRow(1, bottomRight);
|
||
frustumCornersArray.SetRow(2, topLeft);
|
||
frustumCornersArray.SetRow(3, topRight);
|
||
|
||
if (xrSinglePassInstanced) {
|
||
currentCamera.CalculateFrustumCorners(new Rect(0, 0, 1, 1), currentCamera.farClipPlane, Camera.MonoOrStereoscopicEye.Right, frustumCorners);
|
||
bottomLeft = camTransform.TransformVector(frustumCorners[0]);
|
||
topLeft = camTransform.TransformVector(frustumCorners[1]);
|
||
topRight = camTransform.TransformVector(frustumCorners[2]);
|
||
bottomRight = camTransform.TransformVector(frustumCorners[3]);
|
||
|
||
frustumCornersArray2nd.SetRow(0, bottomLeft);
|
||
frustumCornersArray2nd.SetRow(1, bottomRight);
|
||
frustumCornersArray2nd.SetRow(2, topLeft);
|
||
frustumCornersArray2nd.SetRow(3, topRight);
|
||
}
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
|
||
// Set ambient lighting. Spikes so it is taken out of the if.
|
||
UnityEngine.Profiling.Profiler.BeginSample("Set up ambient lighting");
|
||
ambientProbe = RenderSettings.ambientProbe;
|
||
ambientProbe.Evaluate(directions, AmbientLightingSamples);
|
||
if (islinear)
|
||
Shader.SetGlobalColor(Lux_UnderWaterAmbientSkyLightPID, (AmbientLightingSamples[0] * RenderSettings.ambientIntensity).linear);
|
||
else
|
||
Shader.SetGlobalColor(Lux_UnderWaterAmbientSkyLightPID, AmbientLightingSamples[0] * RenderSettings.ambientIntensity);
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
|
||
// Do all heavy spiking stuff before this return!
|
||
// Check if currentCamera is actually within the watervolume
|
||
if (!activeWaterVolumeCameras.Contains(currentCamera) && !EnableAdvancedDeferredFog) {
|
||
// This return breaks split screen cameras, tess, gerstners and the "sample mask to correct vface"
|
||
// But actually we do not really need it. The script will never draw anything but just create a black mask: GL.Clear(true, true, Color.black, 1);
|
||
// return;
|
||
}
|
||
|
||
// Render UnderWaterMask
|
||
if(activeWaterVolume > -1) {
|
||
// Tell all shaders that underwater rendering is active (like e.g. fog shaders)
|
||
Shader.EnableKeyword("LUXWATERENABLED");
|
||
// In case deep water lighting is disabled we pick DirectionalLightingFadeRange from the water material
|
||
if(!EnableDeepwaterLighting) {
|
||
Shader.SetGlobalFloat("_Lux_UnderWaterDirLightingDepth", WaterMaterials[activeWaterVolume].GetFloat(Lux_MaxDirLightDepthPID));
|
||
Shader.SetGlobalFloat("_Lux_UnderWaterFogLightingDepth", WaterMaterials[activeWaterVolume].GetFloat(Lux_MaxFogLightDepthPID));
|
||
}
|
||
Shader.SetGlobalFloat("_Lux_UnderWaterWaterSurfacePos", WaterSurfacePos);
|
||
}
|
||
else {
|
||
Shader.DisableKeyword("LUXWATERENABLED");
|
||
}
|
||
|
||
RenderTargetIdentifier target;
|
||
if(xrSinglePassInstanced) {
|
||
target = new RenderTargetIdentifier(UnderWaterMask, 0, CubemapFace.Unknown, -1); // slice = -1 makes it draw into both layers!
|
||
}
|
||
else {
|
||
target = new RenderTargetIdentifier(UnderWaterMask);
|
||
}
|
||
|
||
cmb.Clear();
|
||
cmb.SetRenderTarget(target);
|
||
cmb.ClearRenderTarget(true, true, new Color(0,0,0,0), 1.0f);
|
||
cmb.SetGlobalMatrix(Lux_FrustumCornersWSPID, frustumCornersArray);
|
||
cmb.SetGlobalMatrix("_Lux_FrustumCornersWS2ndEye", frustumCornersArray2nd);
|
||
cmb.SetGlobalVector(Lux_CameraWSPID, camTransform.position);
|
||
|
||
//GL.PushMatrix();
|
||
//GL.Clear(true, true, Color.black, 1);
|
||
//camProj = currentCamera.projectionMatrix;
|
||
//GL.LoadProjectionMatrix(camProj);
|
||
|
||
// These params might be odd (because of the scene camera or another camera rendering before). Needed by tessellation shader!
|
||
Shader.SetGlobalVector("_WorldSpaceCameraPos", camTransform.position);
|
||
// x is 1.0 (or –1.0 if currently rendering with a flipped projection matrix), y is the camera’s near plane, z is the camera’s far plane and w is 1/FarPlane.
|
||
Shader.SetGlobalVector("_ProjectionParams", new Vector4( Projection, currentCamera.nearClipPlane, currentCamera.farClipPlane, 1.0f / currentCamera.farClipPlane ) );
|
||
// x is the width of the camera’s target texture in pixels, y is the height of the camera’s target texture in pixels, z is 1.0 + 1.0/width and w is 1.0 + 1.0/height.
|
||
Shader.SetGlobalVector("_ScreenParams", new Vector4(currentCamera.pixelWidth, currentCamera.pixelHeight, 1.0f + 1.0f / currentCamera.pixelWidth, 1.0f + 1.0f / currentCamera.pixelHeight ) );
|
||
|
||
for (int i = 0; i < RegisteredWaterVolumes.Count; i++ ) {
|
||
|
||
if( !WaterIsOnScreen[i] && i != activeWaterVolume) {
|
||
continue;
|
||
}
|
||
if( !EnableAdvancedDeferredFog && i != activeWaterVolume) {
|
||
continue;
|
||
}
|
||
|
||
WatervolumeMatrix = WaterTransforms[i].localToWorldMatrix;
|
||
|
||
// Handle sliding volumes
|
||
if( WaterUsesSlidingVolume[i]) {
|
||
// Here we have to change the WatervolumeMatrix and center the volume on the camera
|
||
var CamPos = camTransform.position;
|
||
// Extract local position. NOTE: We must explicitly define a float4 here! (no var)
|
||
Vector4 position = WatervolumeMatrix.GetColumn(3);
|
||
|
||
var scale = WaterTransforms[i].lossyScale;
|
||
var gridSteps = new Vector2(activeGridSize * scale.x, activeGridSize * scale.z);
|
||
|
||
var factor = (float)Math.Round(CamPos.x / gridSteps.x);
|
||
var stepLeft = gridSteps.x * factor;
|
||
factor = (float)Math.Round(CamPos.z / gridSteps.y );
|
||
var stepUp = gridSteps.y * factor;
|
||
|
||
// We have to factor in the original position
|
||
position.x = stepLeft + (position.x % gridSteps.x);
|
||
position.z = stepUp + (position.z % gridSteps.y);
|
||
|
||
WatervolumeMatrix.SetColumn(3, position);
|
||
|
||
}
|
||
|
||
var waterMaterial = WaterMaterials[i];
|
||
|
||
UnityEngine.Profiling.Profiler.BeginSample("Set up Gerstner");
|
||
// Gerstner Waves
|
||
|
||
// SetGerstnerWaves(i); // this produces 32bytes of garbage when entering a volume? So we do it manually.
|
||
if (waterMaterial.GetFloat(GerstnerEnabledPID) == 1.0f) {
|
||
mat.EnableKeyword("GERSTNERENABLED");
|
||
mat.SetVector(LuxWaterMask_GerstnerVertexIntensityPID, waterMaterial.GetVector(GerstnerVertexIntensityPID) );
|
||
mat.SetVector(LuxWaterMask_GAmplitudePID, waterMaterial.GetVector(GAmplitudePID) );
|
||
mat.SetVector(LuxWaterMask_GFinalFrequencyPID, waterMaterial.GetVector(GFinalFrequencyPID) );
|
||
mat.SetVector(LuxWaterMask_GSteepnessPID, waterMaterial.GetVector(GSteepnessPID) );
|
||
mat.SetVector(LuxWaterMask_GFinalSpeedPID, waterMaterial.GetVector(GFinalSpeedPID) );
|
||
mat.SetVector(LuxWaterMask_GDirectionABPID, waterMaterial.GetVector(GDirectionABPID) );
|
||
mat.SetVector(LuxWaterMask_GDirectionCDPID, waterMaterial.GetVector(GDirectionCDPID) );
|
||
mat.SetVector(LuxWaterMask_GerstnerSecondaryWaves, waterMaterial.GetVector(GerstnerSecondaryWaves) );
|
||
}
|
||
else {
|
||
mat.DisableKeyword("GERSTNERENABLED");
|
||
}
|
||
|
||
// Projectors
|
||
mat.SetFloat(LuxMask_ExtrusionPID, waterMaterial.GetFloat(Lux_ExtrusionPID) );
|
||
|
||
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
|
||
// Draw UnderWaterMask
|
||
UnityEngine.Profiling.Profiler.BeginSample("Draw Water Mask");
|
||
bool useTessellation = waterMaterial.HasProperty(Lux_EdgeLengthPID) && SystemInfo.graphicsShaderLevel >= 46;
|
||
if (useTessellation) {
|
||
mat.SetFloat(Lux_EdgeLengthPID, waterMaterial.GetFloat(Lux_EdgeLengthPID));
|
||
}
|
||
UnityEngine.Profiling.Profiler.BeginSample("Draw Water Volume");
|
||
if ( i == activeWaterVolume && activeWaterVolumeCameras.Contains(currentCamera) ) {
|
||
if( WaterUsesSlidingVolume[i] && useTessellation) {
|
||
//mat.SetPass(5);
|
||
cmb.DrawMesh( WaterMeshes[i], WatervolumeMatrix, mat, 0, 5); // submesh 0 = Water box volume
|
||
}
|
||
else {
|
||
//mat.SetPass(0);
|
||
cmb.DrawMesh( WaterMeshes[i], WatervolumeMatrix, mat, 0, 0); // submesh 0 = Water box volume
|
||
}
|
||
//Graphics.DrawMeshNow( WaterMeshes[i], WatervolumeMatrix, 0); // submesh 0 = Water box volume
|
||
|
||
}
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
// Draw water surface only if volume is active or AdvancedDeferredFog is enabled
|
||
if( i == activeWaterVolume && activeWaterVolumeCameras.Contains(currentCamera) || EnableAdvancedDeferredFog ) {
|
||
if (useTessellation) {
|
||
if ( i == activeWaterVolume) {
|
||
//mat.SetPass(3);
|
||
cmb.DrawMesh( WaterMeshes[i], WatervolumeMatrix, mat, 1, 3); // submesh 1 = Water surface
|
||
}
|
||
else {
|
||
//mat.SetPass(4);
|
||
cmb.DrawMesh( WaterMeshes[i], WatervolumeMatrix, mat, 1, 4); // submesh 1 = Water surface
|
||
}
|
||
}
|
||
else {
|
||
if ( i == activeWaterVolume) {
|
||
//mat.SetPass(1);
|
||
cmb.DrawMesh( WaterMeshes[i], WatervolumeMatrix, mat, 1, 1); // submesh 1 = Water surface
|
||
}
|
||
else {
|
||
//mat.SetPass(2);
|
||
cmb.DrawMesh( WaterMeshes[i], WatervolumeMatrix, mat, 1, 2); // submesh 1 = Water surface
|
||
}
|
||
}
|
||
//Graphics.DrawMeshNow( WaterMeshes[i], WatervolumeMatrix, 1); // submesh 1 = Water surface
|
||
|
||
}
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
}
|
||
//GL.PopMatrix();
|
||
}
|
||
|
||
public void RenderUnderWater(RenderTexture src, RenderTexture dest, Camera currentCamera, bool SecondaryCameraRendering) {
|
||
|
||
// Check if currentCamera is actually within the watervolume
|
||
// TODO: This prevents fps camera from rendering underwater
|
||
if (activeWaterVolumeCameras.Contains(currentCamera)) {
|
||
if(DoUnderWaterRendering && activeWaterVolume > -1) {
|
||
// We currently only support one active water volume. So we setup the materials only once.
|
||
if (!UnderwaterIsSetUp) {
|
||
if(Sun) {
|
||
Vector3 SunDir = -Sun.forward;
|
||
Color SunColor = SunLight.color * SunLight.intensity;
|
||
if (islinear) {
|
||
SunColor = SunColor.linear;
|
||
}
|
||
Shader.SetGlobalColor(Lux_UnderWaterSunColorPID, (SunColor) * Mathf.Clamp01( Vector3.Dot(SunDir, Vector3.up) ) );
|
||
Shader.SetGlobalVector(Lux_UnderWaterSunDirPID, -SunDir );
|
||
Shader.SetGlobalVector(Lux_UnderWaterSunDirViewSpacePID, currentCamera.WorldToViewportPoint(
|
||
currentCamera.transform.position - SunDir * 1000.0f
|
||
) );
|
||
}
|
||
if (WaterMaterials[activeWaterVolume].GetFloat(Lux_CausticsEnabledPID) == 1) {
|
||
blitMaterial.EnableKeyword("GEOM_TYPE_FROND");
|
||
if (WaterMaterials[activeWaterVolume].GetFloat(Lux_CausticModePID) == 1) {
|
||
blitMaterial.EnableKeyword("GEOM_TYPE_LEAF");
|
||
}
|
||
else {
|
||
blitMaterial.DisableKeyword("GEOM_TYPE_LEAF");
|
||
}
|
||
}
|
||
else {
|
||
blitMaterial.DisableKeyword("GEOM_TYPE_FROND");
|
||
}
|
||
if (islinear) {
|
||
Shader.SetGlobalColor(Lux_UnderWaterFogColorPID, WaterMaterials[activeWaterVolume].GetColor("_Color").linear );
|
||
}
|
||
else {
|
||
Shader.SetGlobalColor(Lux_UnderWaterFogColorPID, WaterMaterials[activeWaterVolume].GetColor("_Color"));
|
||
}
|
||
|
||
Shader.SetGlobalFloat(Lux_UnderWaterFogDensityPID, WaterMaterials[activeWaterVolume].GetFloat("_Density") );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterFogAbsorptionCancellationPID, WaterMaterials[activeWaterVolume].GetFloat("_FogAbsorptionCancellation") );
|
||
|
||
Shader.SetGlobalFloat(Lux_UnderWaterAbsorptionHeightPID, WaterMaterials[activeWaterVolume].GetFloat("_AbsorptionHeight") );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterAbsorptionMaxHeightPID, WaterMaterials[activeWaterVolume].GetFloat("_AbsorptionMaxHeight") );
|
||
|
||
Shader.SetGlobalFloat(Lux_UnderWaterAbsorptionDepthPID, WaterMaterials[activeWaterVolume].GetFloat("_AbsorptionDepth") );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterAbsorptionColorStrengthPID, WaterMaterials[activeWaterVolume].GetFloat("_AbsorptionColorStrength") );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterAbsorptionStrengthPID, WaterMaterials[activeWaterVolume].GetFloat("_AbsorptionStrength") );
|
||
|
||
Shader.SetGlobalFloat(Lux_UnderWaterUnderwaterScatteringPowerPID, WaterMaterials[activeWaterVolume].GetFloat("_ScatteringPower")); //"_UnderwaterScatteringPower"));
|
||
Shader.SetGlobalFloat(Lux_UnderWaterUnderwaterScatteringIntensityPID, WaterMaterials[activeWaterVolume].GetFloat("_UnderwaterScatteringIntensity"));
|
||
if (islinear) {
|
||
Shader.SetGlobalColor(Lux_UnderWaterUnderwaterScatteringColorPID, WaterMaterials[activeWaterVolume].GetColor("_TranslucencyColor").linear);
|
||
}
|
||
else {
|
||
Shader.SetGlobalColor(Lux_UnderWaterUnderwaterScatteringColorPID, WaterMaterials[activeWaterVolume].GetColor("_TranslucencyColor"));
|
||
}
|
||
Shader.SetGlobalFloat(Lux_UnderWaterUnderwaterScatteringOcclusionPID, WaterMaterials[activeWaterVolume].GetFloat("_ScatterOcclusion")); //"_UnderwaterScatteringPower"));
|
||
|
||
// This spikes and produces garbage. So we do it in RegisterWaterVolume as well.
|
||
Shader.SetGlobalTexture(Lux_UnderWaterCausticsPID, WaterMaterials[activeWaterVolume].GetTexture(CausticTexPID) );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterCausticsTilingPID, WaterMaterials[activeWaterVolume].GetFloat("_CausticsTiling"));
|
||
Shader.SetGlobalFloat(Lux_UnderWaterCausticsScalePID, WaterMaterials[activeWaterVolume].GetFloat("_CausticsScale") );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterCausticsSpeedPID, WaterMaterials[activeWaterVolume].GetFloat("_CausticsSpeed") );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterCausticsTilingPID, WaterMaterials[activeWaterVolume].GetFloat("_CausticsTiling") );
|
||
Shader.SetGlobalFloat(Lux_UnderWaterCausticsSelfDistortionPID, WaterMaterials[activeWaterVolume].GetFloat("_CausticsSelfDistortion") );
|
||
Shader.SetGlobalVector(Lux_UnderWaterFinalBumpSpeed01PID, WaterMaterials[activeWaterVolume].GetVector("_FinalBumpSpeed01") );
|
||
Shader.SetGlobalVector(Lux_UnderWaterFogDepthAttenPID, WaterMaterials[activeWaterVolume].GetVector("_DepthAtten"));
|
||
}
|
||
// Calculate fog and color attenuation and copy to Screen
|
||
Graphics.Blit(src, dest, blitMaterial, 0);
|
||
}
|
||
// We have to blit in any case - otherwise the screen will be black.
|
||
else {
|
||
Graphics.Blit(src, dest);
|
||
}
|
||
}
|
||
// We have to blit in any case - otherwise the screen will be black.
|
||
else {
|
||
Graphics.Blit(src, dest);
|
||
}
|
||
}
|
||
|
||
|
||
#if UNITY_EDITOR
|
||
void OnDrawGizmos() {
|
||
if (enableDebug) {
|
||
if(cam == null || UnderWaterMask == null) { // || activeWaterVolume == -1)
|
||
return;
|
||
}
|
||
int textureDrawWidth = (int)(cam.aspect * 128.0f);
|
||
GL.PushMatrix();
|
||
GL.LoadPixelMatrix(0, Screen.width, Screen.height, 0);
|
||
Graphics.DrawTexture(new Rect(0, 0, textureDrawWidth, 128), UnderWaterMask);
|
||
GL.PopMatrix();
|
||
}
|
||
}
|
||
|
||
void OnGUI() {
|
||
if(enableDebug) {
|
||
var Alignement = GUI.skin.label.alignment;
|
||
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
|
||
if (activeWaterVolume == -1) {
|
||
GUI.Label(new Rect(10, 0, 160, 40), "Active water volume: none\nRegistered volumes: " + RegisteredWaterVolumes.Count);
|
||
}
|
||
else {
|
||
GUI.Label(new Rect(10, 0, 400, 40), "Active water volume: " + RegisteredWaterVolumes[activeWaterVolume].transform.gameObject.name + "\nRegistered volumes: " + RegisteredWaterVolumes.Count ); //+ activeWaterVolume.ToString() );
|
||
}
|
||
GUI.skin.label.alignment = Alignement;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
} |