//------------------------------------------------------------------------------------------------------------------ // Dynamic Fog & Mist 2 // Created by Kronnect //------------------------------------------------------------------------------------------------------------------ using UnityEngine; using System.Collections.Generic; namespace DynamicFogAndMist2 { [ExecuteInEditMode] public partial class DynamicFog : MonoBehaviour { public DynamicFogProfile profile; [Tooltip("Fades in/out fog effect or blends with sub-volumes when reference object enters the fog volume.")] public bool enableFade; public float fadeDistance = 1; [Tooltip("The controller (player or camera) to check if enters the fog volume.")] public Transform fadeController; [Tooltip("Enable sub-volume blending.")] public bool enableSubVolumes; [Tooltip("Allowed subVolumes. If no subvolume is specified, any subvolume entered by this controller will affect this fog volume.")] public List subVolumes; [Tooltip("Shows the fog volume boundary in Game View")] public bool showBoundary; [Tooltip("Allows rotation of the fog volume")] public bool allowRotation; static class ShaderParams { public static int MainTex = Shader.PropertyToID("_MainTex"); public static int SunDir = Shader.PropertyToID("_SunDir"); public static int LightColor = Shader.PropertyToID("_LightColor"); public static int GradientTex = Shader.PropertyToID("_GradientTex"); public static int Dithering = Shader.PropertyToID("_DitherStrength"); public static int WindDirection = Shader.PropertyToID("_WindDirection"); public static int Density = Shader.PropertyToID("_Density"); public static int LightDiffusionPower = Shader.PropertyToID("_LightDiffusionPower"); public static int LightDiffusionIntensity = Shader.PropertyToID("_LightDiffusionIntensity"); public static int Color = Shader.PropertyToID("_Color"); public static int SecondColor = Shader.PropertyToID("_SecondColor"); public static int BoundsCenter = Shader.PropertyToID("_BoundsCenter"); public static int BoundsExtents = Shader.PropertyToID("_BoundsExtents"); public static int Geom = Shader.PropertyToID("_Geom"); public static int NoiseData = Shader.PropertyToID("_NoiseData"); public static int FogOfWarTex = Shader.PropertyToID("_FogOfWarTex"); public static int FogOfWarCenter = Shader.PropertyToID("_FogOfWarCenter"); public static int FogOfWarCenterAdjusted = Shader.PropertyToID("_FogOfWarCenterAdjusted"); public static int FogOfWarSize = Shader.PropertyToID("_FogOfWarSize"); public static int TurbulenceAmount = Shader.PropertyToID("_TurbulenceAmount"); public const string SKW_FOW = "DF2_FOW"; public const string SKW_BOX_PROJECTION = "DF2_BOX_PROJECTION"; public const string SKW_DEPTH_CLIP = "DF2_DEPTH_CLIP"; public const string SKW_3D_DISTANCE = "DF2_3D_DISTANCE"; public const string SKW_LIGHT_DIFFUSION = "DF2_LIGHT_DIFFUSION"; public const string SKW_VERTICAL_GRADIENT = "DF2_VERTICAL_GRADIENT"; } Renderer r; Material fogMat, turbulenceMat; Material fogMat2D, turbulenceMat2D; RenderTexture rtTurbulence; float turbAcum; float windAcum; Vector3 sunDir; float dayLight; static Texture2D noiseTex; Texture2D gradientTex; Mesh debugMesh; Material fogDebugMat; DynamicFogProfile activeProfile, lerpProfile; Vector3 lastControllerPosition; float alphaMultiplier = 1f; public static bool requireSubvolumeUpdate; void OnEnable() { if (noiseTex == null) { noiseTex = Resources.Load("DynamicFog/Textures/NoiseTex256"); } FogOfWarInit(); UpdateMaterialProperties(); } private void OnDisable() { if (profile != null) { profile.onSettingsChanged -= UpdateMaterialProperties; } } private void OnValidate() { UpdateMaterialProperties(); } private void OnDestroy() { if (rtTurbulence != null) { rtTurbulence.Release(); } if (fogMat != null) { DestroyImmediate(fogMat); fogMat = null; } FogOfWarDestroy(); } void OnDrawGizmosSelected() { Gizmos.color = new Color(1, 1, 0, 0.75F); Gizmos.matrix = transform.localToWorldMatrix; Gizmos.DrawWireCube(Vector3.zero, Vector3.one); } void LateUpdate() { if (!allowRotation) { transform.rotation = Quaternion.identity; } if (fogMat == null || r == null || profile == null) return; ComputeActiveProfile(); DynamicFogManager globalManager = DynamicFogManager.instance; Light sun = globalManager.sun; if (sun != null) { sunDir = -sun.transform.forward; fogMat.SetVector(ShaderParams.SunDir, sunDir); dayLight = 1f + sunDir.y * 2f; if (dayLight < 0) dayLight = 0; else if (dayLight > 1f) dayLight = 1f; if (activeProfile.useSunColor) { Color lightColor = sun.color * (sun.intensity * dayLight * activeProfile.brightness); fogMat.SetVector(ShaderParams.LightColor, lightColor); } else { fogMat.SetVector(ShaderParams.LightColor, Color.white); } } windAcum += Time.deltaTime * activeProfile.speed; windAcum %= 10000; fogMat.SetVector(ShaderParams.WindDirection, activeProfile.direction * windAcum); Bounds bounds = r.bounds; fogMat.SetVector(ShaderParams.BoundsCenter, bounds.center); fogMat.SetVector(ShaderParams.BoundsExtents, bounds.extents); UpdateNoise(); if (enableFade || enableSubVolumes) { ApplyProfileSettings(); } if (enableFogOfWar) { UpdateFogOfWar(); } if (showBoundary) { if (fogDebugMat == null) { fogDebugMat = new Material(Shader.Find("Hidden/DynamicFog2/DynamicFogDebug")); } if (debugMesh == null) { MeshFilter mf = GetComponent(); if (mf != null) { debugMesh = mf.sharedMesh; } } Matrix4x4 m = Matrix4x4.TRS(transform.position, transform.rotation, transform.lossyScale); Graphics.DrawMesh(debugMesh, m, fogDebugMat, 0); } } void UpdateNoise() { if (activeProfile == null || noiseTex == null) return; if (rtTurbulence == null || rtTurbulence.width != noiseTex.width) { RenderTextureDescriptor desc = new RenderTextureDescriptor(noiseTex.width, noiseTex.height, RenderTextureFormat.ARGB32, 0); rtTurbulence = new RenderTexture(desc); rtTurbulence.wrapMode = TextureWrapMode.Repeat; } turbAcum += Time.deltaTime * activeProfile.speed; turbAcum %= 10000; turbulenceMat.SetFloat(ShaderParams.TurbulenceAmount, turbAcum); Graphics.Blit(noiseTex, rtTurbulence, turbulenceMat); fogMat.SetTexture(ShaderParams.MainTex, rtTurbulence); } public void UpdateMaterialProperties() { if (this == null || gameObject == null || !gameObject.activeInHierarchy) return; fadeDistance = Mathf.Max(0.1f, fadeDistance); if (r == null) { r = GetComponent(); } if (profile == null) { if (fogMat == null && r != null) { fogMat = new Material(Shader.Find("DynamicFog2/Empty")); fogMat.hideFlags = HideFlags.DontSave; r.sharedMaterial = fogMat; } return; } profile.onSettingsChanged -= UpdateMaterialProperties; profile.onSettingsChanged += UpdateMaterialProperties; // Subscribe to sub-volume profile changes if (subVolumes != null) { foreach (DynamicFogSubVolume subVol in subVolumes) { if (subVol != null && subVol.profile != null) { subVol.profile.onSettingsChanged -= UpdateMaterialProperties; subVol.profile.onSettingsChanged += UpdateMaterialProperties; } } } if (fogMat2D == null) { fogMat2D = new Material(Shader.Find("DynamicFog2/DynamicFog2DURP")); fogMat2D.hideFlags = HideFlags.DontSave; } fogMat = fogMat2D; if (turbulenceMat2D == null) { turbulenceMat2D = new Material(Shader.Find("DynamicFog2/Turbulence2D")); } turbulenceMat = turbulenceMat2D; if (r != null) { r.sharedMaterial = fogMat; } if (fogMat == null || profile == null) return; profile.ValidateSettings(); lastControllerPosition.x = float.MaxValue; activeProfile = profile; ComputeActiveProfile(); ApplyProfileSettings(); } void ComputeActiveProfile() { if (maskEditorEnabled) alphaMultiplier = 0.85f; if (Application.isPlaying) { if (enableFade || enableSubVolumes) { if (fadeController == null) { Camera cam = Camera.main; if (cam != null) { fadeController = Camera.main.transform; } } if (fadeController != null && (requireSubvolumeUpdate || lastControllerPosition != fadeController.position)) { requireSubvolumeUpdate = false; lastControllerPosition = fadeController.position; activeProfile = profile; alphaMultiplier = 1f; // Self volume if (enableFade) { float t = ComputeVolumeFade(transform, fadeDistance); alphaMultiplier *= t; } // Check sub-volumes if (enableSubVolumes) { int subVolumeCount = DynamicFogSubVolume.subVolumes.Count; int allowedSubVolumesCount = subVolumes != null ? subVolumes.Count : 0; for (int k = 0; k < subVolumeCount; k++) { DynamicFogSubVolume subVolume = DynamicFogSubVolume.subVolumes[k]; if (subVolume == null || subVolume.profile == null) continue; if (allowedSubVolumesCount > 0 && !subVolumes.Contains(subVolume)) continue; float t = ComputeVolumeFade(subVolume.transform, subVolume.fadeDistance); if (t > 0) { if (lerpProfile == null) { lerpProfile = ScriptableObject.CreateInstance(); } lerpProfile.Lerp(activeProfile, subVolume.profile, t); activeProfile = lerpProfile; } } } } } } if (activeProfile == null) { activeProfile = profile; } Color fogAlbedo = activeProfile.tintColor; Color secondColor = activeProfile.noiseColor; fogAlbedo.a *= alphaMultiplier; secondColor.a *= alphaMultiplier; fogMat.SetColor(ShaderParams.Color, fogAlbedo); fogMat.SetColor(ShaderParams.SecondColor, secondColor); } float ComputeVolumeFade(Transform transform, float fadeDistance) { Vector3 diff = transform.position - fadeController.position; diff.x = diff.x < 0 ? -diff.x : diff.x; diff.y = diff.y < 0 ? -diff.y : diff.y; diff.z = diff.z < 0 ? -diff.z : diff.z; Vector3 extents = transform.lossyScale * 0.5f; Vector3 gap = extents - diff; float minDiff = gap.x < gap.y ? gap.x : gap.y; minDiff = minDiff < gap.z ? minDiff : gap.z; fadeDistance += 0.0001f; float t = Mathf.Clamp01(minDiff / fadeDistance); return t; } void ApplyProfileSettings() { if (activeProfile == null) return; r.sortingLayerID = activeProfile.sortingLayerID; r.sortingOrder = activeProfile.sortingOrder; // update gradient texture int w = DynamicFogProfile.GRADIENT_TEXTURE_WIDTH; if (gradientTex == null) { gradientTex = new Texture2D(w, 1, SystemInfo.SupportsTextureFormat(TextureFormat.RGBAHalf) ? TextureFormat.RGBAHalf : TextureFormat.RGBA32, false); gradientTex.wrapMode = TextureWrapMode.Clamp; } if (activeProfile.gradientColors == null || activeProfile.gradientColors.Length != w) { activeProfile.ValidateSettings(); } if (activeProfile.gradientColors.Length == w) { gradientTex.SetPixels(activeProfile.gradientColors); } gradientTex.Apply(); fogMat.SetTexture(ShaderParams.GradientTex, gradientTex); fogMat.renderQueue = activeProfile.renderQueue; fogMat.SetVector(ShaderParams.Geom, new Vector4(activeProfile.baseAltitude, 0.001f + activeProfile.maxHeight, activeProfile.distanceMax, activeProfile.noiseDistanceAtten)); fogMat.SetVector(ShaderParams.NoiseData, new Vector4(1f / (1f + activeProfile.scale * 200f), activeProfile.turbulence, activeProfile.shift, activeProfile.noiseColorBlend)); fogMat.SetFloat(ShaderParams.LightDiffusionPower, activeProfile.lightDiffusionPower); fogMat.SetFloat(ShaderParams.LightDiffusionIntensity, activeProfile.lightDiffusionIntensity); fogMat.SetVector(ShaderParams.Density, new Vector3(1.001f - activeProfile.densityExponential, activeProfile.densityLinear)); fogMat.SetFloat(ShaderParams.Dithering, activeProfile.dithering * 0.01f); if (activeProfile.boxProjection) { fogMat.EnableKeyword(ShaderParams.SKW_BOX_PROJECTION); } else { fogMat.DisableKeyword(ShaderParams.SKW_BOX_PROJECTION); } if (activeProfile.depthClip) { fogMat.EnableKeyword(ShaderParams.SKW_DEPTH_CLIP); } else { fogMat.DisableKeyword(ShaderParams.SKW_DEPTH_CLIP); } if (activeProfile.useXYZDistance) { fogMat.EnableKeyword(ShaderParams.SKW_3D_DISTANCE); } else { fogMat.DisableKeyword(ShaderParams.SKW_3D_DISTANCE); } if (activeProfile.useVerticalGradient) { fogMat.EnableKeyword(ShaderParams.SKW_VERTICAL_GRADIENT); } else { fogMat.DisableKeyword(ShaderParams.SKW_VERTICAL_GRADIENT); } if (activeProfile.lightDiffusionIntensity > 0) { fogMat.EnableKeyword(ShaderParams.SKW_LIGHT_DIFFUSION); } else { fogMat.DisableKeyword(ShaderParams.SKW_LIGHT_DIFFUSION); } if (enableFogOfWar) { fogMat.SetTexture(ShaderParams.FogOfWarTex, fogOfWarTexture); fogMat.SetVector(ShaderParams.FogOfWarCenter, fogOfWarCenter); fogMat.SetVector(ShaderParams.FogOfWarSize, fogOfWarSize); Vector3 ca = fogOfWarCenter - 0.5f * fogOfWarSize; fogMat.SetVector(ShaderParams.FogOfWarCenterAdjusted, new Vector3(ca.x / (fogOfWarSize.x + 0.0001f), 1f, ca.z / (fogOfWarSize.z + 0.0001f))); fogMat.EnableKeyword(ShaderParams.SKW_FOW); } else { fogMat.DisableKeyword(ShaderParams.SKW_FOW); } } } }