226 lines
12 KiB
C#
226 lines
12 KiB
C#
using System;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
/// <summary>Utilities for <see cref="ProbeSettings"/></summary>
|
|
public static class ProbeSettingsUtilities
|
|
{
|
|
internal enum PositionMode
|
|
{
|
|
UseProbeTransform,
|
|
MirrorReferenceTransformWithProbePlane
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply <paramref name="settings"/> and <paramref name="probePosition"/> to
|
|
/// <paramref name="cameraPosition"/> and <paramref name="cameraSettings"/>.
|
|
/// </summary>
|
|
/// <param name="settings">Settings to apply. (Read only)</param>
|
|
/// <param name="probePosition">Position to apply. (Read only)</param>
|
|
/// <param name="cameraSettings">Settings to update.</param>
|
|
/// <param name="cameraPosition">Position to update.</param>
|
|
/// <param name="referenceFieldOfView">Reference field of view</param>
|
|
/// <param name="referenceAspect">Reference aspect ratio</param>
|
|
public static void ApplySettings(
|
|
ref ProbeSettings settings, // In Parameter
|
|
ref ProbeCapturePositionSettings probePosition, // In parameter
|
|
ref CameraSettings cameraSettings, // InOut parameter
|
|
ref CameraPositionSettings cameraPosition, // InOut parameter
|
|
float referenceFieldOfView = 90,
|
|
float referenceAspect = 1
|
|
)
|
|
{
|
|
cameraSettings = settings.cameraSettings;
|
|
// Compute the modes for each probe type
|
|
PositionMode positionMode;
|
|
bool useReferenceTransformAsNearClipPlane;
|
|
switch (settings.type)
|
|
{
|
|
case ProbeSettings.ProbeType.PlanarProbe:
|
|
positionMode = PositionMode.MirrorReferenceTransformWithProbePlane;
|
|
useReferenceTransformAsNearClipPlane = true;
|
|
ApplyPlanarFrustumHandling(
|
|
ref settings, ref probePosition,
|
|
ref cameraSettings, ref cameraPosition,
|
|
referenceFieldOfView, referenceAspect
|
|
);
|
|
break;
|
|
case ProbeSettings.ProbeType.ReflectionProbe:
|
|
positionMode = PositionMode.UseProbeTransform;
|
|
useReferenceTransformAsNearClipPlane = false;
|
|
cameraSettings.frustum.mode = CameraSettings.Frustum.Mode.ComputeProjectionMatrix;
|
|
cameraSettings.frustum.aspect = 1;
|
|
cameraSettings.frustum.fieldOfView = 90;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
// Update the position
|
|
switch (positionMode)
|
|
{
|
|
case PositionMode.UseProbeTransform:
|
|
{
|
|
cameraPosition.mode = CameraPositionSettings.Mode.ComputeWorldToCameraMatrix;
|
|
var proxyMatrix = Matrix4x4.TRS(probePosition.proxyPosition, probePosition.proxyRotation, Vector3.one);
|
|
cameraPosition.position = proxyMatrix.MultiplyPoint(settings.proxySettings.capturePositionProxySpace);
|
|
cameraPosition.rotation = proxyMatrix.rotation * settings.proxySettings.captureRotationProxySpace;
|
|
|
|
// In case of probe baking, 99% of the time, orientation of the cubemap doesn't matters
|
|
// so, we build one without any rotation, thus we don't have to change the basis
|
|
// during sampling the cubemap.
|
|
if (settings.type == ProbeSettings.ProbeType.ReflectionProbe)
|
|
cameraPosition.rotation = Quaternion.identity;
|
|
break;
|
|
}
|
|
case PositionMode.MirrorReferenceTransformWithProbePlane:
|
|
{
|
|
cameraPosition.mode = CameraPositionSettings.Mode.UseWorldToCameraMatrixField;
|
|
ApplyMirroredReferenceTransform(
|
|
ref settings, ref probePosition,
|
|
ref cameraSettings, ref cameraPosition
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update the clip plane
|
|
if (useReferenceTransformAsNearClipPlane)
|
|
{
|
|
ApplyObliqueNearClipPlane(
|
|
ref settings, ref probePosition,
|
|
ref cameraSettings, ref cameraPosition
|
|
);
|
|
}
|
|
|
|
// Propagate the desired custom exposure
|
|
cameraSettings.probeRangeCompressionFactor = settings.lighting.rangeCompressionFactor;
|
|
|
|
// Frame Settings Overrides
|
|
switch (settings.mode)
|
|
{
|
|
default:
|
|
case ProbeSettings.Mode.Realtime:
|
|
cameraSettings.defaultFrameSettings = FrameSettingsRenderType.RealtimeReflection;
|
|
break;
|
|
case ProbeSettings.Mode.Baked:
|
|
case ProbeSettings.Mode.Custom:
|
|
cameraSettings.defaultFrameSettings = FrameSettingsRenderType.CustomOrBakedReflection;
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal static void ApplyMirroredReferenceTransform(
|
|
ref ProbeSettings settings, // In Parameter
|
|
ref ProbeCapturePositionSettings probePosition, // In parameter
|
|
ref CameraSettings cameraSettings, // InOut parameter
|
|
ref CameraPositionSettings cameraPosition // InOut parameter
|
|
)
|
|
{
|
|
// Calculate mirror position and forward world space
|
|
var proxyMatrix = Matrix4x4.TRS(probePosition.proxyPosition, probePosition.proxyRotation, Vector3.one);
|
|
var mirrorPosition = proxyMatrix.MultiplyPoint(settings.proxySettings.mirrorPositionProxySpace);
|
|
var mirrorForward = proxyMatrix.MultiplyVector(settings.proxySettings.mirrorRotationProxySpace * Vector3.forward);
|
|
var reflectionMatrix = GeometryUtils.CalculateReflectionMatrix(mirrorPosition, mirrorForward);
|
|
|
|
// If the camera is on the reflection plane, we offset it by 0.1 mm to avoid a degenerate case.
|
|
if (Vector3.Dot(mirrorForward, probePosition.referencePosition - mirrorPosition) < 1e-4f)
|
|
probePosition.referencePosition += 1e-4f * mirrorForward;
|
|
|
|
var worldToCameraRHS = GeometryUtils.CalculateWorldToCameraMatrixRHS(
|
|
probePosition.referencePosition,
|
|
|
|
// TODO: The capture camera should look at a better direction to only capture texels that
|
|
// will actually be sampled.
|
|
// The position it should look at is the center of the visible influence volume of the probe.
|
|
// (visible influence volume: the intersection of the frustum with the probe's influence volume).
|
|
// But this is not trivial to get.
|
|
// So currently, only look in the mirrored direction of the reference. This will capture
|
|
// more pixels than we want with a lesser resolution, but still work for most cases.
|
|
|
|
// Note: looking at the center of the influence volume don't work in all cases (see case 1157921)
|
|
probePosition.referenceRotation
|
|
);
|
|
cameraPosition.worldToCameraMatrix = worldToCameraRHS * reflectionMatrix;
|
|
// We must invert the culling because we performed a plane reflection
|
|
cameraSettings.invertFaceCulling = true;
|
|
|
|
// Calculate capture position and rotation
|
|
cameraPosition.position = reflectionMatrix.MultiplyPoint(probePosition.referencePosition);
|
|
var forward = reflectionMatrix.MultiplyVector(probePosition.referenceRotation * Vector3.forward);
|
|
var up = reflectionMatrix.MultiplyVector(probePosition.referenceRotation * Vector3.up);
|
|
cameraPosition.rotation = Quaternion.LookRotation(forward, up);
|
|
}
|
|
|
|
internal static void ApplyPlanarFrustumHandling(
|
|
ref ProbeSettings settings, // In Parameter
|
|
ref ProbeCapturePositionSettings probePosition, // In parameter
|
|
ref CameraSettings cameraSettings, // InOut parameter
|
|
ref CameraPositionSettings cameraPosition, // InOut parameter
|
|
float referenceFieldOfView, float referenceAspect
|
|
)
|
|
{
|
|
const float k_MaxFieldOfView = 170;
|
|
|
|
var proxyMatrix = Matrix4x4.TRS(probePosition.proxyPosition, probePosition.proxyRotation, Vector3.one);
|
|
var mirrorPosition = proxyMatrix.MultiplyPoint(settings.proxySettings.mirrorPositionProxySpace);
|
|
|
|
cameraSettings.frustum.aspect = referenceAspect;
|
|
|
|
switch (settings.frustum.fieldOfViewMode)
|
|
{
|
|
case ProbeSettings.Frustum.FOVMode.Fixed:
|
|
cameraSettings.frustum.fieldOfView = settings.frustum.fixedValue;
|
|
break;
|
|
case ProbeSettings.Frustum.FOVMode.Viewer:
|
|
cameraSettings.frustum.fieldOfView = Mathf.Min(
|
|
referenceFieldOfView * settings.frustum.viewerScale,
|
|
k_MaxFieldOfView
|
|
);
|
|
break;
|
|
case ProbeSettings.Frustum.FOVMode.Automatic:
|
|
// Dynamic FOV tries to adapt the FOV to have maximum usage of the target render texture
|
|
// (A lot of pixel can be discarded in the render texture). This way we can have a greater
|
|
// resolution for the planar with the same cost.
|
|
cameraSettings.frustum.fieldOfView = Mathf.Min(
|
|
settings.influence.ComputeFOVAt(
|
|
probePosition.referencePosition, mirrorPosition, probePosition.influenceToWorld
|
|
) * settings.frustum.automaticScale,
|
|
k_MaxFieldOfView
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal static void ApplyObliqueNearClipPlane(
|
|
ref ProbeSettings settings, // In Parameter
|
|
ref ProbeCapturePositionSettings probePosition, // In parameter
|
|
ref CameraSettings cameraSettings, // InOut parameter
|
|
ref CameraPositionSettings cameraPosition // InOut parameter
|
|
)
|
|
{
|
|
var proxyMatrix = Matrix4x4.TRS(probePosition.proxyPosition, probePosition.proxyRotation, Vector3.one);
|
|
var mirrorPosition = proxyMatrix.MultiplyPoint(settings.proxySettings.mirrorPositionProxySpace);
|
|
var mirrorForward = proxyMatrix.MultiplyVector(settings.proxySettings.mirrorRotationProxySpace * Vector3.forward);
|
|
|
|
var clipPlaneCameraSpace = GeometryUtils.CameraSpacePlane(
|
|
cameraPosition.worldToCameraMatrix,
|
|
mirrorPosition,
|
|
mirrorForward
|
|
);
|
|
|
|
var sourceProjection = Matrix4x4.Perspective(
|
|
HDUtils.ClampFOV(cameraSettings.frustum.fieldOfView),
|
|
cameraSettings.frustum.aspect,
|
|
cameraSettings.frustum.nearClipPlane,
|
|
cameraSettings.frustum.farClipPlane
|
|
);
|
|
var obliqueProjection = GeometryUtils.CalculateObliqueMatrix(
|
|
sourceProjection, clipPlaneCameraSpace
|
|
);
|
|
cameraSettings.frustum.mode = CameraSettings.Frustum.Mode.UseProjectionMatrixField;
|
|
cameraSettings.frustum.projectionMatrix = obliqueProjection;
|
|
}
|
|
}
|
|
}
|