using System;
using System.Diagnostics;
namespace UnityEngine.Rendering.HighDefinition
{
///
/// The model used to control the complexity of the simulation.
///
public enum PhysicallyBasedSkyModel
{
/// Suitable to simulate Earth
EarthSimple,
/// Suitable to simulate Earth
EarthAdvanced,
/// Suitable to simulate any planet
Custom
};
///
/// Physically Based Sky model volume parameter.
///
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public sealed class PhysicallyBasedSkyModelParameter : VolumeParameter
{
///
/// constructor.
///
/// Model parameter.
/// Initial override state.
public PhysicallyBasedSkyModelParameter(PhysicallyBasedSkyModel value, bool overrideState = false)
: base(value, overrideState) {}
}
///
/// Physically Based Sky Volume Component.
///
[VolumeComponentMenu("Sky/Physically Based Sky")]
[SkyUniqueID((int)SkyType.PhysicallyBased)]
[HelpURL(Documentation.baseURL + Documentation.version + Documentation.subURL + "Override-Physically-Based-Sky" + Documentation.endURL)]
public partial class PhysicallyBasedSky : SkySettings
{
/* We use the measurements from Earth as the defaults. */
const float k_DefaultEarthRadius = 6.3781f * 1000000;
const float k_DefaultAirScatteringR = 5.8f / 1000000; // at 680 nm, without ozone
const float k_DefaultAirScatteringG = 13.5f / 1000000; // at 550 nm, without ozone
const float k_DefaultAirScatteringB = 33.1f / 1000000; // at 440 nm, without ozone
const float k_DefaultAirScaleHeight = 8000;
const float k_DefaultAirAlbedoR = 0.9f; // BS values to account for absorption
const float k_DefaultAirAlbedoG = 0.9f; // due to the ozone layer. We assume that ozone
const float k_DefaultAirAlbedoB = 1.0f; // has the same height distribution as air (most certainly WRONG).
const float k_DefaultAerosolScaleHeight = 1200;
static readonly float k_DefaultAerosolMaximumAltitude = LayerDepthFromScaleHeight(k_DefaultAerosolScaleHeight);
/// Simplifies the interface by reducing the number of parameters available.
public PhysicallyBasedSkyModelParameter type = new PhysicallyBasedSkyModelParameter(PhysicallyBasedSkyModel.EarthAdvanced);
/// Allows to specify the location of the planet. If disabled, the planet is always below the camera in the world-space X-Z plane.
[Tooltip("When enabled, you can define the planet in terms of a world-space position and radius. Otherwise, the planet is always below the Camera in the world-space x-z plane.")]
public BoolParameter sphericalMode = new BoolParameter(true);
/// World-space Y coordinate of the sea level of the planet. Units: meters.
[Tooltip("Sets the world-space y coordinate of the planet's sea level in meters.")]
public FloatParameter seaLevel = new FloatParameter(0);
/// Radius of the planet (distance from the center of the planet to the sea level). Units: meters.
[Tooltip("Sets the radius of the planet in meters. This is distance from the center of the planet to the sea level.")]
public MinFloatParameter planetaryRadius = new MinFloatParameter(k_DefaultEarthRadius, 0);
/// Position of the center of the planet in the world space. Units: meters. Does not affect the precomputation.
[Tooltip("Sets the world-space position of the planet's center in meters.")]
public Vector3Parameter planetCenterPosition = new Vector3Parameter(new Vector3(0, -k_DefaultEarthRadius, 0));
/// Opacity (per color channel) of air as measured by an observer on the ground looking towards the zenith.
[Tooltip("Controls the red color channel opacity of air at the point in the sky directly above the observer (zenith).")]
public ClampedFloatParameter airDensityR = new ClampedFloatParameter(ZenithOpacityFromExtinctionAndScaleHeight(k_DefaultAirScatteringR, k_DefaultAirScaleHeight), 0, 1);
/// Opacity (per color channel) of air as measured by an observer on the ground looking towards the zenith.
[Tooltip("Controls the green color channel opacity of air at the point in the sky directly above the observer (zenith).")]
public ClampedFloatParameter airDensityG = new ClampedFloatParameter(ZenithOpacityFromExtinctionAndScaleHeight(k_DefaultAirScatteringG, k_DefaultAirScaleHeight), 0, 1);
/// Opacity (per color channel) of air as measured by an observer on the ground looking towards the zenith.
[Tooltip("Controls the blue color channel opacity of air at the point in the sky directly above the observer (zenith).")]
public ClampedFloatParameter airDensityB = new ClampedFloatParameter(ZenithOpacityFromExtinctionAndScaleHeight(k_DefaultAirScatteringB, k_DefaultAirScaleHeight), 0, 1);
/// Single scattering albedo of air molecules (per color channel). The value of 0 results in absorbing molecules, and the value of 1 results in scattering ones.
[Tooltip("Specifies the color that HDRP tints the air to. This controls the single scattering albedo of air molecules (per color channel). A value of 0 results in absorbing molecules, and a value of 1 results in scattering ones.")]
public ColorParameter airTint = new ColorParameter(new Color(k_DefaultAirAlbedoR, k_DefaultAirAlbedoG, k_DefaultAirAlbedoB), hdr: false, showAlpha: false, showEyeDropper: true);
/// Depth of the atmospheric layer (from the sea level) composed of air particles. Controls the rate of height-based density falloff. Units: meters.
[Tooltip("Sets the depth, in meters, of the atmospheric layer, from sea level, composed of air particles. Controls the rate of height-based density falloff.")]
// We assume the exponential falloff of density w.r.t. the height.
// We can interpret the depth as the height at which the density drops to 0.1% of the initial (sea level) value.
public MinFloatParameter airMaximumAltitude = new MinFloatParameter(LayerDepthFromScaleHeight(k_DefaultAirScaleHeight), 0);
/// Opacity of aerosols as measured by an observer on the ground looking towards the zenith.
[Tooltip("Controls the opacity of aerosols at the point in the sky directly above the observer (zenith).")]
// Note: aerosols are (fairly large) solid or liquid particles suspended in the air.
public ClampedFloatParameter aerosolDensity = new ClampedFloatParameter(ZenithOpacityFromExtinctionAndScaleHeight(10.0f / 1000000, k_DefaultAerosolScaleHeight), 0, 1);
/// Single scattering albedo of aerosol molecules (per color channel). The value of 0 results in absorbing molecules, and the value of 1 results in scattering ones.
[Tooltip("Specifies the color that HDRP tints aerosols to. This controls the single scattering albedo of aerosol molecules (per color channel). A value of 0 results in absorbing molecules, and a value of 1 results in scattering ones.")]
public ColorParameter aerosolTint = new ColorParameter(new Color(0.9f, 0.9f, 0.9f), hdr: false, showAlpha: false, showEyeDropper: true);
/// Depth of the atmospheric layer (from the sea level) composed of aerosol particles. Controls the rate of height-based density falloff. Units: meters.
[Tooltip("Sets the depth, in meters, of the atmospheric layer, from sea level, composed of aerosol particles. Controls the rate of height-based density falloff.")]
// We assume the exponential falloff of density w.r.t. the height.
// We can interpret the depth as the height at which the density drops to 0.1% of the initial (sea level) value.
public MinFloatParameter aerosolMaximumAltitude = new MinFloatParameter(k_DefaultAerosolMaximumAltitude, 0);
/// Positive values for forward scattering, 0 for isotropic scattering. negative values for backward scattering.
[Tooltip("Controls the direction of anisotropy. Set this to a positive value for forward scattering, a negative value for backward scattering, or 0 for isotropic scattering.")]
public ClampedFloatParameter aerosolAnisotropy = new ClampedFloatParameter(0, -1, 1);
/// Number of scattering events.
[Tooltip("Sets the number of scattering events. This increases the quality of the sky visuals but also increases the pre-computation time.")]
public ClampedIntParameter numberOfBounces = new ClampedIntParameter(8, 1, 10);
/// Ground tint.
[Tooltip("Specifies a color that HDRP uses to tint the Ground Color Texture.")]
public ColorParameter groundTint = new ColorParameter(new Color(0.4f, 0.25f, 0.15f), hdr: false, showAlpha: false, showEyeDropper: false);
/// Ground color texture. Does not affect the precomputation.
[Tooltip("Specifies a Texture that represents the planet's surface. Does not affect the precomputation.")]
public CubemapParameter groundColorTexture = new CubemapParameter(null);
/// Ground emission texture. Does not affect the precomputation.
[Tooltip("Specifies a Texture that represents the emissive areas of the planet's surface. Does not affect the precomputation.")]
public CubemapParameter groundEmissionTexture = new CubemapParameter(null);
/// Ground emission multiplier. Does not affect the precomputation.
[Tooltip("Sets the multiplier that HDRP applies to the Ground Emission Texture.")]
public MinFloatParameter groundEmissionMultiplier = new MinFloatParameter(1, 0);
/// Rotation of the planet. Does not affect the precomputation.
[Tooltip("Sets the orientation of the planet. Does not affect the precomputation.")]
public Vector3Parameter planetRotation = new Vector3Parameter(Vector3.zero);
/// Space emission texture. Does not affect the precomputation.
[Tooltip("Specifies a Texture that represents the emissive areas of space. Does not affect the precomputation.")]
public CubemapParameter spaceEmissionTexture = new CubemapParameter(null);
/// Space emission multiplier. Does not affect the precomputation.
[Tooltip("Sets the multiplier that HDRP applies to the Space Emission Texture. Does not affect the precomputation.")]
public MinFloatParameter spaceEmissionMultiplier = new MinFloatParameter(1, 0);
/// Rotation of space. Does not affect the precomputation.
[Tooltip("Sets the orientation of space. Does not affect the precomputation.")]
public Vector3Parameter spaceRotation = new Vector3Parameter(Vector3.zero);
/// Color saturation. Does not affect the precomputation.
[Tooltip("Controls the saturation of the sky color. Does not affect the precomputation.")]
public ClampedFloatParameter colorSaturation = new ClampedFloatParameter(1, 0, 1);
/// Opacity saturation. Does not affect the precomputation.
[Tooltip("Controls the saturation of the sky opacity. Does not affect the precomputation.")]
public ClampedFloatParameter alphaSaturation = new ClampedFloatParameter(1, 0, 1);
/// Opacity multiplier. Does not affect the precomputation.
[Tooltip("Sets the multiplier that HDRP applies to the opacity of the sky. Does not affect the precomputation.")]
public ClampedFloatParameter alphaMultiplier = new ClampedFloatParameter(1, 0, 1);
/// Horizon tint. Does not affect the precomputation.
[Tooltip("Specifies a color that HDRP uses to tint the sky at the horizon. Does not affect the precomputation.")]
public ColorParameter horizonTint = new ColorParameter(Color.white, hdr: false, showAlpha: false, showEyeDropper: false);
/// Zenith tint. Does not affect the precomputation.
[Tooltip("Specifies a color that HDRP uses to tint the point in the sky directly above the observer (the zenith). Does not affect the precomputation.")]
public ColorParameter zenithTint = new ColorParameter(Color.white, hdr: false, showAlpha: false, showEyeDropper: false);
/// Horizon-zenith shift. Does not affect the precomputation.
[Tooltip("Controls how HDRP blends between the Horizon Tint and Zenith Tint. Does not affect the precomputation.")]
public ClampedFloatParameter horizonZenithShift = new ClampedFloatParameter(0, -1, 1);
static internal float ScaleHeightFromLayerDepth(float d)
{
// Exp[-d / H] = 0.001
// -d / H = Log[0.001]
// H = d / -Log[0.001]
return d * 0.144765f;
}
static internal float LayerDepthFromScaleHeight(float H)
{
return H / 0.144765f;
}
static internal float ExtinctionFromZenithOpacityAndScaleHeight(float alpha, float H)
{
float opacity = Mathf.Min(alpha, 0.999999f);
float optDepth = -Mathf.Log(1 - opacity, 2.71828183f); // product of extinction and H
return optDepth / H;
}
static internal float ZenithOpacityFromExtinctionAndScaleHeight(float ext, float H)
{
float optDepth = ext * H;
return 1 - Mathf.Exp(-optDepth);
}
internal float GetAirScaleHeight()
{
if (type.value != PhysicallyBasedSkyModel.Custom)
{
return k_DefaultAirScaleHeight;
}
else
{
return ScaleHeightFromLayerDepth(airMaximumAltitude.value);
}
}
internal float GetMaximumAltitude()
{
if (type.value == PhysicallyBasedSkyModel.Custom)
return Mathf.Max(airMaximumAltitude.value, aerosolMaximumAltitude.value);
float aerosolMaxAltitude = (type.value == PhysicallyBasedSkyModel.EarthSimple) ? k_DefaultAerosolMaximumAltitude : aerosolMaximumAltitude.value;
return Mathf.Max(LayerDepthFromScaleHeight(k_DefaultAirScaleHeight), aerosolMaxAltitude);
}
internal float GetPlanetaryRadius()
{
if (type.value != PhysicallyBasedSkyModel.Custom)
{
return k_DefaultEarthRadius;
}
else
{
return planetaryRadius.value;
}
}
internal Vector3 GetPlanetCenterPosition(Vector3 camPosWS)
{
if (sphericalMode.value && (type.value != PhysicallyBasedSkyModel.EarthSimple))
{
return planetCenterPosition.value;
}
else // Planar mode
{
float R = GetPlanetaryRadius();
float h = seaLevel.value;
return new Vector3(camPosWS.x, -R + h, camPosWS.z);
}
}
internal Vector3 GetAirExtinctionCoefficient()
{
Vector3 airExt = new Vector3();
if (type.value != PhysicallyBasedSkyModel.Custom)
{
airExt.x = k_DefaultAirScatteringR;
airExt.y = k_DefaultAirScatteringG;
airExt.z = k_DefaultAirScatteringB;
}
else
{
airExt.x = ExtinctionFromZenithOpacityAndScaleHeight(airDensityR.value, GetAirScaleHeight());
airExt.y = ExtinctionFromZenithOpacityAndScaleHeight(airDensityG.value, GetAirScaleHeight());
airExt.z = ExtinctionFromZenithOpacityAndScaleHeight(airDensityB.value, GetAirScaleHeight());
}
return airExt;
}
internal Vector3 GetAirAlbedo()
{
Vector3 airAlb = new Vector3();
if (type.value != PhysicallyBasedSkyModel.Custom)
{
airAlb.x = k_DefaultAirAlbedoR;
airAlb.y = k_DefaultAirAlbedoG;
airAlb.z = k_DefaultAirAlbedoB;
}
else
{
airAlb.x = airTint.value.r;
airAlb.y = airTint.value.g;
airAlb.z = airTint.value.b;
}
return airAlb;
}
internal Vector3 GetAirScatteringCoefficient()
{
Vector3 airExt = GetAirExtinctionCoefficient();
Vector3 airAlb = GetAirAlbedo();
return new Vector3(airExt.x * airAlb.x,
airExt.y * airAlb.y,
airExt.z * airAlb.z);
}
internal float GetAerosolScaleHeight()
{
if (type.value == PhysicallyBasedSkyModel.EarthSimple)
{
return k_DefaultAerosolScaleHeight;
}
else
{
return ScaleHeightFromLayerDepth(aerosolMaximumAltitude.value);
}
}
internal float GetAerosolAnisotropy()
{
if (type.value == PhysicallyBasedSkyModel.EarthSimple)
{
return 0;
}
else
{
return aerosolAnisotropy.value;
}
}
internal float GetAerosolExtinctionCoefficient()
{
return ExtinctionFromZenithOpacityAndScaleHeight(aerosolDensity.value, GetAerosolScaleHeight());
}
internal Vector3 GetAerosolScatteringCoefficient()
{
float aerExt = GetAerosolExtinctionCoefficient();
return new Vector3(aerExt * aerosolTint.value.r,
aerExt * aerosolTint.value.g,
aerExt * aerosolTint.value.b);
}
PhysicallyBasedSky()
{
displayName = "Physically Based Sky";
}
internal int GetPrecomputationHashCode()
{
int hash = base.GetHashCode();
unchecked
{
#if UNITY_2019_3 // In 2019.3, when we call GetHashCode on a VolumeParameter it generate garbage (due to the boxing of the generic parameter)
// These parameters affect precomputation.
hash = hash * 23 + type.overrideState.GetHashCode();
hash = hash * 23 + planetaryRadius.overrideState.GetHashCode();
hash = hash * 23 + groundTint.overrideState.GetHashCode();
hash = hash * 23 + airMaximumAltitude.overrideState.GetHashCode();
hash = hash * 23 + airDensityR.overrideState.GetHashCode();
hash = hash * 23 + airDensityG.overrideState.GetHashCode();
hash = hash * 23 + airDensityB.overrideState.GetHashCode();
hash = hash * 23 + airTint.overrideState.GetHashCode();
hash = hash * 23 + aerosolMaximumAltitude.overrideState.GetHashCode();
hash = hash * 23 + aerosolDensity.overrideState.GetHashCode();
hash = hash * 23 + aerosolTint.overrideState.GetHashCode();
hash = hash * 23 + aerosolAnisotropy.overrideState.GetHashCode();
hash = hash * 23 + numberOfBounces.overrideState.GetHashCode();
#else
// These parameters affect precomputation.
hash = hash * 23 + type.GetHashCode();
hash = hash * 23 + planetaryRadius.GetHashCode();
hash = hash * 23 + groundTint.GetHashCode();
hash = hash * 23 + airMaximumAltitude.GetHashCode();
hash = hash * 23 + airDensityR.GetHashCode();
hash = hash * 23 + airDensityG.GetHashCode();
hash = hash * 23 + airDensityB.GetHashCode();
hash = hash * 23 + airTint.GetHashCode();
hash = hash * 23 + aerosolMaximumAltitude.GetHashCode();
hash = hash * 23 + aerosolDensity.GetHashCode();
hash = hash * 23 + aerosolTint.GetHashCode();
hash = hash * 23 + aerosolAnisotropy.GetHashCode();
hash = hash * 23 + numberOfBounces.GetHashCode();
#endif
}
return hash;
}
///
/// Returns the hash code of the sky parameters.
///
/// The camera we want to use to compute the hash of the sky.
/// The hash code of the sky parameters.
public override int GetHashCode(Camera camera)
{
int hash = GetHashCode();
Vector3 cameraLocation = camera.transform.position;
float r = Vector3.Distance(cameraLocation, GetPlanetCenterPosition(cameraLocation));
float R = GetPlanetaryRadius();
bool isPbrSkyActive = r > R; // Disable sky rendering below the ground
hash = hash * 23 + isPbrSkyActive.GetHashCode();
return hash;
}
/// Returns the hash code of the parameters of the sky.
/// The hash code of the parameters of the sky.
public override int GetHashCode()
{
int hash = GetPrecomputationHashCode();
unchecked
{
#if UNITY_2019_3 // In 2019.3, when we call GetHashCode on a VolumeParameter it generate garbage (due to the boxing of the generic parameter)
// These parameters do NOT affect precomputation.
hash = hash * 23 + sphericalMode.overrideState.GetHashCode();
hash = hash * 23 + seaLevel.overrideState.GetHashCode();
hash = hash * 23 + planetCenterPosition.overrideState.GetHashCode();
hash = hash * 23 + planetRotation.overrideState.GetHashCode();
if (groundColorTexture.value != null)
hash = hash * 23 + groundColorTexture.overrideState.GetHashCode();
if (groundEmissionTexture.value != null)
hash = hash * 23 + groundEmissionTexture.overrideState.GetHashCode();
hash = hash * 23 + groundEmissionMultiplier.overrideState.GetHashCode();
hash = hash * 23 + spaceRotation.overrideState.GetHashCode();
if (spaceEmissionTexture.value != null)
hash = hash * 23 + spaceEmissionTexture.overrideState.GetHashCode();
hash = hash * 23 + spaceEmissionMultiplier.overrideState.GetHashCode();
hash = hash * 23 + colorSaturation.overrideState.GetHashCode();
hash = hash * 23 + alphaSaturation.overrideState.GetHashCode();
hash = hash * 23 + alphaMultiplier.overrideState.GetHashCode();
hash = hash * 23 + horizonTint.overrideState.GetHashCode();
hash = hash * 23 + zenithTint.overrideState.GetHashCode();
hash = hash * 23 + horizonZenithShift.overrideState.GetHashCode();
#else
// These parameters do NOT affect precomputation.
hash = hash * 23 + sphericalMode.GetHashCode();
hash = hash * 23 + seaLevel.GetHashCode();
hash = hash * 23 + planetCenterPosition.GetHashCode();
hash = hash * 23 + planetRotation.GetHashCode();
if (groundColorTexture.value != null)
hash = hash * 23 + groundColorTexture.GetHashCode();
if (groundEmissionTexture.value != null)
hash = hash * 23 + groundEmissionTexture.GetHashCode();
hash = hash * 23 + groundEmissionMultiplier.GetHashCode();
hash = hash * 23 + spaceRotation.GetHashCode();
if (spaceEmissionTexture.value != null)
hash = hash * 23 + spaceEmissionTexture.GetHashCode();
hash = hash * 23 + spaceEmissionMultiplier.GetHashCode();
hash = hash * 23 + colorSaturation.GetHashCode();
hash = hash * 23 + alphaSaturation.GetHashCode();
hash = hash * 23 + alphaMultiplier.GetHashCode();
hash = hash * 23 + horizonTint.GetHashCode();
hash = hash * 23 + zenithTint.GetHashCode();
hash = hash * 23 + horizonZenithShift.GetHashCode();
#endif
}
return hash;
}
/// Returns the type of the sky renderer.
/// PhysicallyBasedSkyRenderer type.
public override Type GetSkyRendererType() { return typeof(PhysicallyBasedSkyRenderer); }
}
}