Chocolade19721309

Untitled

May 28th, 2024
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.85 KB | None | 0 0
  1. // Copyright(c) 2016, Michal Skalsky
  2. // All rights reserved.
  3. //
  4. // Redistribution and use in source and binary forms, with or without modification,
  5. // are permitted provided that the following conditions are met:
  6. //
  7. // 1. Redistributions of source code must retain the above copyright notice,
  8. // this list of conditions and the following disclaimer.
  9. //
  10. // 2. Redistributions in binary form must reproduce the above copyright notice,
  11. // this list of conditions and the following disclaimer in the documentation
  12. // and/or other materials provided with the distribution.
  13. //
  14. // 3. Neither the name of the copyright holder nor the names of its contributors
  15. // may be used to endorse or promote products derived from this software without
  16. // specific prior written permission.
  17. //
  18. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  19. // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20. // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT
  21. // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  22. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
  23. // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  25. // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  26. // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  
  28.  
  29.  
  30. using UnityEngine;
  31. using System.Collections;
  32. using UnityEngine.Rendering;
  33. using System;
  34.  
  35. [RequireComponent(typeof(Light))]
  36. public class VolumetricLight : MonoBehaviour
  37. {
  38. public event Action<VolumetricLightRenderer, VolumetricLight, CommandBuffer, Matrix4x4> CustomRenderEvent;
  39.  
  40. private Light _light;
  41. private Material _material;
  42. private CommandBuffer _commandBuffer;
  43. private CommandBuffer _cascadeShadowCommandBuffer;
  44.  
  45. public float intensityMultiplier = 1;
  46.  
  47. [Range(1, 64)]
  48. public int SampleCount = 8;
  49. [Range(0.0f, 1.0f)]
  50. public float ScatteringCoef = 0.5f;
  51. [Range(0.0f, 0.1f)]
  52. public float ExtinctionCoef = 0.01f;
  53. [Range(0.0f, 1.0f)]
  54. public float SkyboxExtinctionCoef = 0.9f;
  55. [Range(0.0f, 0.999f)]
  56. public float MieG = 0.1f;
  57. public bool HeightFog = false;
  58. [Range(0, 0.5f)]
  59. public float HeightScale = 0.10f;
  60. public float GroundLevel = 0;
  61. public bool Noise = false;
  62. public float NoiseScale = 0.015f;
  63. public float NoiseIntensity = 1.0f;
  64. public float NoiseIntensityOffset = 0.3f;
  65. public Vector2 NoiseVelocity = new Vector2(3.0f, 3.0f);
  66.  
  67. [Tooltip("")]
  68. public float MaxRayLength = 400.0f;
  69.  
  70. public Light Light { get { return _light; } }
  71. public Material VolumetricMaterial { get { return _material; } }
  72.  
  73. private Vector4[] _frustumCorners = new Vector4[4];
  74.  
  75. private bool _reversedZ = false;
  76.  
  77. /// <summary>
  78. ///
  79. /// </summary>
  80. void Start()
  81. {
  82. #if UNITY_5_5_OR_NEWER
  83. if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12 ||
  84. SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal || SystemInfo.graphicsDeviceType == GraphicsDeviceType.PlayStation4 ||
  85. SystemInfo.graphicsDeviceType == GraphicsDeviceType.Vulkan || SystemInfo.graphicsDeviceType == GraphicsDeviceType.XboxOne)
  86. {
  87. _reversedZ = true;
  88. }
  89. #endif
  90.  
  91. _commandBuffer = new CommandBuffer();
  92. _commandBuffer.name = "Light Command Buffer";
  93.  
  94. _cascadeShadowCommandBuffer = new CommandBuffer();
  95. _cascadeShadowCommandBuffer.name = "Dir Light Command Buffer";
  96. _cascadeShadowCommandBuffer.SetGlobalTexture("_CascadeShadowMapTexture", new UnityEngine.Rendering.RenderTargetIdentifier(UnityEngine.Rendering.BuiltinRenderTextureType.CurrentActive));
  97.  
  98. _light = GetComponent<Light>();
  99. //_light.RemoveAllCommandBuffers();
  100. if(_light.type == LightType.Directional)
  101. {
  102. _light.AddCommandBuffer(LightEvent.BeforeScreenspaceMask, _commandBuffer);
  103. _light.AddCommandBuffer(LightEvent.AfterShadowMap, _cascadeShadowCommandBuffer);
  104.  
  105. }
  106. else
  107. _light.AddCommandBuffer(LightEvent.AfterShadowMap, _commandBuffer);
  108.  
  109. Shader shader = Shader.Find("Sandbox/VolumetricLight");
  110. if (shader == null)
  111. throw new Exception("Critical Error: \"Sandbox/VolumetricLight\" shader is missing. Make sure it is included in \"Always Included Shaders\" in ProjectSettings/Graphics.");
  112. _material = new Material(shader); // new Material(VolumetricLightRenderer.GetLightMaterial());
  113. }
  114.  
  115. /// <summary>
  116. ///
  117. /// </summary>
  118. void OnEnable()
  119. {
  120. VolumetricLightRenderer.PreRenderEvent += VolumetricLightRenderer_PreRenderEvent;
  121. }
  122.  
  123. /// <summary>
  124. ///
  125. /// </summary>
  126. void OnDisable()
  127. {
  128. VolumetricLightRenderer.PreRenderEvent -= VolumetricLightRenderer_PreRenderEvent;
  129. }
  130.  
  131. /// <summary>
  132. ///
  133. /// </summary>
  134. public void OnDestroy()
  135. {
  136. Destroy(_material);
  137. }
  138.  
  139. /// <summary>
  140. ///
  141. /// </summary>
  142. /// <param name="renderer"></param>
  143. /// <param name="viewProj"></param>
  144. private void VolumetricLightRenderer_PreRenderEvent(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
  145. {
  146. // light was destroyed without deregistring, deregister now
  147. if (_light == null || _light.gameObject == null)
  148. {
  149. VolumetricLightRenderer.PreRenderEvent -= VolumetricLightRenderer_PreRenderEvent;
  150. }
  151.  
  152. if (!_light.gameObject.activeInHierarchy || _light.enabled == false)
  153. return;
  154.  
  155. _material.SetVector("_CameraForward", Camera.current.transform.forward);
  156.  
  157. _material.SetInt("_SampleCount", SampleCount);
  158. _material.SetVector("_NoiseVelocity", new Vector4(NoiseVelocity.x, NoiseVelocity.y) * NoiseScale);
  159. _material.SetVector("_NoiseData", new Vector4(NoiseScale, NoiseIntensity, NoiseIntensityOffset));
  160. _material.SetVector("_MieG", new Vector4(1 - (MieG * MieG), 1 + (MieG * MieG), 2 * MieG, 1.0f / (4.0f * Mathf.PI)));
  161. _material.SetVector("_VolumetricLight", new Vector4(ScatteringCoef, ExtinctionCoef, _light.range, 1.0f - SkyboxExtinctionCoef));
  162.  
  163. _material.SetTexture("_CameraDepthTexture", renderer.GetVolumeLightDepthBuffer());
  164.  
  165. //if (renderer.Resolution == VolumetricLightRenderer.VolumtericResolution.Full)
  166. {
  167. //_material.SetFloat("_ZTest", (int)UnityEngine.Rendering.CompareFunction.LessEqual);
  168. //_material.DisableKeyword("MANUAL_ZTEST");
  169. }
  170. //else
  171. {
  172. _material.SetFloat("_ZTest", (int)UnityEngine.Rendering.CompareFunction.Always);
  173. // downsampled light buffer can't use native zbuffer for ztest, try to perform ztest in pixel shader to avoid ray marching for occulded geometry
  174. //_material.EnableKeyword("MANUAL_ZTEST");
  175. }
  176.  
  177. if (HeightFog)
  178. {
  179. _material.EnableKeyword("HEIGHT_FOG");
  180.  
  181. _material.SetVector("_HeightFog", new Vector4(GroundLevel, HeightScale));
  182. }
  183. else
  184. {
  185. _material.DisableKeyword("HEIGHT_FOG");
  186. }
  187.  
  188. if(_light.type == LightType.Point)
  189. {
  190. SetupPointLight(renderer, viewProj);
  191. }
  192. else if(_light.type == LightType.Spot)
  193. {
  194. SetupSpotLight(renderer, viewProj);
  195. }
  196. else if (_light.type == LightType.Directional)
  197. {
  198. SetupDirectionalLight(renderer, viewProj);
  199. }
  200. }
  201.  
  202. /// <summary>
  203. ///
  204. /// </summary>
  205. /// <param name="renderer"></param>
  206. /// <param name="viewProj"></param>
  207. private void SetupPointLight(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
  208. {
  209. _commandBuffer.Clear();
  210.  
  211. int pass = 0;
  212. if (!IsCameraInPointLightBounds())
  213. pass = 2;
  214.  
  215. _material.SetPass(pass);
  216.  
  217. Mesh mesh = VolumetricLightRenderer.GetPointLightMesh();
  218.  
  219. float scale = _light.range * 2.0f;
  220. Matrix4x4 world = Matrix4x4.TRS(transform.position, _light.transform.rotation, new Vector3(scale, scale, scale));
  221.  
  222. _material.SetMatrix("_WorldViewProj", viewProj * world);
  223. _material.SetMatrix("_WorldView", Camera.current.worldToCameraMatrix * world);
  224.  
  225. if (Noise)
  226. _material.EnableKeyword("NOISE");
  227. else
  228. _material.DisableKeyword("NOISE");
  229.  
  230. _material.SetVector("_LightPos", new Vector4(_light.transform.position.x, _light.transform.position.y, _light.transform.position.z, 1.0f / (_light.range * _light.range)));
  231. _material.SetColor("_LightColor", _light.color * _light.intensity * intensityMultiplier);
  232.  
  233. if (_light.cookie == null)
  234. {
  235. _material.EnableKeyword("POINT");
  236. _material.DisableKeyword("POINT_COOKIE");
  237. }
  238. else
  239. {
  240. Matrix4x4 view = Matrix4x4.TRS(_light.transform.position, _light.transform.rotation, Vector3.one).inverse;
  241. _material.SetMatrix("_MyLightMatrix0", view);
  242.  
  243. _material.EnableKeyword("POINT_COOKIE");
  244. _material.DisableKeyword("POINT");
  245.  
  246. _material.SetTexture("_LightTexture0", _light.cookie);
  247. }
  248.  
  249. bool forceShadowsOff = false;
  250. if ((_light.transform.position - Camera.current.transform.position).magnitude >= QualitySettings.shadowDistance)
  251. forceShadowsOff = true;
  252.  
  253. if (_light.shadows != LightShadows.None && forceShadowsOff == false)
  254. {
  255. _material.EnableKeyword("SHADOWS_CUBE");
  256. _commandBuffer.SetGlobalTexture("_ShadowMapTexture", BuiltinRenderTextureType.CurrentActive);
  257. _commandBuffer.SetRenderTarget(renderer.GetVolumeLightBuffer());
  258.  
  259. _commandBuffer.DrawMesh(mesh, world, _material, 0, pass);
  260.  
  261. if (CustomRenderEvent != null)
  262. CustomRenderEvent(renderer, this, _commandBuffer, viewProj);
  263. }
  264. else
  265. {
  266. _material.DisableKeyword("SHADOWS_CUBE");
  267. renderer.GlobalCommandBuffer.DrawMesh(mesh, world, _material, 0, pass);
  268.  
  269. if (CustomRenderEvent != null)
  270. CustomRenderEvent(renderer, this, renderer.GlobalCommandBuffer, viewProj);
  271. }
  272. }
  273.  
  274. /// <summary>
  275. ///
  276. /// </summary>
  277. /// <param name="renderer"></param>
  278. /// <param name="viewProj"></param>
  279. private void SetupSpotLight(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
  280. {
  281. _commandBuffer.Clear();
  282.  
  283. int pass = 1;
  284. if (!IsCameraInSpotLightBounds())
  285. {
  286. pass = 3;
  287. }
  288.  
  289. Mesh mesh = VolumetricLightRenderer.GetSpotLightMesh();
  290.  
  291. float scale = _light.range;
  292. float angleScale = Mathf.Tan((_light.spotAngle + 1) * 0.5f * Mathf.Deg2Rad) * _light.range;
  293.  
  294. Matrix4x4 world = Matrix4x4.TRS(transform.position, transform.rotation, new Vector3(angleScale, angleScale, scale));
  295.  
  296. Matrix4x4 view = Matrix4x4.TRS(_light.transform.position, _light.transform.rotation, Vector3.one).inverse;
  297.  
  298. Matrix4x4 clip = Matrix4x4.TRS(new Vector3(0.5f, 0.5f, 0.0f), Quaternion.identity, new Vector3(-0.5f, -0.5f, 1.0f));
  299. Matrix4x4 proj = Matrix4x4.Perspective(_light.spotAngle, 1, 0, 1);
  300.  
  301. _material.SetMatrix("_MyLightMatrix0", clip * proj * view);
  302.  
  303. _material.SetMatrix("_WorldViewProj", viewProj * world);
  304.  
  305. _material.SetVector("_LightPos", new Vector4(_light.transform.position.x, _light.transform.position.y, _light.transform.position.z, 1.0f / (_light.range * _light.range)));
  306. _material.SetVector("_LightColor", _light.color * _light.intensity * intensityMultiplier);
  307.  
  308.  
  309. Vector3 apex = transform.position;
  310. Vector3 axis = transform.forward;
  311. // plane equation ax + by + cz + d = 0; precompute d here to lighten the shader
  312. Vector3 center = apex + axis * _light.range;
  313. float d = -Vector3.Dot(center, axis);
  314.  
  315. // update material
  316. _material.SetFloat("_PlaneD", d);
  317. _material.SetFloat("_CosAngle", Mathf.Cos((_light.spotAngle + 1) * 0.5f * Mathf.Deg2Rad));
  318.  
  319. _material.SetVector("_ConeApex", new Vector4(apex.x, apex.y, apex.z));
  320. _material.SetVector("_ConeAxis", new Vector4(axis.x, axis.y, axis.z));
  321.  
  322. _material.EnableKeyword("SPOT");
  323.  
  324. if (Noise)
  325. _material.EnableKeyword("NOISE");
  326. else
  327. _material.DisableKeyword("NOISE");
  328.  
  329. if (_light.cookie == null)
  330. {
  331. _material.SetTexture("_LightTexture0", VolumetricLightRenderer.GetDefaultSpotCookie());
  332. }
  333. else
  334. {
  335. _material.SetTexture("_LightTexture0", _light.cookie);
  336. }
  337.  
  338. bool forceShadowsOff = false;
  339. if ((_light.transform.position - Camera.current.transform.position).magnitude >= QualitySettings.shadowDistance)
  340. forceShadowsOff = true;
  341.  
  342. if (_light.shadows != LightShadows.None && forceShadowsOff == false)
  343. {
  344. clip = Matrix4x4.TRS(new Vector3(0.5f, 0.5f, 0.5f), Quaternion.identity, new Vector3(0.5f, 0.5f, 0.5f));
  345.  
  346. if(_reversedZ)
  347. proj = Matrix4x4.Perspective(_light.spotAngle, 1, _light.range, _light.shadowNearPlane);
  348. else
  349. proj = Matrix4x4.Perspective(_light.spotAngle, 1, _light.shadowNearPlane, _light.range);
  350.  
  351. Matrix4x4 m = clip * proj;
  352. m[0, 2] *= -1;
  353. m[1, 2] *= -1;
  354. m[2, 2] *= -1;
  355. m[3, 2] *= -1;
  356.  
  357. //view = _light.transform.worldToLocalMatrix;
  358. _material.SetMatrix("_MyWorld2Shadow", m * view);
  359. _material.SetMatrix("_WorldView", m * view);
  360.  
  361. _material.EnableKeyword("SHADOWS_DEPTH");
  362. _commandBuffer.SetGlobalTexture("_ShadowMapTexture", BuiltinRenderTextureType.CurrentActive);
  363. _commandBuffer.SetRenderTarget(renderer.GetVolumeLightBuffer());
  364.  
  365. _commandBuffer.DrawMesh(mesh, world, _material, 0, pass);
  366.  
  367. if (CustomRenderEvent != null)
  368. CustomRenderEvent(renderer, this, _commandBuffer, viewProj);
  369. }
  370. else
  371. {
  372. _material.DisableKeyword("SHADOWS_DEPTH");
  373. renderer.GlobalCommandBuffer.DrawMesh(mesh, world, _material, 0, pass);
  374.  
  375. if (CustomRenderEvent != null)
  376. CustomRenderEvent(renderer, this, renderer.GlobalCommandBuffer, viewProj);
  377. }
  378. }
  379.  
  380. /// <summary>
  381. ///
  382. /// </summary>
  383. /// <param name="renderer"></param>
  384. /// <param name="viewProj"></param>
  385. private void SetupDirectionalLight(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
  386. {
  387. _commandBuffer.Clear();
  388.  
  389. int pass = 4;
  390.  
  391. _material.SetPass(pass);
  392.  
  393. if (Noise)
  394. _material.EnableKeyword("NOISE");
  395. else
  396. _material.DisableKeyword("NOISE");
  397.  
  398. _material.SetVector("_LightDir", new Vector4(_light.transform.forward.x, _light.transform.forward.y, _light.transform.forward.z, 1.0f / (_light.range * _light.range)));
  399. _material.SetVector("_LightColor", _light.color * _light.intensity * intensityMultiplier);
  400. _material.SetFloat("_MaxRayLength", MaxRayLength);
  401.  
  402. if (_light.cookie == null)
  403. {
  404. _material.EnableKeyword("DIRECTIONAL");
  405. _material.DisableKeyword("DIRECTIONAL_COOKIE");
  406. }
  407. else
  408. {
  409. _material.EnableKeyword("DIRECTIONAL_COOKIE");
  410. _material.DisableKeyword("DIRECTIONAL");
  411.  
  412. _material.SetTexture("_LightTexture0", _light.cookie);
  413. }
  414.  
  415. // setup frustum corners for world position reconstruction
  416. // bottom left
  417. _frustumCorners[0] = Camera.current.ViewportToWorldPoint(new Vector3(0, 0, Camera.current.farClipPlane));
  418. // top left
  419. _frustumCorners[2] = Camera.current.ViewportToWorldPoint(new Vector3(0, 1, Camera.current.farClipPlane));
  420. // top right
  421. _frustumCorners[3] = Camera.current.ViewportToWorldPoint(new Vector3(1, 1, Camera.current.farClipPlane));
  422. // bottom right
  423. _frustumCorners[1] = Camera.current.ViewportToWorldPoint(new Vector3(1, 0, Camera.current.farClipPlane));
  424.  
  425. #if UNITY_5_4_OR_NEWER
  426. _material.SetVectorArray("_FrustumCorners", _frustumCorners);
  427. #else
  428. _material.SetVector("_FrustumCorners0", _frustumCorners[0]);
  429. _material.SetVector("_FrustumCorners1", _frustumCorners[1]);
  430. _material.SetVector("_FrustumCorners2", _frustumCorners[2]);
  431. _material.SetVector("_FrustumCorners3", _frustumCorners[3]);
  432. #endif
  433.  
  434. Texture nullTexture = null;
  435. if (_light.shadows != LightShadows.None)
  436. {
  437. _material.EnableKeyword("SHADOWS_DEPTH");
  438. _commandBuffer.Blit(nullTexture, renderer.GetVolumeLightBuffer(), _material, pass);
  439.  
  440. if (CustomRenderEvent != null)
  441. CustomRenderEvent(renderer, this, _commandBuffer, viewProj);
  442. }
  443. else
  444. {
  445. _material.DisableKeyword("SHADOWS_DEPTH");
  446. renderer.GlobalCommandBuffer.Blit(nullTexture, renderer.GetVolumeLightBuffer(), _material, pass);
  447.  
  448. if (CustomRenderEvent != null)
  449. CustomRenderEvent(renderer, this, renderer.GlobalCommandBuffer, viewProj);
  450. }
  451. }
  452.  
  453. /// <summary>
  454. ///
  455. /// </summary>
  456. /// <returns></returns>
  457. private bool IsCameraInPointLightBounds()
  458. {
  459. float distanceSqr = (_light.transform.position - Camera.current.transform.position).sqrMagnitude;
  460. float extendedRange = _light.range + 1;
  461. if (distanceSqr < (extendedRange * extendedRange))
  462. return true;
  463. return false;
  464. }
  465.  
  466. /// <summary>
  467. ///
  468. /// </summary>
  469. /// <returns></returns>
  470. private bool IsCameraInSpotLightBounds()
  471. {
  472. // check range
  473. float distance = Vector3.Dot(_light.transform.forward, (Camera.current.transform.position - _light.transform.position));
  474. float extendedRange = _light.range + 1;
  475. if (distance > (extendedRange))
  476. return false;
  477.  
  478. // check angle
  479. float cosAngle = Vector3.Dot(transform.forward, (Camera.current.transform.position - _light.transform.position).normalized);
  480. if((Mathf.Acos(cosAngle) * Mathf.Rad2Deg) > (_light.spotAngle + 3) * 0.5f)
  481. return false;
  482.  
  483. return true;
  484. }
  485. }
  486.  
Advertisement
Add Comment
Please, Sign In to add comment