Unity_1lab/1 laba/Assets/furniture/BK_AlchemistHouse/Scripts/VolumetricLight.cs
lapich_valya ba035efa87 models
добавила модели, текстуры стен, пола
2025-09-18 12:50:59 +03:00

483 lines
18 KiB
C#

// Copyright(c) 2016, Michal Skalsky
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using UnityEngine;
using System.Collections;
using UnityEngine.Rendering;
using System;
[RequireComponent(typeof(Light))]
public class VolumetricLight : MonoBehaviour
{
public event Action<VolumetricLightRenderer, VolumetricLight, CommandBuffer, Matrix4x4> CustomRenderEvent;
private Light _light;
private Material _material;
private CommandBuffer _commandBuffer;
private CommandBuffer _cascadeShadowCommandBuffer;
[Range(1, 64)]
public int SampleCount = 8;
[Range(0.0f, 1.0f)]
public float ScatteringCoef = 0.5f;
[Range(0.0f, 0.1f)]
public float ExtinctionCoef = 0.01f;
[Range(0.0f, 1.0f)]
public float SkyboxExtinctionCoef = 0.9f;
[Range(0.0f, 0.999f)]
public float MieG = 0.1f;
public bool HeightFog = false;
[Range(0, 0.5f)]
public float HeightScale = 0.10f;
public float GroundLevel = 0;
public bool Noise = false;
public float NoiseScale = 0.015f;
public float NoiseIntensity = 1.0f;
public float NoiseIntensityOffset = 0.3f;
public Vector2 NoiseVelocity = new Vector2(3.0f, 3.0f);
[Tooltip("")]
public float MaxRayLength = 400.0f;
public Light Light { get { return _light; } }
public Material VolumetricMaterial { get { return _material; } }
private Vector4[] _frustumCorners = new Vector4[4];
private bool _reversedZ = false;
/// <summary>
///
/// </summary>
void Start()
{
#if UNITY_5_5_OR_NEWER
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12 ||
SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal || SystemInfo.graphicsDeviceType == GraphicsDeviceType.PlayStation4 ||
SystemInfo.graphicsDeviceType == GraphicsDeviceType.Vulkan || SystemInfo.graphicsDeviceType == GraphicsDeviceType.XboxOne)
{
_reversedZ = true;
}
#endif
_commandBuffer = new CommandBuffer();
_commandBuffer.name = "Light Command Buffer";
_cascadeShadowCommandBuffer = new CommandBuffer();
_cascadeShadowCommandBuffer.name = "Dir Light Command Buffer";
_cascadeShadowCommandBuffer.SetGlobalTexture("_CascadeShadowMapTexture", new UnityEngine.Rendering.RenderTargetIdentifier(UnityEngine.Rendering.BuiltinRenderTextureType.CurrentActive));
_light = GetComponent<Light>();
//_light.RemoveAllCommandBuffers();
if(_light.type == LightType.Directional)
{
_light.AddCommandBuffer(LightEvent.BeforeScreenspaceMask, _commandBuffer);
_light.AddCommandBuffer(LightEvent.AfterShadowMap, _cascadeShadowCommandBuffer);
}
else
_light.AddCommandBuffer(LightEvent.AfterShadowMap, _commandBuffer);
Shader shader = Shader.Find("Sandbox/VolumetricLight");
if (shader == null)
throw new Exception("Critical Error: \"Sandbox/VolumetricLight\" shader is missing. Make sure it is included in \"Always Included Shaders\" in ProjectSettings/Graphics.");
_material = new Material(shader); // new Material(VolumetricLightRenderer.GetLightMaterial());
}
/// <summary>
///
/// </summary>
void OnEnable()
{
VolumetricLightRenderer.PreRenderEvent += VolumetricLightRenderer_PreRenderEvent;
}
/// <summary>
///
/// </summary>
void OnDisable()
{
VolumetricLightRenderer.PreRenderEvent -= VolumetricLightRenderer_PreRenderEvent;
}
/// <summary>
///
/// </summary>
public void OnDestroy()
{
Destroy(_material);
}
/// <summary>
///
/// </summary>
/// <param name="renderer"></param>
/// <param name="viewProj"></param>
private void VolumetricLightRenderer_PreRenderEvent(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
{
// light was destroyed without deregistring, deregister now
if (_light == null || _light.gameObject == null)
{
VolumetricLightRenderer.PreRenderEvent -= VolumetricLightRenderer_PreRenderEvent;
}
if (!_light.gameObject.activeInHierarchy || _light.enabled == false)
return;
_material.SetVector("_CameraForward", Camera.current.transform.forward);
_material.SetInt("_SampleCount", SampleCount);
_material.SetVector("_NoiseVelocity", new Vector4(NoiseVelocity.x, NoiseVelocity.y) * NoiseScale);
_material.SetVector("_NoiseData", new Vector4(NoiseScale, NoiseIntensity, NoiseIntensityOffset));
_material.SetVector("_MieG", new Vector4(1 - (MieG * MieG), 1 + (MieG * MieG), 2 * MieG, 1.0f / (4.0f * Mathf.PI)));
_material.SetVector("_VolumetricLight", new Vector4(ScatteringCoef, ExtinctionCoef, _light.range, 1.0f - SkyboxExtinctionCoef));
_material.SetTexture("_CameraDepthTexture", renderer.GetVolumeLightDepthBuffer());
//if (renderer.Resolution == VolumetricLightRenderer.VolumtericResolution.Full)
{
//_material.SetFloat("_ZTest", (int)UnityEngine.Rendering.CompareFunction.LessEqual);
//_material.DisableKeyword("MANUAL_ZTEST");
}
//else
{
_material.SetFloat("_ZTest", (int)UnityEngine.Rendering.CompareFunction.Always);
// downsampled light buffer can't use native zbuffer for ztest, try to perform ztest in pixel shader to avoid ray marching for occulded geometry
//_material.EnableKeyword("MANUAL_ZTEST");
}
if (HeightFog)
{
_material.EnableKeyword("HEIGHT_FOG");
_material.SetVector("_HeightFog", new Vector4(GroundLevel, HeightScale));
}
else
{
_material.DisableKeyword("HEIGHT_FOG");
}
if(_light.type == LightType.Point)
{
SetupPointLight(renderer, viewProj);
}
else if(_light.type == LightType.Spot)
{
SetupSpotLight(renderer, viewProj);
}
else if (_light.type == LightType.Directional)
{
SetupDirectionalLight(renderer, viewProj);
}
}
void Update()
{
_commandBuffer.Clear();
}
/// <summary>
///
/// </summary>
/// <param name="renderer"></param>
/// <param name="viewProj"></param>
private void SetupPointLight(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
{
int pass = 0;
if (!IsCameraInPointLightBounds())
pass = 2;
_material.SetPass(pass);
Mesh mesh = VolumetricLightRenderer.GetPointLightMesh();
float scale = _light.range * 2.0f;
Matrix4x4 world = Matrix4x4.TRS(transform.position, _light.transform.rotation, new Vector3(scale, scale, scale));
_material.SetMatrix("_WorldViewProj", viewProj * world);
_material.SetMatrix("_WorldView", Camera.current.worldToCameraMatrix * world);
if (Noise)
_material.EnableKeyword("NOISE");
else
_material.DisableKeyword("NOISE");
_material.SetVector("_LightPos", new Vector4(_light.transform.position.x, _light.transform.position.y, _light.transform.position.z, 1.0f / (_light.range * _light.range)));
_material.SetColor("_LightColor", _light.color * _light.intensity);
if (_light.cookie == null)
{
_material.EnableKeyword("POINT");
_material.DisableKeyword("POINT_COOKIE");
}
else
{
Matrix4x4 view = Matrix4x4.TRS(_light.transform.position, _light.transform.rotation, Vector3.one).inverse;
_material.SetMatrix("_MyLightMatrix0", view);
_material.EnableKeyword("POINT_COOKIE");
_material.DisableKeyword("POINT");
_material.SetTexture("_LightTexture0", _light.cookie);
}
bool forceShadowsOff = false;
if ((_light.transform.position - Camera.current.transform.position).magnitude >= QualitySettings.shadowDistance)
forceShadowsOff = true;
if (_light.shadows != LightShadows.None && forceShadowsOff == false)
{
_material.EnableKeyword("SHADOWS_CUBE");
_commandBuffer.SetGlobalTexture("_ShadowMapTexture", BuiltinRenderTextureType.CurrentActive);
_commandBuffer.SetRenderTarget(renderer.GetVolumeLightBuffer());
_commandBuffer.DrawMesh(mesh, world, _material, 0, pass);
if (CustomRenderEvent != null)
CustomRenderEvent(renderer, this, _commandBuffer, viewProj);
}
else
{
_material.DisableKeyword("SHADOWS_CUBE");
renderer.GlobalCommandBuffer.DrawMesh(mesh, world, _material, 0, pass);
if (CustomRenderEvent != null)
CustomRenderEvent(renderer, this, renderer.GlobalCommandBuffer, viewProj);
}
}
/// <summary>
///
/// </summary>
/// <param name="renderer"></param>
/// <param name="viewProj"></param>
private void SetupSpotLight(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
{
int pass = 1;
if (!IsCameraInSpotLightBounds())
{
pass = 3;
}
Mesh mesh = VolumetricLightRenderer.GetSpotLightMesh();
float scale = _light.range;
float angleScale = Mathf.Tan((_light.spotAngle + 1) * 0.5f * Mathf.Deg2Rad) * _light.range;
Matrix4x4 world = Matrix4x4.TRS(transform.position, transform.rotation, new Vector3(angleScale, angleScale, scale));
Matrix4x4 view = Matrix4x4.TRS(_light.transform.position, _light.transform.rotation, Vector3.one).inverse;
Matrix4x4 clip = Matrix4x4.TRS(new Vector3(0.5f, 0.5f, 0.0f), Quaternion.identity, new Vector3(-0.5f, -0.5f, 1.0f));
Matrix4x4 proj = Matrix4x4.Perspective(_light.spotAngle, 1, 0, 1);
_material.SetMatrix("_MyLightMatrix0", clip * proj * view);
_material.SetMatrix("_WorldViewProj", viewProj * world);
_material.SetVector("_LightPos", new Vector4(_light.transform.position.x, _light.transform.position.y, _light.transform.position.z, 1.0f / (_light.range * _light.range)));
_material.SetVector("_LightColor", _light.color * _light.intensity);
Vector3 apex = transform.position;
Vector3 axis = transform.forward;
// plane equation ax + by + cz + d = 0; precompute d here to lighten the shader
Vector3 center = apex + axis * _light.range;
float d = -Vector3.Dot(center, axis);
// update material
_material.SetFloat("_PlaneD", d);
_material.SetFloat("_CosAngle", Mathf.Cos((_light.spotAngle + 1) * 0.5f * Mathf.Deg2Rad));
_material.SetVector("_ConeApex", new Vector4(apex.x, apex.y, apex.z));
_material.SetVector("_ConeAxis", new Vector4(axis.x, axis.y, axis.z));
_material.EnableKeyword("SPOT");
if (Noise)
_material.EnableKeyword("NOISE");
else
_material.DisableKeyword("NOISE");
if (_light.cookie == null)
{
_material.SetTexture("_LightTexture0", VolumetricLightRenderer.GetDefaultSpotCookie());
}
else
{
_material.SetTexture("_LightTexture0", _light.cookie);
}
bool forceShadowsOff = false;
if ((_light.transform.position - Camera.current.transform.position).magnitude >= QualitySettings.shadowDistance)
forceShadowsOff = true;
if (_light.shadows != LightShadows.None && forceShadowsOff == false)
{
clip = Matrix4x4.TRS(new Vector3(0.5f, 0.5f, 0.5f), Quaternion.identity, new Vector3(0.5f, 0.5f, 0.5f));
if(_reversedZ)
proj = Matrix4x4.Perspective(_light.spotAngle, 1, _light.range, _light.shadowNearPlane);
else
proj = Matrix4x4.Perspective(_light.spotAngle, 1, _light.shadowNearPlane, _light.range);
Matrix4x4 m = clip * proj;
m[0, 2] *= -1;
m[1, 2] *= -1;
m[2, 2] *= -1;
m[3, 2] *= -1;
//view = _light.transform.worldToLocalMatrix;
_material.SetMatrix("_MyWorld2Shadow", m * view);
_material.SetMatrix("_WorldView", m * view);
_material.EnableKeyword("SHADOWS_DEPTH");
_commandBuffer.SetGlobalTexture("_ShadowMapTexture", BuiltinRenderTextureType.CurrentActive);
_commandBuffer.SetRenderTarget(renderer.GetVolumeLightBuffer());
_commandBuffer.DrawMesh(mesh, world, _material, 0, pass);
if (CustomRenderEvent != null)
CustomRenderEvent(renderer, this, _commandBuffer, viewProj);
}
else
{
_material.DisableKeyword("SHADOWS_DEPTH");
renderer.GlobalCommandBuffer.DrawMesh(mesh, world, _material, 0, pass);
if (CustomRenderEvent != null)
CustomRenderEvent(renderer, this, renderer.GlobalCommandBuffer, viewProj);
}
}
/// <summary>
///
/// </summary>
/// <param name="renderer"></param>
/// <param name="viewProj"></param>
private void SetupDirectionalLight(VolumetricLightRenderer renderer, Matrix4x4 viewProj)
{
int pass = 4;
_material.SetPass(pass);
if (Noise)
_material.EnableKeyword("NOISE");
else
_material.DisableKeyword("NOISE");
_material.SetVector("_LightDir", new Vector4(_light.transform.forward.x, _light.transform.forward.y, _light.transform.forward.z, 1.0f / (_light.range * _light.range)));
_material.SetVector("_LightColor", _light.color * _light.intensity);
_material.SetFloat("_MaxRayLength", MaxRayLength);
if (_light.cookie == null)
{
_material.EnableKeyword("DIRECTIONAL");
_material.DisableKeyword("DIRECTIONAL_COOKIE");
}
else
{
_material.EnableKeyword("DIRECTIONAL_COOKIE");
_material.DisableKeyword("DIRECTIONAL");
_material.SetTexture("_LightTexture0", _light.cookie);
}
// setup frustum corners for world position reconstruction
// bottom left
_frustumCorners[0] = Camera.current.ViewportToWorldPoint(new Vector3(0, 0, Camera.current.farClipPlane));
// top left
_frustumCorners[2] = Camera.current.ViewportToWorldPoint(new Vector3(0, 1, Camera.current.farClipPlane));
// top right
_frustumCorners[3] = Camera.current.ViewportToWorldPoint(new Vector3(1, 1, Camera.current.farClipPlane));
// bottom right
_frustumCorners[1] = Camera.current.ViewportToWorldPoint(new Vector3(1, 0, Camera.current.farClipPlane));
#if UNITY_5_4_OR_NEWER
_material.SetVectorArray("_FrustumCorners", _frustumCorners);
#else
_material.SetVector("_FrustumCorners0", _frustumCorners[0]);
_material.SetVector("_FrustumCorners1", _frustumCorners[1]);
_material.SetVector("_FrustumCorners2", _frustumCorners[2]);
_material.SetVector("_FrustumCorners3", _frustumCorners[3]);
#endif
Texture nullTexture = null;
if (_light.shadows != LightShadows.None)
{
_material.EnableKeyword("SHADOWS_DEPTH");
_commandBuffer.Blit(nullTexture, renderer.GetVolumeLightBuffer(), _material, pass);
if (CustomRenderEvent != null)
CustomRenderEvent(renderer, this, _commandBuffer, viewProj);
}
else
{
_material.DisableKeyword("SHADOWS_DEPTH");
renderer.GlobalCommandBuffer.Blit(nullTexture, renderer.GetVolumeLightBuffer(), _material, pass);
if (CustomRenderEvent != null)
CustomRenderEvent(renderer, this, renderer.GlobalCommandBuffer, viewProj);
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool IsCameraInPointLightBounds()
{
float distanceSqr = (_light.transform.position - Camera.current.transform.position).sqrMagnitude;
float extendedRange = _light.range + 1;
if (distanceSqr < (extendedRange * extendedRange))
return true;
return false;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool IsCameraInSpotLightBounds()
{
// check range
float distance = Vector3.Dot(_light.transform.forward, (Camera.current.transform.position - _light.transform.position));
float extendedRange = _light.range + 1;
if (distance > (extendedRange))
return false;
// check angle
float cosAngle = Vector3.Dot(transform.forward, (Camera.current.transform.position - _light.transform.position).normalized);
if((Mathf.Acos(cosAngle) * Mathf.Rad2Deg) > (_light.spotAngle + 3) * 0.5f)
return false;
return true;
}
}