using System;
namespace UnityEngine.Rendering
{
///
/// A marker to adjust probes in an area of the scene.
///
[CoreRPHelpURL("probevolumes-settings#probe-adjustment-volume", "com.unity.render-pipelines.high-definition")]
[ExecuteAlways]
[AddComponentMenu("Rendering/Probe Adjustment Volume")]
public class ProbeAdjustmentVolume : MonoBehaviour, ISerializationCallbackReceiver
{
/// The type of shape that an adjustment volume can take.
public enum Shape
{
/// A Box shape.
Box,
/// A Sphere shape.
Sphere,
};
/// The shape of the adjustment volume
[Tooltip("Select the shape used for this Probe Adjustment Volume.")]
public Shape shape = Shape.Box;
///
/// The size for box shape.
///
[Min(0.0f), Tooltip("Modify the size of this Probe Adjustment Volume. This is unaffected by the GameObject's Transform's Scale property.")]
public Vector3 size = new Vector3(1, 1, 1);
///
/// The size for sphere shape.
///
[Min(0.0f), Tooltip("Modify the radius of this Probe Adjustment Volume. This is unaffected by the GameObject's Transform's Scale property.")]
public float radius = 1.0f;
/// The mode that adjustment volume will operate in. It determines what probes falling within the volume will do.
public enum Mode
{
/// Invalidate the probes within the adjustment volume.
InvalidateProbes,
/// Override the dilation validity threshold for the probes within the adjustment volume.
OverrideValidityThreshold,
/// Apply an explicit virtual offset to the probes within the adjustment volume.
ApplyVirtualOffset,
/// Override the virtual offset settings for the probes within the adjustment volume.
OverrideVirtualOffsetSettings,
/// Override the dynamic sky shading direction for the probes within the adjustment volume.
OverrideSkyDirection,
/// Override the Lightmapper sample count for the probes within the adjustment volume.
OverrideSampleCount,
/// Control the rendering layer masks for the probes within the adjustment volume.
OverrideRenderingLayerMask,
/// Scale probe intensity.
IntensityScale = 99, // make sure this appears last
};
/// The mode that adjustment volume will operate in. It determines what probes falling within the volume will do.
public enum RenderingLayerMaskOperation
{
/// Overrides the rendering layer mask for the probes within the adjustment volume.
Override,
/// Add a rendering layer to the probes within the adjustment volume.
Add,
/// Removes a rendering layer to the probes within the adjustment volume.
Remove,
};
/// Choose what to do with probes falling inside this volume
public Mode mode = Mode.InvalidateProbes;
///
/// A scale to apply to probes falling within the invalidation volume. It is really important to use this with caution as it can lead to inconsistent lighting.
///
[Range(0.0001f, 2.0f), Tooltip("A multiplier applied to the intensity of probes covered by this Probe Adjustment Volume.")]
public float intensityScale = 1.0f;
///
/// The overridden dilation threshold.
///
[Range(0.0f, 0.95f)]
public float overriddenDilationThreshold = 0.75f;
/// The rotation angles for the virtual offset direction.
public Vector3 virtualOffsetRotation = Vector3.zero;
/// Determines how far probes are pushed along the specified virtual offset direction.
[Min(0.0f)]
public float virtualOffsetDistance = 1.0f;
/// Determines how far Unity pushes a probe out of geometry after a ray hit.
[Range(0f, 1f), Tooltip("Determines how far Unity pushes a probe out of geometry after a ray hit.")]
public float geometryBias = 0.01f;
/// Virtual Offset validity threshold.
[Range(0f, 0.95f)]
public float virtualOffsetThreshold = 0.75f;
/// Distance from the probe position used to determine the origin of the sampling ray.
[Range(-0.05f, 0f), Tooltip("Distance from the probe position used to determine the origin of the sampling ray.")]
public float rayOriginBias = -0.001f;
/// The sky direction.
[Tooltip("The direction for sampling the ambient probe in worldspace when using the Sky Visibility feature.")]
public Vector3 skyDirection = Vector3.zero;
internal Vector3 skyShadingDirectionRotation = Vector3.zero;
/// Number of samples for direct lighting computations.
[Logarithmic(1, 1024), Tooltip("Number of samples for direct lighting computations.")]
public int directSampleCount = 32;
/// Number of samples for indirect lighting computations. This includes environment samples.
[Logarithmic(1, 8192), Tooltip("Number of samples for indirect lighting computations. This includes environment samples.")]
public int indirectSampleCount = 512;
/// Multiplier for the number of samples specified above.
[Min(0), Tooltip("Multiplier for the number of samples specified above.")]
public int sampleCountMultiplier = 4;
/// Maximum number of bounces for indirect lighting.
[Min(0), Tooltip("Maximum number of bounces for indirect lighting.")]
public int maxBounces = 2;
/// Controls the number of samples per probe for sky occlusion baking.
[Logarithmic(1, ProbeVolumeBakingSet.k_MaxSkyOcclusionBakingSamples)]
public int skyOcclusionSampleCount = 2048;
/// Controls the number of bounces per light path for sky occlusion baking.
[Range(0, 5)]
public int skyOcclusionMaxBounces = 2;
/// Rendering Layer Mask operation.
public RenderingLayerMaskOperation renderingLayerMaskOperation;
/// Rendering layer mask used for the combine operation with the probes inside the volume.
public byte renderingLayerMask;
#if UNITY_EDITOR
[SerializeField] internal int cachedHashCode = 0;
public override int GetHashCode()
{
int hash = 17;
unchecked
{
hash = hash * 23 + gameObject.transform.worldToLocalMatrix.GetHashCode();
hash = hash * 23 + shape.GetHashCode();
hash = hash * 23 + size.GetHashCode();
hash = hash * 23 + radius.GetHashCode();
hash = hash * 23 + mode.GetHashCode();
hash = hash * 23 + intensityScale.GetHashCode();
hash = hash * 23 + overriddenDilationThreshold.GetHashCode();
hash = hash * 23 + virtualOffsetRotation.GetHashCode();
hash = hash * 23 + virtualOffsetDistance.GetHashCode();
hash = hash * 23 + geometryBias.GetHashCode();
hash = hash * 23 + rayOriginBias.GetHashCode();
hash = hash * 23 + skyDirection.GetHashCode();
hash = hash * 23 + skyShadingDirectionRotation.GetHashCode();
hash = hash * 23 + directSampleCount.GetHashCode();
hash = hash * 23 + indirectSampleCount.GetHashCode();
hash = hash * 23 + sampleCountMultiplier.GetHashCode();
hash = hash * 23 + maxBounces.GetHashCode();
hash = hash * 23 + skyOcclusionSampleCount.GetHashCode();
hash = hash * 23 + skyOcclusionMaxBounces.GetHashCode();
}
return hash;
}
#endif
#if UNITY_EDITOR
///
/// Returns the extents of the volume.
///
/// The extents of the ProbeVolume.
public Vector3 GetExtents()
{
return size;
}
internal void GetOBBandAABB(out ProbeReferenceVolume.Volume volume, out Bounds bounds)
{
if (shape == Shape.Box)
{
volume = new ProbeReferenceVolume.Volume(Matrix4x4.TRS(transform.position, transform.rotation, GetExtents()), 0, 0);
bounds = volume.CalculateAABB();
}
else
{
volume = default;
bounds = new Bounds(transform.position, radius * Vector3.up);
}
}
internal float ComputeVolume(in ProbeReferenceVolume.Volume touchupOBB)
{
if (shape == Shape.Box)
return touchupOBB.X.magnitude * touchupOBB.Y.magnitude * touchupOBB.Z.magnitude;
else
return (4.0f / 3.0f) * Mathf.PI * radius * radius * radius;
}
internal bool IntersectsVolume(in ProbeReferenceVolume.Volume touchupOBB, in Bounds touchupBounds, Bounds volumeBounds)
{
if (shape == Shape.Box)
return ProbeVolumePositioning.OBBAABBIntersect(touchupOBB, volumeBounds, touchupBounds);
else
return volumeBounds.SqrDistance(touchupBounds.center) < radius * radius;
}
internal bool ContainsPoint(in ProbeReferenceVolume.Volume touchupOBB, in Vector3 touchupCenter, in Vector3 position)
{
if (shape == Shape.Box)
return ProbeVolumePositioning.OBBContains(touchupOBB, position);
else
return (touchupCenter - position).sqrMagnitude < radius * radius;
}
internal Vector3 GetVirtualOffset()
{
if (mode != Mode.ApplyVirtualOffset)
return Vector3.zero;
return (transform.rotation * Quaternion.Euler(virtualOffsetRotation) * Vector3.forward) * virtualOffsetDistance;
}
void OnDrawGizmos()
{
Gizmos.DrawIcon(transform.position, ProbeVolume.s_gizmosLocationPath + "ProbeTouchupVolume.png", true);
}
#endif
// Migration related stuff
enum Version
{
Initial,
Mode,
Count
}
[SerializeField]
Version version = Version.Count;
/// Whether to invalidate all probes falling within this volume.
[Obsolete("Use mode")]
public bool invalidateProbes = false;
/// Whether to use a custom threshold for dilation for probes falling withing this volume.
[Obsolete("Use mode")]
public bool overrideDilationThreshold = false;
void Awake()
{
if (version == Version.Count)
return;
if (version == Version.Initial)
{
#pragma warning disable 618 // Type or member is obsolete
if (invalidateProbes)
mode = Mode.InvalidateProbes;
else if (overrideDilationThreshold)
mode = Mode.OverrideValidityThreshold;
#pragma warning restore 618
version++;
}
}
// This piece of code is needed because some objects could have been created before existence of Version enum
/// OnBeforeSerialize needed to handle migration before the versioning system was in place.
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
if (version == Version.Count) // serializing a newly created object
version = Version.Count - 1; // mark as up to date
}
/// OnAfterDeserialize needed to handle migration before the versioning system was in place.
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
if (version == Version.Count) // deserializing and object without version
version = Version.Initial; // reset to run the migration
}
}
}