diff options
Diffstat (limited to 'DeepSky.Haze/DS_HazeLightVolume.cs')
-rw-r--r-- | DeepSky.Haze/DS_HazeLightVolume.cs | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/DeepSky.Haze/DS_HazeLightVolume.cs b/DeepSky.Haze/DS_HazeLightVolume.cs new file mode 100644 index 0000000..1a9928e --- /dev/null +++ b/DeepSky.Haze/DS_HazeLightVolume.cs @@ -0,0 +1,604 @@ +using System; +using UnityEngine; +using UnityEngine.Rendering; + +namespace DeepSky.Haze; + +[ExecuteInEditMode] +[RequireComponent(typeof(Light))] +[AddComponentMenu("DeepSky Haze/Light Volume", 2)] +public class DS_HazeLightVolume : MonoBehaviour +{ + private static int kConeSubdivisions = 16; + + private static Shader kLightVolumeShader; + + private Light m_Light; + + private Mesh m_ProxyMesh; + + private Matrix4x4 m_LightVolumeTransform; + + private CommandBuffer m_RenderCmd; + + private Material m_VolumeMaterial; + + private Vector3 m_DensityOffset = Vector3.zero; + + [SerializeField] + private DS_SamplingQuality m_Samples = DS_SamplingQuality.x16; + + [SerializeField] + private DS_LightFalloff m_Falloff; + + [SerializeField] + private bool m_UseFog; + + [SerializeField] + [Range(0f, 100f)] + private float m_Scattering = 1f; + + [SerializeField] + [Range(0f, 1f)] + private float m_SecondaryScattering = 0.1f; + + [SerializeField] + [Range(-1f, 1f)] + private float m_ScatteringDirection = 0.75f; + + [SerializeField] + private Texture3D m_DensityTexture; + + [SerializeField] + [Range(0.1f, 10f)] + private float m_DensityTextureScale = 1f; + + [SerializeField] + [Range(0.1f, 3f)] + private float m_DensityTextureContrast = 1f; + + [SerializeField] + private Vector3 m_AnimateDirection = Vector3.zero; + + [SerializeField] + [Range(0f, 10f)] + private float m_AnimateSpeed = 1f; + + [SerializeField] + private float m_StartFade = 25f; + + [SerializeField] + private float m_EndFade = 30f; + + [SerializeField] + [Range(0.01f, 1f)] + private float m_FarClip = 1f; + + private LightType m_PreviousLightType = LightType.Point; + + private float m_PreviousAngle = 45f; + + private LightShadows m_PreviousShadowMode; + + public Light LightSource => m_Light; + + public LightType Type => (!(m_Light != null)) ? LightType.Point : m_Light.type; + + public bool CastShadows => (m_Light.shadows != 0) ? true : false; + + public CommandBuffer RenderCommandBuffer => m_RenderCmd; + + public DS_SamplingQuality Samples + { + get + { + return m_Samples; + } + set + { + m_Samples = value; + } + } + + public DS_LightFalloff Falloff + { + get + { + return m_Falloff; + } + set + { + m_Falloff = value; + } + } + + public bool UseFog + { + get + { + return m_UseFog; + } + set + { + m_UseFog = value; + } + } + + public float Scattering + { + get + { + return m_Scattering; + } + set + { + m_Scattering = Mathf.Clamp01(value); + } + } + + public float ScatteringDirection + { + get + { + return m_ScatteringDirection; + } + set + { + m_ScatteringDirection = Mathf.Clamp(value, -1f, 1f); + } + } + + public Texture3D DensityTexture + { + get + { + return m_DensityTexture; + } + set + { + m_DensityTexture = value; + } + } + + public float DensityTextureScale + { + get + { + return m_DensityTextureScale; + } + set + { + m_DensityTextureScale = Mathf.Clamp01(m_DensityTextureScale); + } + } + + public Vector3 AnimateDirection + { + get + { + return m_AnimateDirection; + } + set + { + m_AnimateDirection = value.normalized; + } + } + + public float AnimateSpeed + { + get + { + return m_AnimateSpeed; + } + set + { + m_AnimateSpeed = Mathf.Clamp01(value); + } + } + + public float StartFade + { + get + { + return m_StartFade; + } + set + { + m_StartFade = ((!(value > 0f)) ? 1f : value); + } + } + + public float EndFade + { + get + { + return m_EndFade; + } + set + { + m_EndFade = ((!(value > m_StartFade)) ? (m_StartFade + 1f) : value); + } + } + + private void CreateProxyMeshCone(Mesh proxyMesh) + { + Vector3[] array = null; + int[] array2 = null; + float num = Mathf.Tan(m_Light.spotAngle / 2f * ((float)Math.PI / 180f)) * m_FarClip; + array = new Vector3[kConeSubdivisions + 2]; + array2 = new int[kConeSubdivisions * 6]; + float num2 = (float)Math.PI * 2f / (float)kConeSubdivisions; + float num3 = 0f; + for (int i = 0; i < kConeSubdivisions; i++) + { + ref Vector3 reference = ref array[i]; + reference = new Vector3(Mathf.Sin(num3) * num, Mathf.Cos(num3) * num, m_FarClip); + num3 += num2; + } + ref Vector3 reference2 = ref array[kConeSubdivisions]; + reference2 = new Vector3(0f, 0f, m_FarClip); + ref Vector3 reference3 = ref array[kConeSubdivisions + 1]; + reference3 = new Vector3(0f, 0f, -0.1f); + for (int j = 0; j < kConeSubdivisions; j++) + { + array2[j * 3] = kConeSubdivisions; + array2[j * 3 + 1] = ((j != kConeSubdivisions - 1) ? (j + 1) : 0); + array2[j * 3 + 2] = j; + array2[kConeSubdivisions * 3 + j * 3] = j; + array2[kConeSubdivisions * 3 + j * 3 + 1] = ((j != kConeSubdivisions - 1) ? (j + 1) : 0); + array2[kConeSubdivisions * 3 + j * 3 + 2] = kConeSubdivisions + 1; + } + proxyMesh.vertices = array; + proxyMesh.triangles = array2; + proxyMesh.hideFlags = HideFlags.HideAndDontSave; + m_PreviousAngle = m_Light.spotAngle; + } + + public bool ProxyMeshRequiresRebuild() + { + if (m_Light == null) + { + return false; + } + if (m_ProxyMesh == null || (m_Light.type == LightType.Spot && m_Light.spotAngle != m_PreviousAngle)) + { + return true; + } + return false; + } + + public bool LightTypeChanged() + { + if (m_Light == null) + { + return false; + } + return m_Light.type != m_PreviousLightType; + } + + public void UpdateLightType() + { + m_VolumeMaterial.DisableKeyword("POINT_COOKIE"); + m_VolumeMaterial.DisableKeyword("SPOT_COOKIE"); + if (m_Light.type == LightType.Point) + { + m_VolumeMaterial.EnableKeyword("POINT"); + m_VolumeMaterial.DisableKeyword("SPOT"); + } + else + { + if (m_Light.type != 0) + { + Debug.LogError("DeepSky::DS_HazeLightVolume: Unsupported light type! " + base.gameObject.name + " will not render volumetrics."); + base.enabled = false; + return; + } + m_VolumeMaterial.EnableKeyword("SPOT"); + m_VolumeMaterial.DisableKeyword("POINT"); + } + RebuildProxyMesh(); + m_PreviousLightType = m_Light.type; + } + + public void RebuildProxyMesh() + { + switch (m_Light.type) + { + case LightType.Point: + if (m_PreviousLightType != LightType.Point) + { + UnityEngine.Object.DestroyImmediate(m_ProxyMesh); + } + m_ProxyMesh = Resources.Load<Mesh>("DS_HazeMeshProxySphere"); + break; + case LightType.Spot: + if (m_PreviousLightType == LightType.Point) + { + m_ProxyMesh = new Mesh(); + } + else if (m_ProxyMesh != null) + { + m_ProxyMesh.Clear(); + } + CreateProxyMeshCone(m_ProxyMesh); + break; + default: + Debug.LogError("DeepSky::DS_HazeLightVolume: Unsupported light type! " + base.gameObject.name + " will not render volumetrics."); + base.enabled = false; + break; + } + } + + public bool ShadowModeChanged() + { + if (m_Light == null) + { + return false; + } + return m_Light.shadows != m_PreviousShadowMode; + } + + public void UpdateShadowMode() + { + if (m_Light.shadows == LightShadows.None) + { + m_VolumeMaterial.DisableKeyword("SHADOWS_DEPTH"); + m_VolumeMaterial.DisableKeyword("SHADOWS_CUBE"); + } + else if (m_Light.type == LightType.Point) + { + m_VolumeMaterial.EnableKeyword("SHADOWS_CUBE"); + m_VolumeMaterial.DisableKeyword("SHADOWS_DEPTH"); + } + else if (m_Light.type == LightType.Spot) + { + m_VolumeMaterial.EnableKeyword("SHADOWS_DEPTH"); + m_VolumeMaterial.DisableKeyword("SHADOWS_CUBE"); + } + m_PreviousShadowMode = m_Light.shadows; + } + + public void Register() + { + DS_HazeCore instance = DS_HazeCore.Instance; + if (instance == null) + { + Debug.LogError("DeepSky::DS_HazeLightVolume: Attempting to add a light volume but no HS_HazeCore found in scene! Please make sure there is a DS_HazeCore object."); + } + else + { + instance.AddLightVolume(this); + } + } + + public void Deregister() + { + DS_HazeCore instance = DS_HazeCore.Instance; + if (instance != null) + { + instance.RemoveLightVolume(this); + } + } + + public bool WillRender(Vector3 cameraPos) + { + return base.isActiveAndEnabled & (Vector3.Distance(cameraPos, base.transform.position) < m_EndFade); + } + + private void Update() + { + m_DensityOffset -= m_AnimateDirection * m_AnimateSpeed * Time.deltaTime * 0.1f; + } + + private void OnEnable() + { + m_Light = GetComponent<Light>(); + if (m_Light == null) + { + Debug.LogError("DeepSky::DS_HazeLightVolume: No Light component found on " + base.gameObject.name); + base.enabled = false; + } + if (kLightVolumeShader == null) + { + kLightVolumeShader = Resources.Load<Shader>("DS_HazeLightVolume"); + } + if (m_VolumeMaterial == null) + { + m_VolumeMaterial = new Material(kLightVolumeShader); + m_VolumeMaterial.hideFlags = HideFlags.HideAndDontSave; + } + if (m_RenderCmd == null) + { + m_RenderCmd = new CommandBuffer(); + m_RenderCmd.name = base.gameObject.name + "_DS_Haze_RenderLightVolume"; + m_Light.AddCommandBuffer(LightEvent.AfterShadowMap, m_RenderCmd); + } + if (LightTypeChanged()) + { + UpdateLightType(); + } + else if (ProxyMeshRequiresRebuild()) + { + RebuildProxyMesh(); + } + if (ShadowModeChanged()) + { + UpdateShadowMode(); + } + Register(); + } + + private void OnDisable() + { + Deregister(); + } + + private void OnDestroy() + { + if (m_RenderCmd != null) + { + m_RenderCmd.Dispose(); + } + Deregister(); + if (m_ProxyMesh != null && m_Light.type != LightType.Point) + { + UnityEngine.Object.DestroyImmediate(m_ProxyMesh); + } + if (m_VolumeMaterial != null) + { + UnityEngine.Object.DestroyImmediate(m_VolumeMaterial); + } + } + + private int SetShaderPassAndMatrix(Transform cameraTransform, int downSampleFactor, out Matrix4x4 worldMtx) + { + worldMtx = Matrix4x4.TRS(base.transform.position, base.transform.rotation, new Vector3(m_Light.range, m_Light.range, m_Light.range)); + int num = 0; + if (m_Light.type == LightType.Spot) + { + float num2 = Mathf.Cos(m_Light.spotAngle / 2f * ((float)Math.PI / 180f)); + Vector3 normalized = (cameraTransform.position - base.transform.position).normalized; + float num3 = Vector3.Dot(normalized, base.transform.forward); + num = ((num3 > num2) ? 1 : 2); + } + if (downSampleFactor == 4) + { + num += 3; + } + if (m_Falloff == DS_LightFalloff.Quadratic) + { + num += 6; + } + if (m_UseFog) + { + num += 12; + } + return num; + } + + public void FillLightCommandBuffer(RenderTexture radianceTarget, Transform cameraTransform, int downSampleFactor) + { + m_RenderCmd.SetGlobalTexture("_ShadowMapTexture", BuiltinRenderTextureType.CurrentActive); + Matrix4x4 worldMtx; + int shaderPass = SetShaderPassAndMatrix(cameraTransform, downSampleFactor, out worldMtx); + m_RenderCmd.SetRenderTarget(radianceTarget); + m_RenderCmd.DrawMesh(m_ProxyMesh, worldMtx, m_VolumeMaterial, 0, shaderPass); + } + + public void AddLightRenderCommand(Transform cameraTransform, CommandBuffer cmd, int downSampleFactor) + { + Matrix4x4 worldMtx; + int shaderPass = SetShaderPassAndMatrix(cameraTransform, downSampleFactor, out worldMtx); + cmd.DrawMesh(m_ProxyMesh, worldMtx, m_VolumeMaterial, 0, shaderPass); + } + + public void SetupMaterialPerFrame(Matrix4x4 viewProjMtx, Matrix4x4 viewMtx, Transform cameraTransform, float offsetIndex) + { + m_VolumeMaterial.DisableKeyword("SAMPLES_4"); + m_VolumeMaterial.DisableKeyword("SAMPLES_8"); + m_VolumeMaterial.DisableKeyword("SAMPLES_16"); + m_VolumeMaterial.DisableKeyword("SAMPLES_32"); + switch (m_Samples) + { + case DS_SamplingQuality.x4: + m_VolumeMaterial.EnableKeyword("SAMPLES_4"); + break; + case DS_SamplingQuality.x8: + m_VolumeMaterial.EnableKeyword("SAMPLES_8"); + break; + case DS_SamplingQuality.x16: + m_VolumeMaterial.EnableKeyword("SAMPLES_16"); + break; + case DS_SamplingQuality.x32: + m_VolumeMaterial.EnableKeyword("SAMPLES_32"); + break; + default: + m_VolumeMaterial.EnableKeyword("SAMPLES_16"); + break; + } + float num = 1f - Mathf.Clamp01((Vector3.Distance(cameraTransform.position, base.transform.position) - m_StartFade) / (m_EndFade - m_StartFade)); + m_VolumeMaterial.SetVector("_DS_HazeSamplingParams", new Vector4(offsetIndex, 0f, m_DensityTextureContrast, 0f)); + m_VolumeMaterial.SetVector("_DS_HazeCameraDirection", new Vector4(cameraTransform.forward.x, cameraTransform.forward.y, cameraTransform.forward.z, 1f)); + m_VolumeMaterial.SetColor("_DS_HazeLightVolumeColour", m_Light.color.linear * m_Light.intensity * num); + m_VolumeMaterial.SetVector("_DS_HazeLightVolumeScattering", new Vector4(m_Scattering, m_SecondaryScattering, m_ScatteringDirection, Mathf.Clamp01(1f - m_SecondaryScattering))); + m_VolumeMaterial.SetVector("_DS_HazeLightVolumeParams0", new Vector4(base.transform.position.x, base.transform.position.y, base.transform.position.z, m_Light.range)); + Matrix4x4 matrix4x = Matrix4x4.TRS(base.transform.position, base.transform.rotation, new Vector3(m_Light.range, m_Light.range, m_Light.range)); + m_VolumeMaterial.SetMatrix("_WorldViewProj", viewProjMtx * matrix4x); + m_VolumeMaterial.SetMatrix("_WorldView", viewMtx * matrix4x); + if ((bool)m_DensityTexture) + { + m_VolumeMaterial.EnableKeyword("DENSITY_TEXTURE"); + m_VolumeMaterial.SetTexture("_DensityTexture", m_DensityTexture); + m_VolumeMaterial.SetVector("_DS_HazeDensityParams", new Vector4(m_DensityOffset.x, m_DensityOffset.y, m_DensityOffset.z, m_DensityTextureScale * 0.01f)); + } + else + { + m_VolumeMaterial.DisableKeyword("DENSITY_TEXTURE"); + } + bool flag = m_Light.shadows != LightShadows.None; + if (m_Light.type == LightType.Point) + { + m_VolumeMaterial.DisableKeyword("SPOT_COOKIE"); + m_VolumeMaterial.DisableKeyword("SHADOWS_DEPTH"); + if (flag) + { + m_VolumeMaterial.EnableKeyword("SHADOWS_CUBE"); + } + else + { + m_VolumeMaterial.DisableKeyword("SHADOWS_CUBE"); + } + if ((bool)m_Light.cookie) + { + m_VolumeMaterial.EnableKeyword("POINT_COOKIE"); + m_VolumeMaterial.SetMatrix("_DS_Haze_WorldToCookie", base.transform.worldToLocalMatrix); + m_VolumeMaterial.SetTexture("_LightTexture0", m_Light.cookie); + } + else + { + m_VolumeMaterial.DisableKeyword("POINT_COOKIE"); + } + } + else if (m_Light.type == LightType.Spot) + { + m_VolumeMaterial.DisableKeyword("POINT_COOKIE"); + m_VolumeMaterial.DisableKeyword("SHADOWS_CUBE"); + if (flag) + { + m_VolumeMaterial.EnableKeyword("SHADOWS_DEPTH"); + Matrix4x4 inverse = Matrix4x4.TRS(base.transform.position, base.transform.rotation, Vector3.one).inverse; + Matrix4x4 matrix4x2 = Matrix4x4.TRS(new Vector3(0.5f, 0.5f, 0.5f), Quaternion.identity, new Vector3(0.5f, 0.5f, 0.5f)); + Matrix4x4 matrix4x3 = Matrix4x4.Perspective(m_Light.spotAngle, 1f, m_Light.range, m_Light.shadowNearPlane); + Matrix4x4 value = matrix4x2 * matrix4x3; + value[0, 2] *= -1f; + value[1, 2] *= -1f; + value[2, 2] *= -1f; + value[3, 2] *= -1f; + value *= inverse; + m_VolumeMaterial.SetMatrix("_DS_Haze_WorldToShadow", value); + } + else + { + m_VolumeMaterial.DisableKeyword("SHADOWS_DEPTH"); + } + float num2 = Mathf.Cos(m_Light.spotAngle / 2f * ((float)Math.PI / 180f)); + Vector3 lhs = base.transform.position + base.transform.forward * m_Light.range; + float z = 0f - Vector3.Dot(lhs, base.transform.forward); + m_VolumeMaterial.SetVector("_DS_HazeLightVolumeParams1", new Vector4(base.transform.forward.x, base.transform.forward.y, base.transform.forward.z, 1f)); + m_VolumeMaterial.SetVector("_DS_HazeLightVolumeParams2", new Vector4(num2, 1f / num2, z, 0f)); + if ((bool)m_Light.cookie) + { + m_VolumeMaterial.EnableKeyword("SPOT_COOKIE"); + Matrix4x4 inverse2 = Matrix4x4.TRS(base.transform.position, base.transform.rotation, Vector3.one).inverse; + Matrix4x4 matrix4x4 = Matrix4x4.TRS(new Vector3(0.5f, 0.5f, 0f), Quaternion.identity, new Vector3(-0.5f, -0.5f, 1f)); + Matrix4x4 matrix4x5 = Matrix4x4.Perspective(m_Light.spotAngle, 1f, 0f, 1f); + m_VolumeMaterial.SetMatrix("_DS_Haze_WorldToCookie", matrix4x4 * matrix4x5 * inverse2); + m_VolumeMaterial.SetTexture("_LightTexture0", m_Light.cookie); + } + else + { + m_VolumeMaterial.DisableKeyword("SPOT_COOKIE"); + } + } + } +} |