//------------------------------------------------------------------------------------------------------------------ // Volumetric Fog & Mist 2 // Created by Kronnect //------------------------------------------------------------------------------------------------------------------ using UnityEngine; using UnityEngine.Rendering.Universal; namespace VolumetricFogAndMist2 { public partial class VolumetricFog : MonoBehaviour { const string SURFACE_CAM_NAME = "SurfaceCam"; public enum HeightmapCaptureResolution { _64 = 64, _128 = 128, _256 = 256, _512 = 512, _1024 = 1024 } RenderTexture rt; Camera cam; int camStartFrameCount; Matrix4x4 camMatrix; Vector3 lastCamPos; void DisposeSurfaceCapture() { DisableSurfaceCapture(); if (rt != null) { rt.Release(); DestroyImmediate(rt); } } void CheckSurfaceCapture() { if (cam == null) { Transform childCam = transform.Find(SURFACE_CAM_NAME); if (childCam != null) { cam = childCam.GetComponent(); if (cam == null) { DestroyImmediate(childCam.gameObject); } } } } void DisableSurfaceCapture() { if (cam != null) { cam.enabled = false; } } void SurfaceCaptureSupportCheck() { Transform childCam = transform.Find(SURFACE_CAM_NAME); if (childCam != null) { cam = childCam.GetComponent(); } if (!activeProfile.terrainFit) { DisposeSurfaceCapture(); return; } if (cam == null) { if (childCam != null) { DestroyImmediate(childCam.gameObject); } if (cam == null) { GameObject camObj = new GameObject(SURFACE_CAM_NAME, typeof(Camera)); camObj.transform.SetParent(transform, false); cam = camObj.GetComponent(); cam.depthTextureMode = DepthTextureMode.None; cam.clearFlags = CameraClearFlags.Depth; cam.allowHDR = false; cam.allowMSAA = false; } cam.stereoTargetEye = StereoTargetEyeMask.None; } UniversalAdditionalCameraData camData = cam.GetComponent(); if (camData != null) { camData.dithering = false; camData.renderPostProcessing = false; camData.renderShadows = false; camData.requiresColorTexture = false; camData.requiresDepthTexture = false; camData.stopNaN = false; } cam.orthographic = true; cam.nearClipPlane = 1f; if (rt != null && rt.width != (int)activeProfile.terrainFitResolution) { if (cam.targetTexture == rt) { cam.targetTexture = null; } rt.Release(); DestroyImmediate(rt); } if (rt == null) { rt = new RenderTexture((int)activeProfile.terrainFitResolution, (int)activeProfile.terrainFitResolution, 24, RenderTextureFormat.Depth); rt.antiAliasing = 1; } int thisLayer = 1 << gameObject.layer; if ((activeProfile.terrainLayerMask & thisLayer) != 0) { activeProfile.terrainLayerMask &= ~thisLayer; // exclude fog layer } cam.cullingMask = activeProfile.terrainLayerMask; cam.targetTexture = rt; if (activeProfile.terrainFit) { ScheduleShadowCapture(); } else { cam.enabled = false; } } /// /// Updates shadows on this volumetric fog /// public void ScheduleShadowCapture() { if (cam != null) { cam.enabled = true; camStartFrameCount = Time.frameCount; if (!fogMat.IsKeywordEnabled(ShaderParams.SKW_SURFACE)) { fogMat.EnableKeyword(ShaderParams.SKW_SURFACE); } } } void SetupCameraCaptureMatrix() { Vector3 camPos = transform.position + new Vector3(0, transform.lossyScale.y * 0.51f, 0); cam.farClipPlane = 10000; cam.transform.position = camPos; cam.transform.eulerAngles = new Vector3(90, 0, 0); Vector3 size = transform.lossyScale; cam.orthographicSize = Mathf.Max(size.x * 0.5f, size.z * 0.5f); ComputeSufaceTransform(cam.projectionMatrix, cam.worldToCameraMatrix); fogMat.SetMatrix(ShaderParams.SurfaceCaptureMatrix, camMatrix); fogMat.SetTexture(ShaderParams.SurfaceDepthTexture, cam.targetTexture); fogMat.SetVector(ShaderParams.SurfaceData, new Vector4(camPos.y, activeProfile.terrainFogHeight, activeProfile.terrainFogMinAltitude, activeProfile.terrainFogMaxAltitude)); } void SurfaceCaptureUpdate() { if (!activeProfile.terrainFit) return; if (cam == null) return; SetupCameraCaptureMatrix(); if (!cam.enabled && lastCamPos != cam.transform.position) { lastCamPos = cam.transform.position; ScheduleShadowCapture(); } else if (Time.frameCount > camStartFrameCount + 1 && Application.isPlaying) { cam.enabled = false; } } static Matrix4x4 identityMatrix = Matrix4x4.identity; void ComputeSufaceTransform(Matrix4x4 proj, Matrix4x4 view) { // Currently CullResults ComputeDirectionalShadowMatricesAndCullingPrimitives doesn't // apply z reversal to projection matrix. We need to do it manually here. if (SystemInfo.usesReversedZBuffer) { proj.m20 = -proj.m20; proj.m21 = -proj.m21; proj.m22 = -proj.m22; proj.m23 = -proj.m23; } Matrix4x4 worldToShadow = proj * view; var textureScaleAndBias = identityMatrix; textureScaleAndBias.m00 = 0.5f; textureScaleAndBias.m11 = 0.5f; textureScaleAndBias.m22 = 0.5f; textureScaleAndBias.m03 = 0.5f; textureScaleAndBias.m23 = 0.5f; textureScaleAndBias.m13 = 0.5f; // Apply texture scale and offset to save a MAD in shader. camMatrix = textureScaleAndBias * worldToShadow; } } }