This commit is contained in:
2025-05-16 23:31:59 +08:00
parent 9e4fef3f1e
commit d891e3f0ee
1198 changed files with 274242 additions and 1558 deletions

View File

@@ -0,0 +1,556 @@
#if DEBUG
//#define DEBUG_SHOW_MESH_NORMALS
#endif
#define FORCE_CURRENT_CAMERA_DEPTH_TEXTURE_MODE
#if UNITY_2018_1_OR_NEWER
#define VLB_SRP_SUPPORT // Comment this to disable SRP support
#endif
using UnityEngine;
using System.Collections;
#pragma warning disable 0429, 0162 // Unreachable expression code detected (because of Noise3D.isSupported on mobile)
namespace VLB
{
[AddComponentMenu("")] // hide it from Component search
[ExecuteInEditMode]
[HelpURL(Consts.Help.HD.UrlBeam)]
public class BeamGeometryHD : BeamGeometryAbstractBase
{
VolumetricLightBeamHD m_Master = null;
VolumetricCookieHD m_Cookie = null;
VolumetricShadowHD m_Shadow = null;
protected override VolumetricLightBeamAbstractBase GetMaster() { return m_Master; }
public bool visible
{
set { if (meshRenderer) meshRenderer.enabled = value; }
}
public int sortingLayerID
{
set { if (meshRenderer) meshRenderer.sortingLayerID = value; }
}
public int sortingOrder
{
set { if(meshRenderer) meshRenderer.sortingOrder = value; }
}
#if VLB_SRP_SUPPORT
Camera m_CurrentCameraRenderingSRP = null;
void OnDisable()
{
SRPHelper.UnregisterOnBeginCameraRendering(OnBeginCameraRenderingSRP);
m_CurrentCameraRenderingSRP = null;
}
public static bool isCustomRenderPipelineSupported { get { return true; } }
#else
public static bool isCustomRenderPipelineSupported { get { return false; } }
#endif
bool shouldUseGPUInstancedMaterial
{
get
{
if (Config.Instance.GetActualRenderingMode(ShaderMode.HD) == RenderingMode.GPUInstancing)
{
return m_Cookie == null && m_Shadow == null; // sampler cannot be passed to shader as instanced property
}
return false;
}
}
void OnEnable()
{
#if VLB_SRP_SUPPORT
SRPHelper.RegisterOnBeginCameraRendering(OnBeginCameraRenderingSRP);
#endif
}
public void Initialize(VolumetricLightBeamHD master)
{
Debug.Assert(master != null);
var customHideFlags = Consts.Internal.ProceduralObjectsHideFlags;
m_Master = master;
transform.SetParent(master.transform, false);
meshRenderer = gameObject.GetOrAddComponent<MeshRenderer>();
meshRenderer.hideFlags = customHideFlags;
meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
meshRenderer.receiveShadows = false;
meshRenderer.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off; // different reflection probes could break batching with GPU Instancing
meshRenderer.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;
m_Cookie = m_Master.GetAdditionalComponentCookie();
m_Shadow = m_Master.GetAdditionalComponentShadow();
if (!shouldUseGPUInstancedMaterial)
{
m_CustomMaterial = Config.Instance.NewMaterialTransient(ShaderMode.HD, gpuInstanced:false);
ApplyMaterial();
}
if (m_Master.DoesSupportSorting2D())
{
if (SortingLayer.IsValid(m_Master.GetSortingLayerID()))
sortingLayerID = m_Master.GetSortingLayerID();
else
Debug.LogError(string.Format("Beam '{0}' has an invalid sortingLayerID ({1}). Please fix it by setting a valid layer.", Utils.GetPath(m_Master.transform), m_Master.GetSortingLayerID()));
sortingOrder = m_Master.GetSortingOrder();
}
meshFilter = gameObject.GetOrAddComponent<MeshFilter>();
meshFilter.hideFlags = customHideFlags;
gameObject.hideFlags = customHideFlags;
#if UNITY_EDITOR
UnityEditor.GameObjectUtility.SetStaticEditorFlags(gameObject, master.GetStaticEditorFlagsForSubObjects());
gameObject.SetSameSceneVisibilityStatesThan(master.gameObject);
#endif
}
/// <summary>
/// Generate the cone mesh and calls UpdateMaterialAndBounds.
/// Since this process involves recreating a new mesh, make sure to not call it at every frame during playtime.
/// </summary>
public void RegenerateMesh()
{
Debug.Assert(m_Master);
if (Config.Instance.geometryOverrideLayer)
gameObject.layer = Config.Instance.geometryLayerID;
else
gameObject.layer = m_Master.gameObject.layer;
gameObject.tag = Config.Instance.geometryTag;
coneMesh = GlobalMeshHD.Get();
meshFilter.sharedMesh = coneMesh;
UpdateMaterialAndBounds();
}
Vector3 ComputeLocalMatrix()
{
// In the VS, we compute the vertices so the whole beam fits into a fixed 2x2x1 box.
// We have to apply some scaling to get the proper beam size.
// This way we have the proper bounds without having to recompute specific bounds foreach beam.
var maxRadius = Mathf.Max(m_Master.coneRadiusStart, m_Master.coneRadiusEnd);
var localScale = new Vector3(maxRadius, maxRadius, m_Master.maxGeometryDistance);
if (!m_Master.scalable)
localScale = localScale.Divide(m_Master.GetLossyScale());
transform.localScale = localScale;
transform.localRotation = m_Master.beamInternalLocalRotation;
return localScale;
}
bool isNoiseEnabled { get { return m_Master.isNoiseEnabled && m_Master.noiseIntensity > 0f && Noise3D.isSupported; } } // test Noise3D.isSupported the last
MaterialManager.StaticPropertiesHD ComputeMaterialStaticProperties()
{
var colorGradient = MaterialManager.ColorGradient.Off;
if (m_Master.colorMode == ColorMode.Gradient)
{
var precision = Utils.GetFloatPackingPrecision();
colorGradient = precision == Utils.FloatPackingPrecision.High ? MaterialManager.ColorGradient.MatrixHigh : MaterialManager.ColorGradient.MatrixLow;
}
Debug.Assert((int)BlendingMode.Additive == (int)MaterialManager.BlendingMode.Additive);
Debug.Assert((int)BlendingMode.SoftAdditive == (int)MaterialManager.BlendingMode.SoftAdditive);
Debug.Assert((int)BlendingMode.TraditionalTransparency == (int)MaterialManager.BlendingMode.TraditionalTransparency);
return new MaterialManager.StaticPropertiesHD
{
blendingMode = (MaterialManager.BlendingMode)m_Master.blendingMode,
attenuation = m_Master.attenuationEquation == AttenuationEquationHD.Linear ? MaterialManager.HD.Attenuation.Linear : MaterialManager.HD.Attenuation.Quadratic,
noise3D = isNoiseEnabled ? MaterialManager.Noise3D.On : MaterialManager.Noise3D.Off,
colorGradient = colorGradient,
shadow = m_Shadow != null ? MaterialManager.HD.Shadow.On : MaterialManager.HD.Shadow.Off,
cookie = (m_Cookie != null ? (m_Cookie.channel == CookieChannel.RGBA ? MaterialManager.HD.Cookie.RGBA : MaterialManager.HD.Cookie.SingleChannel) : MaterialManager.HD.Cookie.Off),
raymarchingQualityIndex = m_Master.raymarchingQualityIndex
};
}
bool ApplyMaterial()
{
var staticProps = ComputeMaterialStaticProperties();
Material mat = null;
if (!shouldUseGPUInstancedMaterial)
{
mat = m_CustomMaterial;
if(mat)
staticProps.ApplyToMaterial(mat);
}
else
{
mat = MaterialManager.GetInstancedMaterial(m_Master._INTERNAL_InstancedMaterialGroupID, ref staticProps);
}
meshRenderer.material = mat;
return mat != null;
}
#if DEBUG
bool m_CanChangePropertyBlock = false;
#endif
public void SetMaterialProp(int nameID, float value)
{
if (m_CustomMaterial)
m_CustomMaterial.SetFloat(nameID, value);
else
{
#if DEBUG
Debug.Assert(m_CanChangePropertyBlock == true);
#endif
MaterialManager.materialPropertyBlock.SetFloat(nameID, value);
}
}
public void SetMaterialProp(int nameID, Vector4 value)
{
if (m_CustomMaterial)
m_CustomMaterial.SetVector(nameID, value);
else
{
#if DEBUG
Debug.Assert(m_CanChangePropertyBlock == true);
#endif
MaterialManager.materialPropertyBlock.SetVector(nameID, value);
}
}
public void SetMaterialProp(int nameID, Color value)
{
if (m_CustomMaterial)
m_CustomMaterial.SetColor(nameID, value);
else
{
#if DEBUG
Debug.Assert(m_CanChangePropertyBlock == true);
#endif
MaterialManager.materialPropertyBlock.SetColor(nameID, value);
}
}
public void SetMaterialProp(int nameID, Matrix4x4 value)
{
if (m_CustomMaterial)
m_CustomMaterial.SetMatrix(nameID, value);
else
{
#if DEBUG
Debug.Assert(m_CanChangePropertyBlock == true);
#endif
MaterialManager.materialPropertyBlock.SetMatrix(nameID, value);
}
}
public void SetMaterialProp(int nameID, Texture value)
{
if (m_CustomMaterial)
m_CustomMaterial.SetTexture(nameID, value);
#if DEBUG
else
{
Debug.Assert(m_CanChangePropertyBlock == true);
Debug.LogErrorFormat(m_Master, "Setting a Texture property to a GPU instanced material is not supported: '{0}'", m_Master);
}
#endif
}
public enum InvalidTexture
{
Null,
NoDepth
}
public void SetMaterialProp(int nameID, InvalidTexture invalidTexture)
{
if (m_CustomMaterial)
{
Texture tex = null;
if (invalidTexture == InvalidTexture.NoDepth)
tex = SystemInfo.usesReversedZBuffer? Texture2D.blackTexture: Texture2D.whiteTexture;
m_CustomMaterial.SetTexture(nameID, tex);
}
}
void MaterialChangeStart()
{
if (m_CustomMaterial == null)
meshRenderer.GetPropertyBlock(MaterialManager.materialPropertyBlock);
#if DEBUG
m_CanChangePropertyBlock = true;
#endif
}
void MaterialChangeStop()
{
#if DEBUG
m_CanChangePropertyBlock = false;
#endif
if (m_CustomMaterial == null)
meshRenderer.SetPropertyBlock(MaterialManager.materialPropertyBlock);
}
////////////////////////
/// DIRTY PROPERTIES
////////////////////////
DirtyProps m_DirtyProps = DirtyProps.None;
public void SetPropertyDirty(DirtyProps prop)
{
m_DirtyProps |= prop;
if(prop.HasAtLeastOneFlag(DirtyProps.OnlyMaterialChangeOnly))
{
UpdateMaterialAndBounds(); // need to change material variant
}
}
void UpdateMaterialAndBounds()
{
Debug.Assert(m_Master);
if (ApplyMaterial() == false)
{
return;
}
MaterialChangeStart();
{
m_DirtyProps = DirtyProps.All; // make sure all props will be updated on next camera render
if (isNoiseEnabled)
{
Noise3D.LoadIfNeeded();
}
// make sure the bounds are good from the startup
ComputeLocalMatrix(); // compute matrix before sending it to the shader
#if VLB_SRP_SUPPORT
// This update is to make QA test 'ReflectionObliqueProjection' pass
UpdateMatricesPropertiesForGPUInstancingSRP();
#endif
}
MaterialChangeStop();
#if DEBUG_SHOW_MESH_NORMALS
for (int vertexInd = 0; vertexInd < coneMesh.vertexCount; vertexInd++)
{
var vertex = coneMesh.vertices[vertexInd];
// apply modification done inside VS
vertex.x *= Mathf.Lerp(coneRadius.x, coneRadius.y, vertex.z);
vertex.y *= Mathf.Lerp(coneRadius.x, coneRadius.y, vertex.z);
vertex.z *= m_Master.fallOffEnd;
var cosSinFlat = new Vector2(vertex.x, vertex.y).normalized;
var normal = new Vector3(cosSinFlat.x * Mathf.Cos(slopeRad), cosSinFlat.y * Mathf.Cos(slopeRad), -Mathf.Sin(slopeRad)).normalized;
vertex = transform.TransformPoint(vertex);
normal = transform.TransformDirection(normal);
Debug.DrawRay(vertex, normal * 0.25f);
}
#endif
}
#if VLB_SRP_SUPPORT
void UpdateMatricesPropertiesForGPUInstancingSRP()
{
if (SRPHelper.IsUsingCustomRenderPipeline() && Config.Instance.GetActualRenderingMode(ShaderMode.HD) == RenderingMode.GPUInstancing)
{
SetMaterialProp(ShaderProperties.LocalToWorldMatrix, transform.localToWorldMatrix);
SetMaterialProp(ShaderProperties.WorldToLocalMatrix, transform.worldToLocalMatrix);
}
}
#if UNITY_2019_1_OR_NEWER
void OnBeginCameraRenderingSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
#else
void OnBeginCameraRenderingSRP(Camera cam)
#endif
{
m_CurrentCameraRenderingSRP = cam;
}
#endif
void OnWillRenderObject()
{
Camera currentCam = null;
#if VLB_SRP_SUPPORT
if (SRPHelper.IsUsingCustomRenderPipeline())
{
currentCam = m_CurrentCameraRenderingSRP;
}
else
#endif
{
currentCam = Camera.current;
}
OnWillCameraRenderThisBeam(currentCam);
}
void OnWillCameraRenderThisBeam(Camera cam)
{
if (m_Master && cam)
{
if (
#if UNITY_EDITOR
Utils.IsEditorCamera(cam) || // make sure to call UpdateCameraRelatedProperties for editor scene camera
#endif
cam.enabled) // prevent from doing stuff when we render from a previous DynamicOcclusionDepthBuffer's DepthCamera, because the DepthCamera are disabled
{
Debug.Assert(cam.GetComponentInParent<VolumetricLightBeamHD>() == null);
UpdateMaterialPropertiesForCamera(cam);
if (m_Shadow)
m_Shadow.OnWillCameraRenderThisBeam(cam, this);
}
}
}
void UpdateDirtyMaterialProperties()
{
if (m_DirtyProps != DirtyProps.None)
{
if (m_DirtyProps.HasFlag(DirtyProps.Intensity))
{
SetMaterialProp(ShaderProperties.HD.Intensity, m_Master.intensity);
}
if (m_DirtyProps.HasFlag(DirtyProps.HDRPExposureWeight) && Config.Instance.isHDRPExposureWeightSupported)
{
SetMaterialProp(ShaderProperties.HDRPExposureWeight, m_Master.hdrpExposureWeight);
}
if (m_DirtyProps.HasFlag(DirtyProps.SideSoftness))
{
SetMaterialProp(ShaderProperties.HD.SideSoftness, m_Master.sideSoftness);
}
if (m_DirtyProps.HasFlag(DirtyProps.Color))
{
if (m_Master.colorMode == ColorMode.Flat)
{
SetMaterialProp(ShaderProperties.ColorFlat, m_Master.colorFlat);
}
else
{
var precision = Utils.GetFloatPackingPrecision();
m_ColorGradientMatrix = m_Master.colorGradient.SampleInMatrix((int)precision);
// pass the gradient matrix in OnWillRenderObject()
}
}
if (m_DirtyProps.HasFlag(DirtyProps.Cone))
{
// kMinRadius and kMinApexOffset prevents artifacts when fresnel computation is done in the vertex shader
const float kMinRadius = 0.0001f;
var coneRadius = new Vector2(Mathf.Max(m_Master.coneRadiusStart, kMinRadius), Mathf.Max(m_Master.coneRadiusEnd, kMinRadius));
SetMaterialProp(ShaderProperties.ConeRadius, coneRadius);
const float kMinApexOffset = 0.0001f;
float apexOffsetZ = m_Master.GetConeApexOffsetZ(false);
float nonNullApex = Mathf.Sign(apexOffsetZ) * Mathf.Max(Mathf.Abs(apexOffsetZ), kMinApexOffset);
SetMaterialProp(ShaderProperties.ConeGeomProps, new Vector2(nonNullApex, Config.Instance.sharedMeshSides));
SetMaterialProp(ShaderProperties.DistanceFallOff, new Vector3(m_Master.fallOffStart, m_Master.fallOffEnd, m_Master.maxGeometryDistance));
ComputeLocalMatrix(); // compute matrix before sending it to the shader
}
if (m_DirtyProps.HasFlag(DirtyProps.Jittering))
{
SetMaterialProp(ShaderProperties.HD.Jittering, new Vector4(m_Master.jitteringFactor, m_Master.jitteringFrameRate, m_Master.jitteringLerpRange.minValue, m_Master.jitteringLerpRange.maxValue));
}
if (isNoiseEnabled)
{
if (m_DirtyProps.HasFlag(DirtyProps.NoiseMode) || m_DirtyProps.HasFlag(DirtyProps.NoiseIntensity))
{
SetMaterialProp(ShaderProperties.NoiseParam, new Vector2(
m_Master.noiseIntensity,
m_Master.noiseMode == NoiseMode.WorldSpace ? 0f : 1f));
}
if (m_DirtyProps.HasFlag(DirtyProps.NoiseVelocityAndScale))
{
var noiseVelocity = m_Master.noiseVelocityUseGlobal ? Config.Instance.globalNoiseVelocity : m_Master.noiseVelocityLocal;
var noiseScale = m_Master.noiseScaleUseGlobal ? Config.Instance.globalNoiseScale : m_Master.noiseScaleLocal;
SetMaterialProp(ShaderProperties.NoiseVelocityAndScale, new Vector4(
noiseVelocity.x,
noiseVelocity.y,
noiseVelocity.z,
noiseScale));
}
}
if (m_DirtyProps.HasFlag(DirtyProps.CookieProps))
VolumetricCookieHD.ApplyMaterialProperties(m_Cookie, this);
if (m_DirtyProps.HasFlag(DirtyProps.ShadowProps))
VolumetricShadowHD.ApplyMaterialProperties(m_Shadow, this);
m_DirtyProps = DirtyProps.None;
}
}
void UpdateMaterialPropertiesForCamera(Camera cam)
{
if (cam && m_Master)
{
MaterialChangeStart();
{
SetMaterialProp(ShaderProperties.HD.TransformScale, m_Master.scalable ? m_Master.GetLossyScale() : Vector3.one);
var camForwardVectorOSN = transform.InverseTransformDirection(cam.transform.forward).normalized;
SetMaterialProp(ShaderProperties.HD.CameraForwardOS, camForwardVectorOSN);
SetMaterialProp(ShaderProperties.HD.CameraForwardWS, cam.transform.forward);
UpdateDirtyMaterialProperties();
if (m_Master.colorMode == ColorMode.Gradient)
{
// Send the gradient matrix every frame since it's not a shader's property
SetMaterialProp(ShaderProperties.ColorGradientMatrix, m_ColorGradientMatrix);
}
#if VLB_SRP_SUPPORT
// This update is to be able to move beams without trackChangesDuringPlaytime enabled with SRP & GPU Instancing
UpdateMatricesPropertiesForGPUInstancingSRP();
#endif
}
MaterialChangeStop();
#if FORCE_CURRENT_CAMERA_DEPTH_TEXTURE_MODE
cam.depthTextureMode |= DepthTextureMode.Depth;
#endif
}
}
#if UNITY_EDITOR
public int _EDITOR_InstancedMaterialID { get { return ComputeMaterialStaticProperties().GetMaterialID(); } }
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c44f4896d3776534e9c0542bb3004c8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using UnityEngine;
namespace VLB
{
public static class GlobalMeshHD
{
public static Mesh Get()
{
if (ms_Mesh == null)
{
Destroy();
ms_Mesh = MeshGenerator.GenerateConeZ_Radii_DoubleCaps(
lengthZ: 1f,
radiusStart: 1f,
radiusEnd: 1f,
numSides: Config.Instance.sharedMeshSides,
inverted: true);
ms_Mesh.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
}
return ms_Mesh;
}
public static void Destroy()
{
if (ms_Mesh != null)
{
GameObject.DestroyImmediate(ms_Mesh);
ms_Mesh = null;
}
}
static Mesh ms_Mesh = null;
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: ef8cffdc0722e5740aa18175f5ba5358
timeCreated: 1646242645
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using UnityEngine;
namespace VLB
{
[System.Serializable]
public class RaymarchingQuality
{
public int uniqueID => _UniqueID;
public bool hasValidUniqueID => _UniqueID >= 0;
public string name;
public int stepCount;
public static RaymarchingQuality defaultInstance => ms_DefaultInstance;
[SerializeField] int _UniqueID;
static RaymarchingQuality ms_DefaultInstance = new RaymarchingQuality(-1);
const int kRandomUniqueIdMinRange = 4; // default qualities have fixed ID from 1 to 3
private RaymarchingQuality(int uniqueID)
{
_UniqueID = uniqueID;
name = "New quality";
stepCount = 10;
}
static public RaymarchingQuality New()
{
int id = Random.Range(kRandomUniqueIdMinRange, int.MaxValue);
return new RaymarchingQuality(id);
}
static public RaymarchingQuality New(string name, int forcedUniqueID, int stepCount)
{
var instance = new RaymarchingQuality(forcedUniqueID);
instance.name = name;
instance.stepCount = stepCount;
return instance;
}
static bool HasRaymarchingQualityWithSameUniqueID(RaymarchingQuality[] values, int id)
{
foreach (var qual in values)
{
if (qual != null && qual.uniqueID == id)
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f2d6a8c686dc7e749a8aaa943effba3f
timeCreated: 1613290493
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using UnityEngine;
namespace VLB
{
[DisallowMultipleComponent]
[RequireComponent(typeof(Light), typeof(VolumetricLightBeamHD))]
[HelpURL(Consts.Help.HD.UrlTrackRealtimeChangesOnLight)]
[AddComponentMenu(Consts.Help.HD.AddComponentMenuTrackRealtimeChangesOnLight)]
public class TrackRealtimeChangesOnLightHD : MonoBehaviour
{
public const string ClassName = "TrackRealtimeChangesOnLightHD";
VolumetricLightBeamHD m_Master = null;
void Awake()
{
m_Master = GetComponent<VolumetricLightBeamHD>();
Debug.Assert(m_Master);
}
void Update()
{
if(m_Master.enabled)
{
m_Master.AssignPropertiesFromAttachedSpotLight();
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 05c9c09d0366bdb43b3d087e6ca2aadc
timeCreated: 1616221308
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 92cd5f0496b5c33418ef9b57bb0a7e7b, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,167 @@
using UnityEngine;
namespace VLB
{
[ExecuteInEditMode]
[DisallowMultipleComponent]
[RequireComponent(typeof(VolumetricLightBeamHD))]
[HelpURL(Consts.Help.HD.UrlCookie)]
[AddComponentMenu(Consts.Help.HD.AddComponentMenuCookie)]
public class VolumetricCookieHD : MonoBehaviour
{
public const string ClassName = "VolumetricCookieHD";
/// <summary>
/// How much the cookie texture will contribute to the beam rendering.
/// </summary>
public float contribution
{
get { return m_Contribution; }
set { if (m_Contribution != value) { m_Contribution = value; SetDirty(); } }
}
/// <summary>
/// Specify the texture mask asset.
/// It can be a regular 'Cookie' texture or any other texture type.
/// </summary>
public Texture cookieTexture
{
get { return m_CookieTexture; }
set { if (m_CookieTexture != value) { m_CookieTexture = value; SetDirty(); } }
}
/// <summary>
/// Which channel(s) will be used to render the cookie.
/// </summary>
public CookieChannel channel
{
get { return m_Channel; }
set { if (m_Channel != value) { m_Channel = value; SetDirty(); } }
}
/// <summary>
/// - False: white/opaque value in chosen texture channel is visible.
/// - True: white/opaque value in chosen texture channel is hidden.
/// </summary>
public bool negative
{
get { return m_Negative; }
set { if (m_Negative != value) { m_Negative = value; SetDirty(); } }
}
/// <summary>
/// 2D local translation applied to the cookie texture.
/// </summary>
public Vector2 translation
{
get { return m_Translation; }
set { if (m_Translation != value) { m_Translation = value; SetDirty(); } }
}
/// <summary>
/// Rotation angle of the cookie texture (in degrees).
/// </summary>
public float rotation
{
get { return m_Rotation; }
set { if (m_Rotation != value) { m_Rotation = value; SetDirty(); } }
}
/// <summary>
/// 2D local scale applied to the cookie texture.
/// </summary>
public Vector2 scale
{
get { return m_Scale; }
set { if (m_Scale != value) { m_Scale = value; SetDirty(); } }
}
[SerializeField] float m_Contribution = Consts.Cookie.ContributionDefault;
[SerializeField] Texture m_CookieTexture = Consts.Cookie.CookieTextureDefault;
[SerializeField] CookieChannel m_Channel = Consts.Cookie.ChannelDefault;
[SerializeField] bool m_Negative = Consts.Cookie.NegativeDefault;
[SerializeField] Vector2 m_Translation = Consts.Cookie.TranslationDefault;
[SerializeField] float m_Rotation = Consts.Cookie.RotationDefault;
[SerializeField] Vector2 m_Scale = Consts.Cookie.ScaleDefault;
VolumetricLightBeamHD m_Master = null;
void SetDirty()
{
if (m_Master)
m_Master.SetPropertyDirty(DirtyProps.CookieProps);
}
public static void ApplyMaterialProperties(VolumetricCookieHD instance, BeamGeometryHD geom)
{
Debug.Assert(geom != null);
if (instance && instance.enabled && instance.cookieTexture != null)
{
geom.SetMaterialProp(ShaderProperties.HD.CookieTexture, instance.cookieTexture);
geom.SetMaterialProp(ShaderProperties.HD.CookieProperties,
new Vector4(instance.negative ? instance.contribution : -instance.contribution
, (float)instance.channel
, Mathf.Cos(instance.rotation * Mathf.Deg2Rad)
, Mathf.Sin(instance.rotation * Mathf.Deg2Rad)
));
geom.SetMaterialProp(ShaderProperties.HD.CookiePosAndScale, new Vector4(instance.translation.x, instance.translation.y, instance.scale.x, instance.scale.y));
}
else
{
geom.SetMaterialProp(ShaderProperties.HD.CookieTexture, BeamGeometryHD.InvalidTexture.Null);
geom.SetMaterialProp(ShaderProperties.HD.CookieProperties, Vector4.zero);
}
}
void Awake()
{
m_Master = GetComponent<VolumetricLightBeamHD>();
Debug.Assert(m_Master);
}
void OnEnable() { SetDirty(); }
void OnDisable() { SetDirty(); }
void OnDidApplyAnimationProperties() { SetDirty(); }
void Start()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
MakeBeamGeomDirty();
#endif // UNITY_EDITOR
if (Application.isPlaying)
{
SetDirty();
}
}
void OnDestroy()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
MakeBeamGeomDirty();
#endif // UNITY_EDITOR
if (Application.isPlaying)
{
SetDirty();
}
}
#if UNITY_EDITOR
void MakeBeamGeomDirty()
{
var beam = GetComponent<VolumetricLightBeamHD>();
if (beam)
beam._EditorSetBeamGeomDirty(); // need to recall BeamGeometry.Initialize to force havin a custom material
}
void OnValidate()
{
SetDirty();
}
#endif // UNITY_EDITOR
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 4bac7e82715c48546b7e9cdec13ae802
timeCreated: 1614337268
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: cc27b1da0fcf63b40bfa30f6d7317bc1, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,782 @@
//#define DEBUG_SHOW_APEX
#if UNITY_2019_3_OR_NEWER
#define VLB_LIGHT_TEMPERATURE_SUPPORT
#endif
using UnityEngine;
using UnityEngine.Serialization;
using System.Collections;
namespace VLB
{
[ExecuteInEditMode]
[DisallowMultipleComponent]
[SelectionBase]
[HelpURL(Consts.Help.HD.UrlBeam)]
[AddComponentMenu(Consts.Help.HD.AddComponentMenuBeam3D)]
public partial class VolumetricLightBeamHD : VolumetricLightBeamAbstractBase
{
public new const string ClassName = "VolumetricLightBeamHD";
/// <summary>
/// Get the color value from the light (when attached to a Spotlight) or not
/// </summary>
public bool colorFromLight
{
get { return m_ColorFromLight; }
set { if (m_ColorFromLight != value) { m_ColorFromLight = value; ValidateProperties(); } }
}
/// <summary>
/// Apply a flat/plain/single color, or a gradient
/// </summary>
public ColorMode colorMode
{
get {
if (Config.Instance.featureEnabledColorGradient == FeatureEnabledColorGradient.Off) return ColorMode.Flat;
return m_ColorMode;
}
set { if (m_ColorMode != value) { m_ColorMode = value; ValidateProperties(); SetPropertyDirty(DirtyProps.ColorMode); } }
}
/// <summary>
/// RGBA plain color, if colorMode is Flat (takes account of the alpha value).
/// </summary>
public Color colorFlat
{
get { return m_ColorFlat; }
set { if (m_ColorFlat != value) { m_ColorFlat = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Color); } }
}
/// <summary>
/// Gradient color applied along the beam, if colorMode is Gradient (takes account of the color and alpha variations).
/// </summary>
public Gradient colorGradient
{
get { return m_ColorGradient; }
set { if (m_ColorGradient != value) { m_ColorGradient = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Color); } }
}
#if UNITY_EDITOR
public override Color ComputeColorAtDepth(float depthRatio)
{
if (colorMode == ColorMode.Flat) return colorFlat;
else return colorGradient.Evaluate(depthRatio);
}
#endif
bool useColorFromAttachedLightSpot => colorFromLight && lightSpotAttached != null;
#if VLB_LIGHT_TEMPERATURE_SUPPORT
bool useColorTemperatureFromAttachedLightSpot => useColorFromAttachedLightSpot && lightSpotAttached.useColorTemperature && Config.Instance.useLightColorTemperature;
#else
bool useColorTemperatureFromAttachedLightSpot => false;
#endif
/// <summary>
/// Beam intensity
/// </summary>
public float intensity
{
get { return m_Intensity; }
set { if (m_Intensity != value) { m_Intensity = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Intensity); } }
}
/// <summary>
/// Multiplier to modulate the spotlight intensity.
/// </summary>
public float intensityMultiplier
{
get { return m_IntensityMultiplier; }
set { if (m_IntensityMultiplier != value) { m_IntensityMultiplier = value; ValidateProperties(); } }
}
/// <summary>
/// Get the intensity value from the light (when attached to a Spotlight) or not
/// </summary>
public bool useIntensityFromAttachedLightSpot
{
get { return intensityMultiplier >= 0.0f && lightSpotAttached != null; }
set { intensityMultiplier = (value ? 1.0f : -1.0f) * Mathf.Abs(intensityMultiplier); }
}
/// <summary>
/// HDRP Only
/// Use this property to set how much effect the camera exposure has on the beam intensity.
/// </summary>
public float hdrpExposureWeight
{
get { return m_HDRPExposureWeight; }
set { if (m_HDRPExposureWeight != value) { m_HDRPExposureWeight = value; ValidateProperties(); SetPropertyDirty(DirtyProps.HDRPExposureWeight); } }
}
/// <summary>
/// Change how the light beam colors will be mixed with the scene
/// </summary>
public BlendingMode blendingMode
{
get { return m_BlendingMode; }
set { if (m_BlendingMode != value) { m_BlendingMode = value; ValidateProperties(); SetPropertyDirty(DirtyProps.BlendingMode); } }
}
/// <summary>
/// Spot Angle (in degrees). This doesn't take account of the radiusStart, and is not necessarily the same than the cone angle.
/// </summary>
public float spotAngle
{
get { return m_SpotAngle; }
set { if (m_SpotAngle != value) { m_SpotAngle = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Cone); } }
}
/// <summary>
/// Multiplier to modulate the spotlight spot angle.
/// </summary>
public float spotAngleMultiplier
{
get { return m_SpotAngleMultiplier; }
set { if (m_SpotAngleMultiplier != value) { m_SpotAngleMultiplier = value; ValidateProperties(); } }
}
/// <summary>
/// Get the spotAngle value from the light (when attached to a Spotlight) or not
/// </summary>
public bool useSpotAngleFromAttachedLightSpot
{
get { return spotAngleMultiplier >= 0.0f && lightSpotAttached != null; }
set { spotAngleMultiplier = (value ? 1.0f : -1.0f) * Mathf.Abs(spotAngleMultiplier); }
}
/// <summary>
/// Cone Angle (in degrees). This takes account of the radiusStart, and is not necessarily the same than the spot angle.
/// </summary>
public float coneAngle { get { return Mathf.Atan2(coneRadiusEnd - coneRadiusStart, maxGeometryDistance) * Mathf.Rad2Deg * 2f; } }
/// <summary>
/// Start radius of the cone geometry.
/// 0 will generate a perfect cone geometry. Higher values will generate truncated cones.
/// </summary>
public float coneRadiusStart
{
get { return m_ConeRadiusStart; }
set { if (m_ConeRadiusStart != value) { m_ConeRadiusStart = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Cone); } }
}
/// <summary>
/// End radius of the cone geometry
/// </summary>
public float coneRadiusEnd {
get { return Utils.ComputeConeRadiusEnd(maxGeometryDistance, spotAngle); }
set { spotAngle = Utils.ComputeSpotAngle(maxGeometryDistance, value); }
}
/// <summary>
/// Volume (in unit^3) of the cone (from the base to fallOffEnd)
/// </summary>
public float coneVolume { get { float r1 = coneRadiusStart, r2 = coneRadiusEnd; return (Mathf.PI / 3) * (r1 * r1 + r1 * r2 + r2 * r2) * fallOffEnd; } }
/// <summary>
/// Apex distance of the truncated radius
/// If coneRadiusStart = 0, the apex is the at the truncated radius, so coneApexOffsetZ = 0
/// Otherwise, coneApexOffsetZ > 0 and represents the local position Z offset
/// </summary>
public float GetConeApexOffsetZ(bool counterApplyScaleForUnscalableBeam)
{
// simple intercept
float ratioRadius = coneRadiusStart / coneRadiusEnd;
if (ratioRadius == 1f)
return float.MaxValue;
else
{
float value = ((maxGeometryDistance * ratioRadius) / (1 - ratioRadius));
if(counterApplyScaleForUnscalableBeam && !scalable) value /= GetLossyScale().z;
return value;
}
}
/// <summary>
/// The scaling of the beam's GameObject (or any of its parent in the hierarchy)...
/// - True: ...will be applied to the beam itself and will change its size.
/// - False: ...won't be applied to the beam itself, so the beam won't have its size changed.
/// In short, we recommend to set the Scalable property at:
/// - True when there is no Unity Light attached to the same GameObject, so you will be able to scale your beam easily.
/// - False when there is a Unity Light attached to the same GameObject, because the Unity Light are NOT scalable: this way you beam will always fit the Unity Light size.
/// </summary>
public bool scalable {
get { return m_Scalable; }
set { if (m_Scalable != value) { m_Scalable = value; SetPropertyDirty(DirtyProps.Attenuation); } }
}
public override bool IsScalable() { return scalable; }
/// <summary>
/// Light attenuation formula used to compute fading between 'fallOffStart' and 'fallOffEnd'
/// </summary>
public AttenuationEquationHD attenuationEquation
{
get { return m_AttenuationEquation; }
set { if (m_AttenuationEquation != value) { m_AttenuationEquation = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Attenuation); } }
}
/// <summary>
/// Distance from the light source (in units) the beam will start to fade out.
/// </summary>
public float fallOffStart
{
get { return m_FallOffStart; }
set { if (m_FallOffStart != value) { m_FallOffStart = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Cone); } }
}
/// <summary>
/// Distance from the light source (in units) the beam is entirely faded out.
/// </summary>
public float fallOffEnd
{
get { return m_FallOffEnd; }
set { if (m_FallOffEnd != value) { m_FallOffEnd = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Cone); } }
}
public float maxGeometryDistance { get { return fallOffEnd; } }
/// <summary>
/// Distance multiplier to modulate the spotlight range.
/// </summary>
public float fallOffEndMultiplier
{
get { return m_FallOffEndMultiplier; }
set { if (m_FallOffEndMultiplier != value) { m_FallOffEndMultiplier = value; ValidateProperties(); } }
}
/// <summary>
/// Get the fallOffEnd value from the light (when attached to a Spotlight) or not
/// </summary>
public bool useFallOffEndFromAttachedLightSpot
{
get { return fallOffEndMultiplier >= 0f && lightSpotAttached != null; }
set { fallOffEndMultiplier = (value ? 1.0f : -1.0f) * Mathf.Abs(fallOffEndMultiplier); }
}
public float sideSoftness
{
get { return m_SideSoftness; }
set { if (m_SideSoftness != value) { m_SideSoftness = value; ValidateProperties(); SetPropertyDirty(DirtyProps.SideSoftness); } }
}
/// <summary>
/// When using Shadow or Cookie with a raymarching quality with too low steps count, some banding artifacts can appear.
/// In this case, increase jittering to add noise to smooth raymarching inaccuracy.
/// </summary>
public float jitteringFactor
{
get { return m_JitteringFactor; }
set { if (m_JitteringFactor != value) { m_JitteringFactor = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Jittering); } }
}
/// <summary>
/// Animate the jittering noise texture over the time.
/// </summary>
public int jitteringFrameRate
{
get { return m_JitteringFrameRate; }
set { if (m_JitteringFrameRate != value) { m_JitteringFrameRate = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Jittering); } }
}
/// <summary>
/// Configure where the jittering will be visible along the beam.
/// This range is specified between 0 (the tip of the beam) and 1 (the end of the beam):
/// - before the range: no jittering
/// - in the range: jittering will lerp from 0 to 'jittering factor' value
/// - after the range: 'jittering factor' value
/// </summary>
public MinMaxRangeFloat jitteringLerpRange
{
get { return m_JitteringLerpRange; }
set { if (m_JitteringLerpRange != value) { m_JitteringLerpRange = value; ValidateProperties(); SetPropertyDirty(DirtyProps.Jittering); } }
}
/// <summary>
/// Enable 3D Noise effect and choose the mode
/// </summary>
public NoiseMode noiseMode
{
get { return m_NoiseMode; }
set { if (m_NoiseMode != value) { m_NoiseMode = value; ValidateProperties(); SetPropertyDirty(DirtyProps.NoiseMode); } }
}
public bool isNoiseEnabled { get { return noiseMode != NoiseMode.Disabled; } }
/// <summary>
/// Contribution factor of the 3D Noise (when enabled).
/// Higher intensity means the noise contribution is stronger and more visible.
/// </summary>
public float noiseIntensity
{
get { return m_NoiseIntensity; }
set { if (m_NoiseIntensity != value) { m_NoiseIntensity = value; ValidateProperties(); SetPropertyDirty(DirtyProps.NoiseIntensity); } }
}
/// <summary>
/// Get the noiseScale value from the Global 3D Noise configuration
/// </summary>
public bool noiseScaleUseGlobal
{
get { return m_NoiseScaleUseGlobal; }
set { if (m_NoiseScaleUseGlobal != value) { m_NoiseScaleUseGlobal = value; ValidateProperties(); SetPropertyDirty(DirtyProps.NoiseVelocityAndScale); } }
}
/// <summary>
/// 3D Noise texture scaling: higher scale make the noise more visible, but potentially less realistic.
/// </summary>
public float noiseScaleLocal
{
get { return m_NoiseScaleLocal; }
set { if (m_NoiseScaleLocal != value) { m_NoiseScaleLocal = value; ValidateProperties(); SetPropertyDirty(DirtyProps.NoiseVelocityAndScale); } }
}
/// <summary>
/// Get the noiseVelocity value from the Global 3D Noise configuration
/// </summary>
public bool noiseVelocityUseGlobal
{
get { return m_NoiseVelocityUseGlobal; }
set { if (m_NoiseVelocityUseGlobal != value) { m_NoiseVelocityUseGlobal = value; ValidateProperties(); SetPropertyDirty(DirtyProps.NoiseVelocityAndScale); } }
}
/// <summary>
/// World Space direction and speed of the 3D Noise scrolling, simulating the fog/smoke movement.
/// </summary>
public Vector3 noiseVelocityLocal
{
get { return m_NoiseVelocityLocal; }
set { if (m_NoiseVelocityLocal != value) { m_NoiseVelocityLocal = value; ValidateProperties(); SetPropertyDirty(DirtyProps.NoiseVelocityAndScale); } }
}
public int raymarchingQualityID
{
get { return m_RaymarchingQualityID; }
set { if (m_RaymarchingQualityID != value) { m_RaymarchingQualityID = value; ValidateProperties(); SetPropertyDirty(DirtyProps.RaymarchingQuality); } }
}
public int raymarchingQualityIndex
{
get { return Config.Instance.GetRaymarchingQualityIndexForUniqueID(raymarchingQualityID); }
set { raymarchingQualityID = Config.Instance.GetRaymarchingQualityForIndex(raymarchingQualityIndex).uniqueID; }
}
public override BeamGeometryAbstractBase GetBeamGeometry() { return m_BeamGeom; }
protected override void SetBeamGeometryNull() { m_BeamGeom = null; }
public int blendingModeAsInt { get { return Mathf.Clamp((int)blendingMode, 0, System.Enum.GetValues(typeof(BlendingMode)).Length); } }
public Quaternion beamInternalLocalRotation { get { return GetDimensions() == Dimensions.Dim3D ? Quaternion.identity : Quaternion.LookRotation(Vector3.right, Vector3.up); } }
public Vector3 beamLocalForward { get { return GetDimensions() == Dimensions.Dim3D ? Vector3.forward : Vector3.right; } }
public Vector3 beamGlobalForward { get { return transform.TransformDirection(beamLocalForward); } }
public override Vector3 GetLossyScale() { return GetDimensions() == Dimensions.Dim3D ? transform.lossyScale : new Vector3(transform.lossyScale.z, transform.lossyScale.y, transform.lossyScale.x); }
public VolumetricCookieHD GetAdditionalComponentCookie() { return GetComponent<VolumetricCookieHD>(); }
public VolumetricShadowHD GetAdditionalComponentShadow() { return GetComponent<VolumetricShadowHD>(); }
public void SetPropertyDirty(DirtyProps flags)
{
if (m_BeamGeom) m_BeamGeom.SetPropertyDirty(flags);
}
// Overridden in Beam 2D version
public virtual Dimensions GetDimensions() { return Dimensions.Dim3D; }
public virtual bool DoesSupportSorting2D() { return false; }
public virtual int GetSortingLayerID() { return 0; }
public virtual int GetSortingOrder() { return 0; }
// SERIALIZED PROPERTIES
[SerializeField] bool m_ColorFromLight = true;
[SerializeField] ColorMode m_ColorMode = Consts.Beam.ColorModeDefault;
[SerializeField] Color m_ColorFlat = Consts.Beam.FlatColor;
[SerializeField] Gradient m_ColorGradient;
[SerializeField] BlendingMode m_BlendingMode = Consts.Beam.BlendingModeDefault;
[SerializeField] float m_Intensity = Consts.Beam.IntensityDefault;
[SerializeField] float m_IntensityMultiplier = Consts.Beam.MultiplierDefault;
[SerializeField] float m_HDRPExposureWeight = Consts.Beam.HDRPExposureWeightDefault;
[SerializeField] float m_SpotAngle = Consts.Beam.SpotAngleDefault;
[SerializeField] float m_SpotAngleMultiplier = Consts.Beam.MultiplierDefault;
[SerializeField] float m_ConeRadiusStart = Consts.Beam.ConeRadiusStart;
[SerializeField] bool m_Scalable = Consts.Beam.ScalableDefault;
[SerializeField] float m_FallOffStart = Consts.Beam.FallOffStart;
[SerializeField] float m_FallOffEnd = Consts.Beam.FallOffEnd;
[SerializeField] float m_FallOffEndMultiplier = Consts.Beam.MultiplierDefault;
[SerializeField] AttenuationEquationHD m_AttenuationEquation = Consts.Beam.HD.AttenuationEquationDefault;
[SerializeField] float m_SideSoftness = Consts.Beam.HD.SideSoftnessDefault;
[SerializeField] int m_RaymarchingQualityID = -1;
[SerializeField] float m_JitteringFactor = Consts.Beam.HD.JitteringFactorDefault;
[SerializeField] int m_JitteringFrameRate = Consts.Beam.HD.JitteringFrameRateDefault;
[MinMaxRange(0.0f, 1.0f)] [SerializeField] MinMaxRangeFloat m_JitteringLerpRange = Consts.Beam.HD.JitteringLerpRange;
[SerializeField] NoiseMode m_NoiseMode = Consts.Beam.NoiseModeDefault;
[SerializeField] float m_NoiseIntensity = Consts.Beam.NoiseIntensityDefault;
[SerializeField] bool m_NoiseScaleUseGlobal = true;
[SerializeField] float m_NoiseScaleLocal = Consts.Beam.NoiseScaleDefault;
[SerializeField] bool m_NoiseVelocityUseGlobal = true;
[SerializeField] Vector3 m_NoiseVelocityLocal = Consts.Beam.NoiseVelocityDefault;
/// Internal property used for QA testing purpose, do not change
public uint _INTERNAL_InstancedMaterialGroupID { get; protected set; }
protected BeamGeometryHD m_BeamGeom = null;
#if UNITY_EDITOR
public BeamGeometryHD _EDITOR_GetBeamGeometry() { return m_BeamGeom; }
public override int _EDITOR_GetInstancedMaterialID() { return m_BeamGeom ? m_BeamGeom._EDITOR_InstancedMaterialID : int.MinValue; }
static VolumetricLightBeamHD[] _EditorFindAllInstances()
{
return Resources.FindObjectsOfTypeAll<VolumetricLightBeamHD>();
}
public static void _EditorSetAllMeshesDirty()
{
foreach (var instance in _EditorFindAllInstances())
instance._EditorSetMeshDirty();
}
public static void _EditorSetAllBeamGeomDirty()
{
foreach (var instance in _EditorFindAllInstances())
instance.m_EditorDirtyFlags |= EditorDirtyFlags.FullBeamGeomGAO;
}
#endif // UNITY_EDITOR
/// <summary>
/// Returns a value indicating if the world position passed in argument is inside the light beam or not.
/// This functions treats the beam like infinite (like the beam had an infinite length and never fell off)
/// </summary>
/// <param name="posWS">World position</param>
/// <returns>
/// < 0 position is out
/// = 0 position is exactly on the beam geometry
/// > 0 position is inside the cone
/// </returns>
public float GetInsideBeamFactor(Vector3 posWS) { return GetInsideBeamFactorFromObjectSpacePos(transform.InverseTransformPoint(posWS)); }
public float GetInsideBeamFactorFromObjectSpacePos(Vector3 posOS)
{
if(GetDimensions() == Dimensions.Dim2D)
{
posOS = new Vector3(posOS.z, posOS.y, posOS.x);
}
if (posOS.z < 0f) return -1f;
Vector2 posOSXY = posOS.xy();
// Compute a factor to know how far inside the beam cone the camera is
var triangle2D = new Vector2(posOSXY.magnitude, posOS.z + GetConeApexOffsetZ(true)).normalized;
const float maxRadiansDiff = 0.1f;
float slopeRad = (coneAngle * Mathf.Deg2Rad) / 2;
return Mathf.Clamp((Mathf.Abs(Mathf.Sin(slopeRad)) - Mathf.Abs(triangle2D.x)) / maxRadiansDiff, -1, 1);
}
/// <summary>
/// Regenerate the beam mesh (and also the material).
/// This can be slow (it recreates a mesh from scratch), so don't call this function during playtime.
/// You would need to call this function only if you want to change the properties 'geomSides' and 'geomCap' during playtime.
/// Otherwise, for the other properties, just enable 'trackChangesDuringPlaytime', or manually call 'UpdateAfterManualPropertyChange()'
/// </summary>
public override void GenerateGeometry()
{
if(pluginVersion == -1)
{
// Applied default quality to newly created beams
raymarchingQualityID = Config.Instance.defaultRaymarchingQualityUniqueID;
}
if (!Config.Instance.IsRaymarchingQualityUniqueIDValid(raymarchingQualityID))
{
Debug.LogErrorFormat(gameObject, "HD Beam '{0}': fallback to default quality '{1}'"
, name
, Config.Instance.GetRaymarchingQualityForUniqueID(Config.Instance.defaultRaymarchingQualityUniqueID).name
);
raymarchingQualityID = Config.Instance.defaultRaymarchingQualityUniqueID;
Utils.MarkCurrentSceneDirty();
}
HandleBackwardCompatibility(pluginVersion, Version.Current);
pluginVersion = Version.Current;
ValidateProperties();
if (m_BeamGeom == null)
{
m_BeamGeom = Utils.NewWithComponent<BeamGeometryHD>("Beam Geometry");
m_BeamGeom.Initialize(this);
}
m_BeamGeom.RegenerateMesh();
m_BeamGeom.visible = enabled;
base.GenerateGeometry();
}
/// <summary>
/// Update the beam material and its bounds.
/// Calling manually this function is useless if your beam has its property 'trackChangesDuringPlaytime' enabled
/// (because then this function is automatically called each frame).
/// However, if 'trackChangesDuringPlaytime' is disabled, and you change a property via Script for example,
/// you need to call this function to take the property change into account.
/// All properties changes are took into account, expect 'geomSides' and 'geomCap' which require to regenerate the geometry via 'GenerateGeometry()'
/// </summary>
public virtual void UpdateAfterManualPropertyChange()
{
ValidateProperties();
SetPropertyDirty(DirtyProps.All);
}
#if !UNITY_EDITOR
void Start()
{
InitLightSpotAttachedCached();
// In standalone builds, simply generate the geometry once in Start
GenerateGeometry();
}
#else
void Start()
{
if (Application.isPlaying)
{
InitLightSpotAttachedCached();
GenerateGeometry();
m_EditorDirtyFlags = EditorDirtyFlags.Clean;
}
else
{
// In Editor, creating geometry from Start and/or OnValidate generates warning in Unity 2017.
// So we do it from Update
m_EditorDirtyFlags = EditorDirtyFlags.Everything;
}
}
void OnValidate()
{
m_EditorDirtyFlags |= EditorDirtyFlags.Props; // Props have been modified from Editor
}
void Update() // EDITOR ONLY
{
EditorHandleLightPropertiesUpdate(); // Handle edition of light properties in Editor
if (m_EditorDirtyFlags == EditorDirtyFlags.Clean)
{
if (Application.isPlaying)
{
return;
}
}
else
{
if (m_EditorDirtyFlags.HasFlag(EditorDirtyFlags.Mesh))
{
if (m_EditorDirtyFlags.HasFlag(EditorDirtyFlags.BeamGeomGAO))
DestroyBeam();
GenerateGeometry(); // regenerate everything
}
else if (m_EditorDirtyFlags.HasFlag(EditorDirtyFlags.Props))
{
ValidateProperties();
}
}
// If we modify the attached Spotlight properties, or if we animate the beam via Unity 2017's timeline,
// we are not notified of properties changes. So we update the material anyway.
UpdateAfterManualPropertyChange();
m_EditorDirtyFlags = EditorDirtyFlags.Clean;
}
public override void CopyPropsFrom(VolumetricLightBeamAbstractBase beamSrc, BeamProps beamProps)
{
base.CopyPropsFrom(beamSrc, beamProps);
if (beamSrc is VolumetricLightBeamSD)
{
var beamSD = beamSrc as VolumetricLightBeamSD;
if (beamProps.HasFlag(BeamProps.Color)) { colorMode = beamSD.colorMode; colorFlat = beamSD.color; colorGradient = beamSD.colorGradient; }
if (beamProps.HasFlag(BeamProps.BlendingMode)) { blendingMode = beamSD.blendingMode; }
if (beamProps.HasFlag(BeamProps.Intensity)) { intensity = beamSD.intensityGlobal; intensityMultiplier = beamSD.intensityMultiplier; }
if (beamProps.HasFlag(BeamProps.FallOffAttenuation))
{
attenuationEquation = UtilsBeamProps.ConvertAttenuation(beamSD.attenuationEquation);
fallOffStart = beamSD.fallOffStart;
}
if (beamProps.HasFlag(BeamProps.SpotShape))
{
spotAngle = beamSD.spotAngle; spotAngleMultiplier = beamSD.spotAngleMultiplier;
coneRadiusStart = beamSD.coneRadiusStart;
fallOffEnd = beamSD.fallOffEnd; fallOffEndMultiplier = beamSD.fallOffEndMultiplier;
}
if (beamProps.HasFlag(BeamProps.Noise3D))
{
noiseMode = beamSD.noiseMode; noiseIntensity = beamSD.noiseIntensity;
noiseScaleUseGlobal = beamSD.noiseScaleUseGlobal; noiseScaleLocal = beamSD.noiseScaleLocal;
noiseVelocityUseGlobal = beamSD.noiseVelocityUseGlobal; noiseVelocityLocal = beamSD.noiseVelocityLocal;
}
}
else if (beamSrc is VolumetricLightBeamHD)
{
var beamHD = beamSrc as VolumetricLightBeamHD;
if (beamProps.HasFlag(BeamProps.Color)) { colorMode = beamHD.colorMode; colorFlat = beamHD.colorFlat; colorGradient = beamHD.colorGradient; }
if (beamProps.HasFlag(BeamProps.BlendingMode)) { blendingMode = beamHD.blendingMode; }
if (beamProps.HasFlag(BeamProps.Intensity)) { intensity = beamHD.intensity; intensityMultiplier = beamHD.intensityMultiplier; }
if (beamProps.HasFlag(BeamProps.FallOffAttenuation))
{
attenuationEquation = beamHD.attenuationEquation;
fallOffStart = beamHD.fallOffStart;
}
if (beamProps.HasFlag(BeamProps.SpotShape))
{
spotAngle = beamHD.spotAngle; spotAngleMultiplier = beamHD.spotAngleMultiplier;
coneRadiusStart = beamHD.coneRadiusStart;
fallOffEnd = beamHD.fallOffEnd; fallOffEndMultiplier = beamHD.fallOffEndMultiplier;
scalable = beamHD.scalable;
}
if (beamProps.HasFlag(BeamProps.Noise3D))
{
noiseMode = beamHD.noiseMode; noiseIntensity = beamHD.noiseIntensity;
noiseScaleUseGlobal = beamHD.noiseScaleUseGlobal; noiseScaleLocal = beamHD.noiseScaleLocal;
noiseVelocityUseGlobal = beamHD.noiseVelocityUseGlobal; noiseVelocityLocal = beamHD.noiseVelocityLocal;
}
}
}
public virtual void Reset()
{
m_ColorMode = Consts.Beam.ColorModeDefault;
m_ColorFlat = Consts.Beam.FlatColor;
m_ColorFromLight = true;
m_Intensity = Consts.Beam.IntensityDefault;
m_IntensityMultiplier = Consts.Beam.MultiplierDefault;
m_HDRPExposureWeight = Consts.Beam.HDRPExposureWeightDefault;
m_BlendingMode = Consts.Beam.BlendingModeDefault;
m_SpotAngle = Consts.Beam.SpotAngleDefault;
m_SpotAngleMultiplier = Consts.Beam.MultiplierDefault;
m_ConeRadiusStart = Consts.Beam.ConeRadiusStart;
m_Scalable = Consts.Beam.ScalableDefault;
m_AttenuationEquation = Consts.Beam.HD.AttenuationEquationDefault;
m_FallOffStart = Consts.Beam.FallOffStart;
m_FallOffEnd = Consts.Beam.FallOffEnd;
m_FallOffEndMultiplier = Consts.Beam.MultiplierDefault;
m_SideSoftness = Consts.Beam.HD.SideSoftnessDefault;
m_JitteringFactor = Consts.Beam.HD.JitteringFactorDefault;
m_JitteringFrameRate = Consts.Beam.HD.JitteringFrameRateDefault;
m_JitteringLerpRange = Consts.Beam.HD.JitteringLerpRange;
m_NoiseMode = Consts.Beam.NoiseModeDefault;
m_NoiseIntensity = Consts.Beam.NoiseIntensityDefault;
m_NoiseScaleUseGlobal = true;
m_NoiseScaleLocal = Consts.Beam.NoiseScaleDefault;
m_NoiseVelocityUseGlobal = true;
m_NoiseVelocityLocal = Consts.Beam.NoiseVelocityDefault;
m_EditorDirtyFlags = EditorDirtyFlags.Everything;
}
#endif // UNITY_EDITOR
void OnEnable()
{
if (m_BeamGeom) m_BeamGeom.visible = true;
}
void OnDisable()
{
if (m_BeamGeom) m_BeamGeom.visible = false;
}
void OnDidApplyAnimationProperties()
{
AssignPropertiesFromAttachedSpotLight(); // catch changes on UnityLight properties if it exists
UpdateAfterManualPropertyChange();
}
public void AssignPropertiesFromAttachedSpotLight()
{
var lightSpot = lightSpotAttached;
if (lightSpot)
{
Debug.AssertFormat(lightSpot.type == LightType.Spot, "Light attached to {0} '{1}' must be a Spot", ClassName, name);
if (useIntensityFromAttachedLightSpot) intensity = SpotLightHelper.GetIntensity(lightSpot) * intensityMultiplier;
if (useFallOffEndFromAttachedLightSpot) fallOffEnd = SpotLightHelper.GetFallOffEnd(lightSpot) * fallOffEndMultiplier;
if (useSpotAngleFromAttachedLightSpot) spotAngle = Mathf.Clamp(SpotLightHelper.GetSpotAngle(lightSpot) * spotAngleMultiplier, Consts.Beam.SpotAngleMin, Consts.Beam.SpotAngleMax);
if (m_ColorFromLight)
{
colorMode = ColorMode.Flat;
#if VLB_LIGHT_TEMPERATURE_SUPPORT
if (useColorTemperatureFromAttachedLightSpot)
{
Color colorFromTemp = Mathf.CorrelatedColorTemperatureToRGB(lightSpot.colorTemperature);
var finalColor = lightSpot.color.linear * colorFromTemp;
colorFlat = finalColor.gamma;
}
else
#endif
{
colorFlat = lightSpot.color;
}
}
}
}
void ClampProperties()
{
m_Intensity = Mathf.Max(m_Intensity, Consts.Beam.IntensityMin);
m_FallOffEnd = Mathf.Max(Consts.Beam.FallOffDistancesMinThreshold, m_FallOffEnd);
m_FallOffStart = Mathf.Clamp(m_FallOffStart, 0f, m_FallOffEnd - Consts.Beam.FallOffDistancesMinThreshold);
m_SpotAngle = Mathf.Clamp(m_SpotAngle, Consts.Beam.SpotAngleMin, Consts.Beam.SpotAngleMax);
m_ConeRadiusStart = Mathf.Max(m_ConeRadiusStart, 0f);
m_SideSoftness = Mathf.Clamp(m_SideSoftness, Consts.Beam.HD.SideSoftnessMin, Consts.Beam.HD.SideSoftnessMax);
m_JitteringFactor = Mathf.Max(m_JitteringFactor, Consts.Beam.HD.JitteringFactorMin);
m_JitteringFrameRate = Mathf.Clamp(m_JitteringFrameRate, Consts.Beam.HD.JitteringFrameRateMin, Consts.Beam.HD.JitteringFrameRateMax);
m_NoiseIntensity = Mathf.Clamp(m_NoiseIntensity, Consts.Beam.NoiseIntensityMin, Consts.Beam.NoiseIntensityMax);
// do not clamp multipliers properties since negative values means off
}
void ValidateProperties()
{
AssignPropertiesFromAttachedSpotLight();
ClampProperties();
}
void HandleBackwardCompatibility(int serializedVersion, int newVersion)
{
if (serializedVersion == -1) return; // freshly new spawned entity: nothing to do
if (serializedVersion == newVersion) return; // same version: nothing to do
// TODO
Utils.MarkCurrentSceneDirty();
}
#if UNITY_EDITOR && DEBUG_SHOW_APEX
void OnDrawGizmos()
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(new Vector3(0, 0, -GetConeApexOffsetZ(true)), 0.25f);
}
#endif // UNITY_EDITOR
}
}

View File

@@ -0,0 +1,28 @@
fileFormatVersion: 2
guid: 92b0a6142b2b0b449a62558873544363
labels:
- volumetric
- light
- lighting
- ray
- shaft
- beam
- density
- vr
- dynamic
- spot
- spotlight
- fog
- noise
- occlusion
- procedural
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- m_Shader: {fileID: 4800000, guid: 936bd00b0f168d949b0c27d2be40615a, type: 3}
executionOrder: 0
icon: {fileID: 2800000, guid: 837747a8b63291a48838c340da834873, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
using UnityEngine;
using UnityEngine.Serialization;
using System.Collections;
namespace VLB
{
[ExecuteInEditMode]
[DisallowMultipleComponent]
[SelectionBase]
[HelpURL(Consts.Help.HD.UrlBeam)]
[AddComponentMenu(Consts.Help.HD.AddComponentMenuBeam2D)]
public partial class VolumetricLightBeamHD2D : VolumetricLightBeamHD
{
/// <summary>
/// Unique ID of the beam's sorting layer.
/// </summary>
public int sortingLayerID
{
get { return m_SortingLayerID; }
set {
m_SortingLayerID = value;
if (m_BeamGeom) m_BeamGeom.sortingLayerID = value;
}
}
/// <summary>
/// Name of the beam's sorting layer.
/// </summary>
public string sortingLayerName
{
get { return SortingLayer.IDToName(sortingLayerID); }
set { sortingLayerID = SortingLayer.NameToID(value); }
}
/// <summary>
/// The overlay priority within its layer.
/// Lower numbers are rendered first and subsequent numbers overlay those below.
/// </summary>
public int sortingOrder
{
get { return m_SortingOrder; }
set
{
m_SortingOrder = value;
if (m_BeamGeom) m_BeamGeom.sortingOrder = value;
}
}
public override Dimensions GetDimensions() { return Dimensions.Dim2D; }
public override bool DoesSupportSorting2D() { return true; }
public override int GetSortingLayerID() { return sortingLayerID; }
public override int GetSortingOrder() { return sortingOrder; }
[SerializeField] int m_SortingLayerID = 0;
[SerializeField] int m_SortingOrder = 0;
public override void CopyPropsFrom(VolumetricLightBeamAbstractBase beamSrc, BeamProps beamProps)
{
base.CopyPropsFrom(beamSrc, beamProps);
if (beamSrc is VolumetricLightBeamSD)
{
var beamSD = beamSrc as VolumetricLightBeamSD;
if (beamProps.HasFlag(BeamProps.Props2D)) { sortingLayerID = beamSD.sortingLayerID; sortingOrder = beamSD.sortingOrder; }
}
else if (beamSrc is VolumetricLightBeamHD2D)
{
var beamHD2D = beamSrc as VolumetricLightBeamHD2D;
if (beamProps.HasFlag(BeamProps.Props2D)) { sortingLayerID = beamHD2D.sortingLayerID; sortingOrder = beamHD2D.sortingOrder; }
}
}
#if UNITY_EDITOR
public override void Reset()
{
base.Reset();
sortingLayerID = 0;
sortingOrder = 0;
}
#endif // UNITY_EDITOR
}
}

View File

@@ -0,0 +1,29 @@
fileFormatVersion: 2
guid: 979040b6cddec5e4e8261012ed1f2e84
labels:
- volumetric
- light
- lighting
- ray
- shaft
- beam
- density
- vr
- dynamic
- spot
- spotlight
- fog
- noise
- occlusion
- procedural
timeCreated: 1507404056
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences:
- m_Shader: {fileID: 4800000, guid: 936bd00b0f168d949b0c27d2be40615a, type: 3}
executionOrder: 0
icon: {fileID: 2800000, guid: 837747a8b63291a48838c340da834873, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,420 @@
using UnityEngine;
namespace VLB
{
[ExecuteInEditMode]
[DisallowMultipleComponent]
[RequireComponent(typeof(VolumetricLightBeamHD))]
[HelpURL(Consts.Help.HD.UrlShadow)]
[AddComponentMenu(Consts.Help.HD.AddComponentMenuShadow)]
public class VolumetricShadowHD : MonoBehaviour
{
public const string ClassName = "VolumetricShadowHD";
/// <summary>
/// Controls how dark the shadow cast by this Light Beam will be.
/// The bigger the value, the more the shadow will affect the visual.
/// </summary>
public float strength
{
get { return m_Strength; }
set { if (m_Strength != value) { m_Strength = value; SetDirty(); } }
}
/// <summary>
/// How often will the occlusion be processed?
/// Try to update the occlusion as rarely as possible to keep good performance.
/// </summary>
public ShadowUpdateRate updateRate
{
get { return m_UpdateRate; }
set { m_UpdateRate = value; }
}
/// <summary>
/// How many frames we wait between 2 occlusion tests?
/// If you want your beam to be super responsive to the changes of your environment, update it every frame by setting 1.
/// If you want to save on performance, we recommend to wait few frames between each update by setting a higher value.
/// </summary>
public int waitXFrames
{
get { return m_WaitXFrames; }
set { m_WaitXFrames = value; }
}
/// <summary>
/// The beam can only be occluded by objects located on the layers matching this mask.
/// It's very important to set it as restrictive as possible (checking only the layers which are necessary)
/// to perform a more efficient process in order to increase the performance.
/// It should NOT include the layer on which the beams are generated.
/// </summary>
public LayerMask layerMask
{
get { return m_LayerMask; }
set
{
m_LayerMask = value;
UpdateDepthCameraProperties();
}
}
/// <summary>
/// Whether or not the virtual camera will use occlusion culling during rendering from the beam's POV.
/// </summary>
public bool useOcclusionCulling
{
get { return m_UseOcclusionCulling; }
set
{
m_UseOcclusionCulling = value;
UpdateDepthCameraProperties();
}
}
/// <summary>
/// Controls how large the depth texture captured by the virtual camera is.
/// The lower the resolution, the better the performance, but the less accurate the rendering.
/// </summary>
public int depthMapResolution
{
get { return m_DepthMapResolution; }
set
{
if(m_DepthCamera != null && Application.isPlaying) { Debug.LogErrorFormat(Consts.Shadow.GetErrorChangeRuntimeDepthMapResolution(this)); }
m_DepthMapResolution = value;
}
}
/// <summary>
/// Controls how large the depth texture captured by the virtual camera is.
/// The lower the resolution, the better the performance, but the less accurate the rendering.
/// </summary>
public int depthMapDepth
{
get { return m_DepthMapDepth; }
set
{
if (m_DepthCamera != null && Application.isPlaying) { Debug.LogErrorFormat(Consts.Shadow.GetErrorChangeRuntimeDepthMapDepth(this)); }
m_DepthMapDepth = value;
}
}
/// <summary>
/// Manually process the occlusion.
/// You have to call this function in order to update the occlusion when using ShadowUpdateRate.Never.
/// </summary>
public void ProcessOcclusionManually() { ProcessOcclusion(ProcessOcclusionSource.User); }
[SerializeField] float m_Strength = Consts.Shadow.StrengthDefault;
[SerializeField] ShadowUpdateRate m_UpdateRate = Consts.Shadow.UpdateRateDefault;
[SerializeField] int m_WaitXFrames = Consts.Shadow.WaitFramesCountDefault;
[SerializeField] LayerMask m_LayerMask = Consts.Shadow.LayerMaskDefault;
[SerializeField] bool m_UseOcclusionCulling = Consts.Shadow.OcclusionCullingDefault;
[SerializeField] int m_DepthMapResolution = Consts.Shadow.DepthMapResolutionDefault;
[SerializeField] int m_DepthMapDepth = Consts.Shadow.DepthMapDepthDefault;
public void UpdateDepthCameraProperties()
{
if (m_DepthCamera)
{
m_DepthCamera.cullingMask = layerMask;
m_DepthCamera.useOcclusionCulling = useOcclusionCulling;
}
}
enum ProcessOcclusionSource
{
RenderLoop,
OnEnable,
EditorUpdate,
User,
}
void ProcessOcclusion(ProcessOcclusionSource source)
{
if (!Config.Instance.featureEnabledShadow)
return;
if(m_LastFrameRendered == Time.frameCount && Application.isPlaying && source == ProcessOcclusionSource.OnEnable)
return; // allow to call ProcessOcclusion from OnEnable (when disabling/enabling multiple a beam on the same frame) without generating an error
Debug.Assert(!Application.isPlaying || m_LastFrameRendered != Time.frameCount, "ProcessOcclusion has been called twice on the same frame, which is forbidden");
Debug.Assert(m_Master && m_DepthCamera);
if (SRPHelper.IsUsingCustomRenderPipeline()) // Recursive rendering is not supported on SRP
m_NeedToUpdateOcclusionNextFrame = true;
else
ProcessOcclusionInternal();
SetDirty(); // refresh material
if (updateRate.HasFlag(ShadowUpdateRate.OnBeamMove))
m_TransformPacked = transform.GetWorldPacked();
bool firstTime = m_LastFrameRendered < 0;
m_LastFrameRendered = Time.frameCount;
if (firstTime && _INTERNAL_ApplyRandomFrameOffset)
{
m_LastFrameRendered += Random.Range(0, waitXFrames); // add a random offset to prevent from updating texture for all beams having the same wait value
}
}
public static void ApplyMaterialProperties(VolumetricShadowHD instance, BeamGeometryHD geom)
{
Debug.Assert(geom != null);
if (instance && instance.enabled)
{
Debug.Assert(instance.m_DepthCamera);
geom.SetMaterialProp(ShaderProperties.HD.ShadowDepthTexture, instance.m_DepthCamera.targetTexture);
var scale = instance.m_Master.scalable ? instance.m_Master.GetLossyScale() : Vector3.one;
geom.SetMaterialProp(ShaderProperties.HD.ShadowProps, new Vector4(Mathf.Sign(scale.x) * Mathf.Sign(scale.z), Mathf.Sign(scale.y), instance.m_Strength, instance.m_DepthCamera.orthographic ? 0f : 1f));
}
else
{
geom.SetMaterialProp(ShaderProperties.HD.ShadowDepthTexture, BeamGeometryHD.InvalidTexture.NoDepth);
}
}
void Awake()
{
m_Master = GetComponent<VolumetricLightBeamHD>();
Debug.Assert(m_Master);
#if UNITY_EDITOR
MarkMaterialAsDirty();
#endif
}
void OnEnable()
{
OnValidateProperties();
InstantiateOrActivateDepthCamera();
OnBeamEnabled();
}
void OnDisable()
{
if (m_DepthCamera) m_DepthCamera.gameObject.SetActive(false);
SetDirty(); // refresh material with empty depth texture
}
void OnDestroy()
{
DestroyDepthCamera();
#if UNITY_EDITOR
MarkMaterialAsDirty();
#endif
}
void ProcessOcclusionInternal()
{
UpdateDepthCameraPropertiesAccordingToBeam();
m_DepthCamera.Render();
}
void OnBeamEnabled()
{
#if UNITY_EDITOR
if (!Application.isPlaying) { return; }
#endif
if (!enabled) { return; }
if (!updateRate.HasFlag(ShadowUpdateRate.Never))
ProcessOcclusion(ProcessOcclusionSource.OnEnable);
}
public void OnWillCameraRenderThisBeam(Camera cam, BeamGeometryHD beamGeom)
{
#if UNITY_EDITOR
if (!Application.isPlaying) { return; }
#endif
if (!enabled) { return; }
if(cam != null
&& cam.enabled
&& Time.frameCount != m_LastFrameRendered // prevent from updating multiple times if there are more than 1 camera
&& updateRate != ShadowUpdateRate.Never)
{
bool shouldUpdate = false;
if (!shouldUpdate && updateRate.HasFlag(ShadowUpdateRate.OnBeamMove))
{
if (!m_TransformPacked.IsSame(transform))
shouldUpdate = true;
}
if (!shouldUpdate && updateRate.HasFlag(ShadowUpdateRate.EveryXFrames))
{
if (Time.frameCount >= m_LastFrameRendered + waitXFrames)
shouldUpdate = true;
}
if (shouldUpdate)
ProcessOcclusion(ProcessOcclusionSource.RenderLoop);
}
}
void LateUpdate()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
if (m_NeedToReinstantiateDepthCamera)
{
DestroyDepthCamera();
InstantiateOrActivateDepthCamera();
m_NeedToReinstantiateDepthCamera = false;
}
if (m_Master && m_Master.enabled)
ProcessOcclusion(ProcessOcclusionSource.EditorUpdate);
}
#endif
if (m_NeedToUpdateOcclusionNextFrame && m_Master && m_DepthCamera
&& Time.frameCount > 1) // fix NullReferenceException in UnityEngine.Rendering.Universal.Internal.CopyDepthPass.Execute when using SRP
{
ProcessOcclusionInternal();
m_NeedToUpdateOcclusionNextFrame = false;
}
}
void UpdateDepthCameraPropertiesAccordingToBeam()
{
Debug.Assert(m_Master);
Utils.SetupDepthCamera(m_DepthCamera
, m_Master.GetConeApexOffsetZ(true), m_Master.maxGeometryDistance, m_Master.coneRadiusStart, m_Master.coneRadiusEnd
, m_Master.beamLocalForward, m_Master.GetLossyScale(), m_Master.scalable, m_Master.beamInternalLocalRotation
, false);
}
void InstantiateOrActivateDepthCamera()
{
if (m_DepthCamera != null)
{
m_DepthCamera.gameObject.SetActive(true); // active it in case it has been disabled by OnDisable()
}
else
{
// delete old depth cameras when duplicating the GAO
gameObject.ForeachComponentsInDirectChildrenOnly<Camera>(cam => DestroyImmediate(cam.gameObject), true);
m_DepthCamera = Utils.NewWithComponent<Camera>("Depth Camera");
if (m_DepthCamera && m_Master)
{
m_DepthCamera.enabled = false;
UpdateDepthCameraProperties(); // set layerMask & useOcclusionCulling
m_DepthCamera.clearFlags = CameraClearFlags.Depth;
m_DepthCamera.depthTextureMode = DepthTextureMode.Depth;
m_DepthCamera.renderingPath = RenderingPath.Forward; // RenderingPath.VertexLit is faster, but RenderingPath.Forward allows to catch alpha cutout
m_DepthCamera.gameObject.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
m_DepthCamera.transform.SetParent(transform, false);
Config.Instance.SetURPScriptableRendererIndexToDepthCamera(m_DepthCamera);
var rt = new RenderTexture(depthMapResolution, depthMapResolution, depthMapDepth, RenderTextureFormat.Depth);
m_DepthCamera.targetTexture = rt;
UpdateDepthCameraPropertiesAccordingToBeam();
#if UNITY_EDITOR
UnityEditor.GameObjectUtility.SetStaticEditorFlags(m_DepthCamera.gameObject, m_Master.GetStaticEditorFlagsForSubObjects());
m_DepthCamera.gameObject.SetSameSceneVisibilityStatesThan(m_Master.gameObject);
#endif
}
}
}
void DestroyDepthCamera()
{
if (m_DepthCamera)
{
if (m_DepthCamera.targetTexture)
{
m_DepthCamera.targetTexture.Release();
DestroyImmediate(m_DepthCamera.targetTexture);
m_DepthCamera.targetTexture = null;
}
DestroyImmediate(m_DepthCamera.gameObject); // Make sure to delete the GAO
m_DepthCamera = null;
}
}
void OnValidateProperties()
{
m_WaitXFrames = Mathf.Clamp(m_WaitXFrames, 1, 60);
m_DepthMapResolution = Mathf.Clamp(Mathf.NextPowerOfTwo(m_DepthMapResolution), 8, 2048);
m_DepthMapDepth = (Mathf.Clamp(m_DepthMapDepth, 16, 32) / 8) * 8;
}
void SetDirty()
{
if (m_Master)
m_Master.SetPropertyDirty(DirtyProps.ShadowProps);
}
VolumetricLightBeamHD m_Master = null;
TransformUtils.Packed m_TransformPacked;
int m_LastFrameRendered = int.MinValue;
public int _INTERNAL_LastFrameRendered { get { return m_LastFrameRendered; } } // for unit tests
Camera m_DepthCamera = null;
bool m_NeedToUpdateOcclusionNextFrame = false;
// Internal stuff for QA tests
static bool _INTERNAL_ApplyRandomFrameOffset = true;
public static bool INTERNAL_GetApplyRandomFrameOffset() { return _INTERNAL_ApplyRandomFrameOffset; }
public static void INTERNAL_EnableApplyRandomFrameOffset()
{
_INTERNAL_ApplyRandomFrameOffset = true;
}
public void INTERNAL_DisableApplyRandomFrameOffset()
{
_INTERNAL_ApplyRandomFrameOffset = false;
m_LastFrameRendered = int.MinValue; // we need to do that for HD Shadows since they start before QATest in OnEnable
}
#if UNITY_EDITOR
bool m_NeedToReinstantiateDepthCamera = false;
public void ForceReinstantiateDepthCamera()
{
m_NeedToReinstantiateDepthCamera = true;
}
void MarkMaterialAsDirty()
{
// when adding/removing this component in editor, we might need to switch from a GPU Instanced material to a custom one,
// since this feature doesn't support GPU Instancing
if (!Application.isPlaying)
m_Master._EditorSetBeamGeomDirty();
}
void OnValidate()
{
OnValidateProperties();
m_NeedToReinstantiateDepthCamera = true;
}
public bool HasLayerMaskIssues()
{
if (Config.Instance.geometryOverrideLayer)
{
int layerBit = 1 << Config.Instance.geometryLayerID;
return ((layerMask.value & layerBit) == layerBit);
}
return false;
}
#endif // UNITY_EDITOR
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 51e172702b541584f96980fe85c79969
timeCreated: 1614355336
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: f4140fedf3f72d7448a4e55ea9db44ab, type: 3}
userData:
assetBundleName:
assetBundleVariant: