2025-06-08 00:39:11 +09:00

396 lines
17 KiB
C#

//------------------------------------------------------------------------------------------------------------------
// 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<DynamicFogSubVolume> 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<Texture2D>("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<MeshFilter>();
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<Renderer>();
}
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<DynamicFogProfile>();
}
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);
}
}
}
}