VolumetricFogProfile.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace VolumetricFogAndMist2 {
  6. public delegate void OnSettingsChanged();
  7. [CreateAssetMenu(menuName = "Volumetric Fog \x8B& Mist/Fog Profile", fileName = "VolumetricFogProfile", order = 1001)]
  8. public class VolumetricFogProfile : ScriptableObject {
  9. [Header("Rendering")]
  10. [Range(1, 16)] public int raymarchQuality = 6;
  11. [Tooltip("Determines the minimum step size. Increase to improve performance / decrease to improve accuracy. When increasing this value, you can also increase 'Jittering' amount to improve quality.")]
  12. public float raymarchMinStep = 0.1f;
  13. public float jittering = 0.5f;
  14. [Range(0, 2)] public float dithering = 1f;
  15. [Tooltip("The render queue for this renderer. By default, all transparent objects use a render queue of 3000. Use a lower value to render before all transparent objects.")]
  16. public int renderQueue = 3100;
  17. [Tooltip("Optional sorting layer Id (number) for this renderer. By default 0. Usually used to control the order with other transparent renderers, like Sprite Renderer.")]
  18. public int sortingLayerID;
  19. [Tooltip("Optional sorting order for this renderer. Used to control the order with other transparent renderers, like Sprite Renderer.")]
  20. public int sortingOrder;
  21. [Header("Density")]
  22. [Tooltip("Do not use any noise at all")]
  23. public bool constantDensity;
  24. public Texture2D noiseTexture;
  25. [Range(0, 3)] public float noiseStrength = 1f;
  26. public float noiseScale = 15f;
  27. public float noiseFinalMultiplier = 1f;
  28. public bool useDetailNoise;
  29. public Texture3D detailTexture;
  30. public float detailScale = 0.35f;
  31. [Range(0, 1f)] public float detailStrength = 0.5f;
  32. public float detailOffset = -0.5f;
  33. public float density = 1f;
  34. [Header("Geometry")]
  35. public VolumetricFogShape shape = VolumetricFogShape.Box;
  36. [Range(0, 1f)] public float border = 0.05f;
  37. [Tooltip("Ignores volume height and use a custom height defined by this profile")]
  38. public bool customHeight;
  39. public float height;
  40. public float verticalOffset;
  41. [Tooltip("When enabled, makes fog appear at certain distance from a camera")]
  42. public float distance;
  43. [Range(0, 1)] public float distanceFallOff = 0.93f;
  44. [Tooltip("Maximum distance from camera")]
  45. public float maxDistance = 10000;
  46. [Range(0, 1)]
  47. public float maxDistanceFallOff;
  48. [Tooltip("Fits the fog altitude to the terrain heightmap")]
  49. public bool terrainFit;
  50. public VolumetricFog.HeightmapCaptureResolution terrainFitResolution = VolumetricFog.HeightmapCaptureResolution._128;
  51. [Tooltip("Which objects will be included in the heightmap capture. By default all objects are included but you may want to restrict this to just the terrain.")]
  52. public LayerMask terrainLayerMask = -1;
  53. [Tooltip("The height of fog above terrain surface.")]
  54. public float terrainFogHeight = 25f;
  55. public float terrainFogMinAltitude;
  56. public float terrainFogMaxAltitude = 150f;
  57. [Header("Colors")]
  58. [ColorUsage(showAlpha: false)]
  59. public Color albedo = new Color32(227, 227, 227, 255);
  60. public bool enableDepthGradient;
  61. [GradientUsage(hdr: true, ColorSpace.Linear)] public Gradient depthGradient;
  62. public float depthGradientMaxDistance = 1000f;
  63. public bool enableHeightGradient;
  64. [GradientUsage(hdr: true, ColorSpace.Linear)] public Gradient heightGradient;
  65. public float brightness = 1f;
  66. [Range(0, 2)] public float deepObscurance = 1f;
  67. public Color specularColor = new Color(1, 1, 0.8f, 1);
  68. [Range(0, 1f)] public float specularThreshold = 0.637f;
  69. [Range(0, 1f)] public float specularIntensity = 0.428f;
  70. [Header("Animation")]
  71. public float turbulence = 0.73f;
  72. public Vector3 windDirection = new Vector3(0.02f, 0, 0);
  73. public bool useCustomDetailNoiseWindDirection;
  74. public Vector3 detailNoiseWindDirection = new Vector3(0.02f, 0, 0);
  75. [Header("Directional Light")]
  76. [Tooltip("Enable to synchronize fog light intensity and color with the Sun and the Moon (must be assigned into Volumetric Fog Manager)")]
  77. public bool dayNightCycle = true;
  78. [Tooltip("When day/night cycle option is disabled, customize the direction of the Sun light.")]
  79. public Vector3 sunDirection = Vector3.up;
  80. [Tooltip("Custom sun color when day/night cycle is disabled")]
  81. public Color sunColor = new Color(0, 0.9568f, 0.8392f);
  82. [Tooltip("Custom sun intensity when day/night cycle is disabled")]
  83. public float sunIntensity = 1f;
  84. [Tooltip("Ambient light influence")]
  85. public float ambientLightMultiplier;
  86. [Range(0, 64)] public float lightDiffusionPower = 32;
  87. [Range(0, 1)] public float lightDiffusionIntensity = 0.4f;
  88. public bool receiveShadows;
  89. [Range(0, 1)] public float shadowIntensity = 0.5f;
  90. [Tooltip("Removes shadowed fog")]
  91. [Range(0, 1)] public float shadowCancellation;
  92. public float shadowMaxDistance = 250f;
  93. [Tooltip("Uses the directional light cookie")]
  94. public bool cookie;
  95. [Header("Distant Fog")]
  96. [Tooltip("Enables exponential distant fog. Use this option to cover horizon/sky/far distances with optimal performance")]
  97. public bool distantFog;
  98. public float distantFogStartDistance = 1000f;
  99. public float distantFogDistanceDensity = 0.5f;
  100. public float distantFogMaxHeight = 4000;
  101. public float distantFogHeightDensity = 0.5f;
  102. public Color distantFogColor = new Color(0.358f, 0.358f, 0.358f);
  103. public float distantFogDiffusionIntensity = 0.4f;
  104. public int distantFogRenderQueue = 2999;
  105. public event OnSettingsChanged onSettingsChanged;
  106. [NonSerialized]
  107. public Texture2D depthGradientTex;
  108. [NonSerialized]
  109. public Texture2D heightGradientTex;
  110. Color[] depthGradientColors;
  111. Color[] heightGradientColors;
  112. private void OnEnable() {
  113. if (noiseTexture == null) {
  114. noiseTexture = Resources.Load<Texture2D>("Textures/NoiseTex256");
  115. }
  116. if (detailTexture == null) {
  117. detailTexture = Resources.Load<Texture3D>("Textures/NoiseTex3D");
  118. }
  119. ValidateSettings();
  120. }
  121. private void OnValidate() {
  122. ValidateSettings();
  123. if (onSettingsChanged != null) {
  124. #if UNITY_EDITOR
  125. UnityEditor.EditorApplication.delayCall += () => {
  126. try {
  127. onSettingsChanged();
  128. UnityEditor.EditorApplication.delayCall += () => UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
  129. } catch { }
  130. };
  131. #else
  132. onSettingsChanged();
  133. #endif
  134. }
  135. }
  136. public void ValidateSettings() {
  137. distance = Mathf.Max(0, distance);
  138. density = Mathf.Max(0, density);
  139. noiseScale = Mathf.Max(0.1f, noiseScale);
  140. noiseFinalMultiplier = Mathf.Max(0, noiseFinalMultiplier);
  141. detailScale = Mathf.Max(0.01f, detailScale);
  142. raymarchMinStep = Mathf.Max(0.1f, raymarchMinStep);
  143. jittering = Mathf.Max(0, jittering);
  144. terrainFogHeight = Mathf.Max(0, terrainFogHeight);
  145. height = Mathf.Max(0, height);
  146. if (depthGradient == null) {
  147. depthGradient = new Gradient();
  148. depthGradient.colorKeys = new GradientColorKey[] {
  149. new GradientColorKey(Color.white, 0),
  150. new GradientColorKey(Color.white, 1)
  151. };
  152. }
  153. maxDistance = Mathf.Max(0.0001f, maxDistance);
  154. depthGradientMaxDistance = Mathf.Max(0, depthGradientMaxDistance);
  155. ambientLightMultiplier = Mathf.Max(0, ambientLightMultiplier);
  156. sunIntensity = Mathf.Max(0, sunIntensity);
  157. shadowMaxDistance = Mathf.Max(0, shadowMaxDistance);
  158. distantFogStartDistance = Mathf.Max(0, distantFogStartDistance);
  159. distantFogDistanceDensity = Mathf.Max(0, distantFogDistanceDensity);
  160. distantFogMaxHeight = Mathf.Max(0, distantFogMaxHeight);
  161. distantFogHeightDensity = Mathf.Max(0, distantFogHeightDensity);
  162. distantFogDiffusionIntensity = Mathf.Max(0, distantFogDiffusionIntensity);
  163. if (enableDepthGradient) {
  164. const int DEPTH_GRADIENT_TEX_SIZE = 32;
  165. bool requiresUpdate = false;
  166. if (depthGradientTex == null) {
  167. depthGradientTex = new Texture2D(DEPTH_GRADIENT_TEX_SIZE, 1, TextureFormat.RGBA32, mipChain: false, linear: true);
  168. depthGradientTex.wrapMode = TextureWrapMode.Clamp;
  169. requiresUpdate = true;
  170. }
  171. if (depthGradientColors == null || depthGradientColors.Length != DEPTH_GRADIENT_TEX_SIZE) {
  172. depthGradientColors = new Color[DEPTH_GRADIENT_TEX_SIZE];
  173. requiresUpdate = true;
  174. }
  175. for (int k = 0; k < DEPTH_GRADIENT_TEX_SIZE; k++) {
  176. float t = (float)k / DEPTH_GRADIENT_TEX_SIZE;
  177. Color color = depthGradient.Evaluate(t);
  178. if (color != depthGradientColors[k]) {
  179. depthGradientColors[k] = color;
  180. requiresUpdate = true;
  181. }
  182. }
  183. if (requiresUpdate) {
  184. depthGradientTex.SetPixels(depthGradientColors);
  185. depthGradientTex.Apply();
  186. }
  187. }
  188. if (enableHeightGradient) {
  189. const int HEIGHT_GRADIENT_TEX_SIZE = 32;
  190. bool requiresUpdate = false;
  191. if (heightGradientTex == null) {
  192. heightGradientTex = new Texture2D(HEIGHT_GRADIENT_TEX_SIZE, 1, TextureFormat.RGBA32, mipChain: false, linear: true);
  193. heightGradientTex.wrapMode = TextureWrapMode.Clamp;
  194. requiresUpdate = true;
  195. }
  196. if (heightGradientColors == null || heightGradientColors.Length != HEIGHT_GRADIENT_TEX_SIZE) {
  197. heightGradientColors = new Color[HEIGHT_GRADIENT_TEX_SIZE];
  198. requiresUpdate = true;
  199. }
  200. for (int k = 0; k < HEIGHT_GRADIENT_TEX_SIZE; k++) {
  201. float t = (float)k / HEIGHT_GRADIENT_TEX_SIZE;
  202. Color color = heightGradient.Evaluate(t);
  203. if (color != heightGradientColors[k]) {
  204. heightGradientColors[k] = color;
  205. requiresUpdate = true;
  206. }
  207. }
  208. if (requiresUpdate) {
  209. heightGradientTex.SetPixels(heightGradientColors);
  210. heightGradientTex.Apply();
  211. }
  212. }
  213. }
  214. public void Lerp(VolumetricFogProfile p1, VolumetricFogProfile p2, float t) {
  215. float t0 = 1f - t;
  216. raymarchQuality = (int)(p1.raymarchQuality * t0 + p2.raymarchQuality * t);
  217. raymarchMinStep = p1.raymarchMinStep * t0 + p2.raymarchMinStep * t;
  218. jittering = p1.jittering * t0 + p2.jittering * t;
  219. dithering = p1.dithering * t0 + p2.dithering * t;
  220. renderQueue = t < 0.5f ? p1.renderQueue : p2.renderQueue;
  221. sortingLayerID = t < 0.5f ? p1.sortingLayerID : p2.sortingLayerID;
  222. sortingOrder = t < 0.5f ? p1.sortingOrder : p2.sortingOrder;
  223. noiseStrength = p1.noiseStrength * t0 + p2.noiseStrength * t;
  224. noiseScale = p1.noiseScale * t0 + p2.noiseScale * t;
  225. noiseFinalMultiplier = p1.noiseFinalMultiplier * t0 + p2.noiseFinalMultiplier * t;
  226. noiseTexture = t < 0.5f ? p1.noiseTexture : p2.noiseTexture;
  227. useDetailNoise = t < 0.5f ? p1.useDetailNoise : p2.useDetailNoise;
  228. detailTexture = t < 0.5f ? p1.detailTexture : p2.detailTexture;
  229. detailScale = p1.detailScale * t0 + p2.detailScale * t;
  230. detailStrength = p1.detailStrength * t0 + p2.detailStrength * t;
  231. detailOffset = p1.detailOffset * t0 + p2.detailOffset * t;
  232. density = p1.density * t0 + p2.density * t;
  233. shape = t < 0.5f ? p1.shape : p2.shape;
  234. border = p1.border * t0 + p2.border * t;
  235. verticalOffset = p1.verticalOffset * t0 + p2.verticalOffset * t;
  236. distance = p1.distance * t0 + p2.distance * t;
  237. distanceFallOff = p1.distanceFallOff * t0 + p2.distanceFallOff * t;
  238. albedo = p1.albedo * t0 + p2.albedo * t;
  239. constantDensity = t < 0.5f ? p1.constantDensity : p2.constantDensity;
  240. enableDepthGradient = p1.enableDepthGradient || p2.enableDepthGradient;
  241. LerpGradient(depthGradient, p1.depthGradient, p2.depthGradient, t);
  242. depthGradientMaxDistance = p1.depthGradientMaxDistance * t0 + p2.depthGradientMaxDistance * t;
  243. enableHeightGradient = p1.enableHeightGradient || p2.enableHeightGradient;
  244. LerpGradient(heightGradient, p1.heightGradient, p2.heightGradient, t);
  245. ambientLightMultiplier = p1.ambientLightMultiplier * t0 + p2.ambientLightMultiplier * t;
  246. brightness = p1.brightness * t0 + p2.brightness * t;
  247. deepObscurance = p1.deepObscurance * t0 + p2.deepObscurance * t;
  248. specularColor = p1.specularColor * t0 + p2.specularColor * t;
  249. specularThreshold = p1.specularThreshold * t0 + p2.specularThreshold * t;
  250. specularIntensity = p1.specularIntensity * t0 + p2.specularIntensity * t;
  251. turbulence = p1.turbulence * t0 + p2.turbulence * t;
  252. windDirection = p1.windDirection * t0 + p2.windDirection * t;
  253. useCustomDetailNoiseWindDirection = t < 0.5f ? p1.useCustomDetailNoiseWindDirection : p2.useCustomDetailNoiseWindDirection;
  254. detailNoiseWindDirection = p1.detailNoiseWindDirection * t0 + p2.detailNoiseWindDirection * t;
  255. lightDiffusionPower = p1.lightDiffusionPower * t0 + p2.lightDiffusionPower * t;
  256. lightDiffusionIntensity = p1.lightDiffusionIntensity * t0 + p2.lightDiffusionIntensity * t;
  257. receiveShadows = t < 0.5f ? p1.receiveShadows : p2.receiveShadows;
  258. shadowIntensity = p1.shadowIntensity * t0 + p2.shadowIntensity * t;
  259. shadowCancellation = t < 0.5f ? p1.shadowCancellation : p2.shadowCancellation;
  260. shadowMaxDistance = p1.shadowMaxDistance * t0 + p2.shadowMaxDistance * t;
  261. terrainFit = t < 0.5f ? p1.terrainFit : p2.terrainFit;
  262. terrainFitResolution = t < 0.5 ? p1.terrainFitResolution : p2.terrainFitResolution;
  263. terrainFogHeight = p1.terrainFogHeight * t0 + p2.terrainFogHeight * t;
  264. terrainFogMinAltitude = p1.terrainFogMinAltitude * t0 + p2.terrainFogMinAltitude * t;
  265. terrainFogMaxAltitude = p1.terrainFogMaxAltitude * t0 + p2.terrainFogMaxAltitude * t;
  266. terrainLayerMask = t < 0.5f ? p1.terrainLayerMask : p2.terrainLayerMask;
  267. dayNightCycle = t < 0.5f ? p1.dayNightCycle : p2.dayNightCycle;
  268. sunDirection = Vector3.Slerp(p1.sunDirection, p2.sunDirection, t);
  269. sunColor = p1.sunColor * t0 + p2.sunColor * t;
  270. sunIntensity = p1.sunIntensity * t0 + p2.sunIntensity * t;
  271. ambientLightMultiplier = p1.ambientLightMultiplier * t0 + p2.ambientLightMultiplier * t;
  272. cookie = t < 0.5f ? p1.cookie : p2.cookie;
  273. distantFog = t < 0.5f ? p1.distantFog : p2.distantFog;
  274. distantFogStartDistance = p1.distantFogStartDistance * t0 + p2.distantFogStartDistance * t;
  275. distantFogDistanceDensity = p1.distantFogDistanceDensity * t0 + p2.distantFogDistanceDensity * t;
  276. distantFogMaxHeight = p1.distantFogMaxHeight * t0 + p2.distantFogMaxHeight * t;
  277. distantFogHeightDensity = p1.distantFogHeightDensity * t0 + p2.distantFogHeightDensity * t;
  278. distantFogColor = p1.distantFogColor * t0 + p2.distantFogColor * t;
  279. distantFogDiffusionIntensity = p1.distantFogDiffusionIntensity * t0 + p2.distantFogDiffusionIntensity * t;
  280. ValidateSettings();
  281. }
  282. readonly static List<float> colorKeysTimes = new List<float>();
  283. readonly static List<float> alphaKeysTimes = new List<float>();
  284. void LerpGradient(Gradient g, Gradient a, Gradient b, float t) {
  285. if (a.colorKeys.Length + b.colorKeys.Length > 8 || a.alphaKeys.Length + b.alphaKeys.Length > 8) {
  286. Debug.LogError("Gradients total key count exceeding 8, can not lerp");
  287. return;
  288. }
  289. colorKeysTimes.Clear();
  290. if (a.colorKeys != null) {
  291. for (int i = 0; i < a.colorKeys.Length; i++) {
  292. float k = a.colorKeys[i].time;
  293. if (!colorKeysTimes.Contains(k))
  294. colorKeysTimes.Add(k);
  295. }
  296. }
  297. if (b.colorKeys != null) {
  298. for (int i = 0; i < b.colorKeys.Length; i++) {
  299. float k = b.colorKeys[i].time;
  300. if (!colorKeysTimes.Contains(k))
  301. colorKeysTimes.Add(k);
  302. }
  303. }
  304. alphaKeysTimes.Clear();
  305. if (a.alphaKeys != null) {
  306. for (int i = 0; i < a.alphaKeys.Length; i++) {
  307. float k = a.alphaKeys[i].time;
  308. if (!alphaKeysTimes.Contains(k))
  309. alphaKeysTimes.Add(k);
  310. }
  311. }
  312. if (b.alphaKeys != null) {
  313. for (int i = 0; i < b.alphaKeys.Length; i++) {
  314. float k = b.alphaKeys[i].time;
  315. if (!alphaKeysTimes.Contains(k))
  316. alphaKeysTimes.Add(k);
  317. }
  318. }
  319. int colorKeysTimesCount = colorKeysTimes.Count;
  320. GradientColorKey[] colorKeys = g.colorKeys;
  321. if (colorKeys == null || colorKeys.Length != colorKeysTimesCount) {
  322. colorKeys = new GradientColorKey[colorKeysTimesCount];
  323. }
  324. for (int i = 0; i < colorKeysTimesCount; i++) {
  325. float key = colorKeysTimes[i];
  326. var color = Color.Lerp(a.Evaluate(key), b.Evaluate(key), t);
  327. colorKeys[i] = new GradientColorKey(color, key);
  328. }
  329. int alphaKeysTimesCount = alphaKeysTimes.Count;
  330. GradientAlphaKey[] alphaKeys = g.alphaKeys;
  331. if (alphaKeys == null || alphaKeys.Length != alphaKeysTimesCount) {
  332. alphaKeys = new GradientAlphaKey[alphaKeysTimesCount];
  333. }
  334. for (int i = 0; i < alphaKeysTimesCount; i++) {
  335. float key = alphaKeysTimes[i];
  336. var color = Color.Lerp(a.Evaluate(key), b.Evaluate(key), t);
  337. alphaKeys[i] = new GradientAlphaKey(color.a, key);
  338. }
  339. g.SetKeys(colorKeys, alphaKeys);
  340. }
  341. }
  342. }