315 lines
13 KiB
C#
315 lines
13 KiB
C#
using System;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Runtime.ConstrainedExecution;
|
|
using System.Security.Cryptography;
|
|
using UnityEngine;
|
|
using UnityEngine.Experimental.Rendering.RenderGraphModule;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
[DisallowMultipleRendererFeature("Moment Based Order-Independent Transparency")]
|
|
public class MOITFeature : ScriptableRendererFeature
|
|
{
|
|
public enum MomentsCount
|
|
{
|
|
_4 = 4,
|
|
_6 = 6,
|
|
_8 = 8
|
|
}
|
|
|
|
public enum FloatPrecision
|
|
{
|
|
_Half = 16,
|
|
_Single = 32
|
|
}
|
|
|
|
public enum BoundsType
|
|
{
|
|
NearFarPlanes,
|
|
FindObjects,
|
|
Register
|
|
// ideally add JustIterateThroughTheRendererListHandle at some point
|
|
}
|
|
|
|
[Serializable]
|
|
class Settings
|
|
{
|
|
[SerializeField]
|
|
internal RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
|
|
[SerializeField]
|
|
internal MOITSettings settings;
|
|
[SerializeField]
|
|
internal MOITBias biasSettings;
|
|
}
|
|
|
|
[SerializeField]
|
|
Settings settings;
|
|
|
|
MOITPass pass;
|
|
|
|
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
|
{
|
|
var cam = renderingData.cameraData;
|
|
if (cam.cameraType != CameraType.Game && cam.cameraType != CameraType.SceneView)
|
|
{
|
|
return;
|
|
}
|
|
|
|
renderer.EnqueuePass(pass);
|
|
}
|
|
|
|
public override void Create()
|
|
{
|
|
pass = new(settings);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
pass.Dispose();
|
|
}
|
|
|
|
class MOITPass : ScriptableRenderPass, IDisposable
|
|
{
|
|
private static readonly int b0TextureID = Shader.PropertyToID("_B0");
|
|
private static readonly int b1TextureID = Shader.PropertyToID("_B1");
|
|
private static readonly int b2TextureID = Shader.PropertyToID("_B2");
|
|
private static readonly int logViewMinDeltaID = Shader.PropertyToID("_LogViewDepthMinDelta");
|
|
private static readonly int wrappingZoneParametersID = Shader.PropertyToID("_WrappingZoneParameters");
|
|
private static readonly int biasID = Shader.PropertyToID("_MOIT_MomentBias");
|
|
private static readonly int alphaToMaskAvailableID = Shader.PropertyToID("_AlphaToMaskAvailable");
|
|
|
|
const string generatePassName = "MOIT Generate Moments Pass";
|
|
const string resolvePassName = "MOIT Resolve Moments Pass";
|
|
const string compositePassName = "MOIT Composite Pass";
|
|
|
|
private static readonly int scaleBiasRt = Shader.PropertyToID("_ScaleBiasRt");
|
|
|
|
|
|
private Settings featureSettings;
|
|
private ProfilingSampler profiler;
|
|
private CommandBuffer commandBuffer;
|
|
|
|
public MOITPass(Settings settings)
|
|
{
|
|
this.featureSettings = settings;
|
|
renderPassEvent = settings.renderPassEvent;
|
|
profiler = new(nameof(MOITPass));
|
|
commandBuffer = new CommandBuffer();
|
|
commandBuffer.name = profiler.name;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
RTHandle moitHandle;
|
|
RTHandle b0;
|
|
RTHandle b1;
|
|
RTHandle b2;
|
|
private ShaderTagId shaderTagIdGenerateMoments = new ShaderTagId("GenerateMoments");
|
|
private ShaderTagId shaderTagIdResolveMoments = new ShaderTagId("ResolveMoments");
|
|
|
|
RenderTargetIdentifier[] mrts2 = new RenderTargetIdentifier[2];
|
|
RenderTargetIdentifier[] mrts3 = new RenderTargetIdentifier[3];
|
|
|
|
private const float M_PI = 3.14159265358979323f;
|
|
|
|
private static float CircleToParameter(float angle, out float maxParameter)
|
|
{
|
|
float x = Mathf.Cos(angle);
|
|
float y = Mathf.Sin(angle);
|
|
float result = Mathf.Abs(y) - Mathf.Abs(x);
|
|
result = (x < 0.0f) ? (2.0f - result) : result;
|
|
result = (y < 0.0f) ? (6.0f - result) : result;
|
|
result += (angle >= 2.0f * M_PI) ? 8.0f : 0.0f;
|
|
maxParameter = 7.0f; // why?
|
|
return result;
|
|
}
|
|
|
|
public static Vector4 ComputeWrappingZoneParameters(float newWrappingZoneAngle = 0.1f * M_PI)
|
|
{
|
|
Vector4 result = new Vector4();
|
|
result.x = newWrappingZoneAngle;
|
|
result.y = M_PI - 0.5f * newWrappingZoneAngle;
|
|
if (newWrappingZoneAngle <= 0.0f)
|
|
{
|
|
result.z = 0.0f;
|
|
result.w = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
float zoneEndParameter;
|
|
float zoneBeginParameter = CircleToParameter(2.0f * M_PI - newWrappingZoneAngle, out zoneEndParameter);
|
|
result.z = 1.0f / (zoneEndParameter - zoneBeginParameter);
|
|
result.w = 1.0f - zoneEndParameter * result.z;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Vector4 wrappingZoneParameters = ComputeWrappingZoneParameters();
|
|
|
|
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
|
{
|
|
var biasSettings = featureSettings.biasSettings;
|
|
if(biasSettings == null )
|
|
{
|
|
return;
|
|
}
|
|
var settings = featureSettings.settings;
|
|
bool isHalfPrecision = settings.momentPrecision == FloatPrecision._Half;
|
|
// prevent the use of half precision power moments as quantization relies on ROVs
|
|
if (isHalfPrecision && !settings.trigonometric)
|
|
isHalfPrecision = false;
|
|
|
|
var cmd = commandBuffer;
|
|
using var scp = new ProfilingScope(cmd, profiler);
|
|
var rdr = renderingData.cameraData.renderer;
|
|
ref var cameraData = ref renderingData.cameraData;
|
|
RenderTextureDescriptor baseDescriptor = cameraData.cameraTargetDescriptor;
|
|
baseDescriptor.colorFormat = RenderTextureFormat.ARGBHalf;
|
|
|
|
RenderingUtils.ReAllocateIfNeeded(ref moitHandle, baseDescriptor, name: "_MOIT_Texture");
|
|
|
|
RenderTextureDescriptor descriptorFloat4;
|
|
RenderTextureDescriptor descriptorFloat2;
|
|
RenderTextureDescriptor descriptorFloat;
|
|
if (isHalfPrecision)
|
|
{
|
|
baseDescriptor.colorFormat = RenderTextureFormat.ARGBHalf;
|
|
descriptorFloat4 = baseDescriptor;
|
|
baseDescriptor.colorFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGHalf) ? RenderTextureFormat.RGHalf : RenderTextureFormat.ARGBHalf;
|
|
descriptorFloat2 = baseDescriptor;
|
|
baseDescriptor.colorFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RHalf) ? RenderTextureFormat.RHalf : RenderTextureFormat.ARGBHalf;
|
|
descriptorFloat = baseDescriptor;
|
|
}
|
|
else // single precision
|
|
{
|
|
baseDescriptor.colorFormat = RenderTextureFormat.ARGBFloat;
|
|
descriptorFloat4 = baseDescriptor;
|
|
baseDescriptor.colorFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGFloat) ? RenderTextureFormat.RGFloat : RenderTextureFormat.ARGBFloat;
|
|
descriptorFloat2 = baseDescriptor;
|
|
baseDescriptor.colorFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RFloat) ? RenderTextureFormat.RFloat : RenderTextureFormat.ARGBFloat;
|
|
descriptorFloat = baseDescriptor;
|
|
}
|
|
|
|
FilterMode filterMode = FilterMode.Bilinear;
|
|
|
|
RenderingUtils.ReAllocateIfNeeded(ref b0, descriptorFloat, name: "_MOIT_B0", filterMode: filterMode);
|
|
|
|
RenderingUtils.ReAllocateIfNeeded(ref b1, descriptorFloat4, name: "_MOIT_B1", filterMode: filterMode);
|
|
|
|
if (settings.momentsCount == MomentsCount._8)
|
|
{
|
|
RenderingUtils.ReAllocateIfNeeded(ref b1, descriptorFloat4, name: "_MOIT_B2", filterMode: filterMode);
|
|
|
|
}
|
|
else if (settings.momentsCount == MomentsCount._6)
|
|
{
|
|
RenderingUtils.ReAllocateIfNeeded(ref b1, descriptorFloat2, name: "_MOIT_B2", filterMode: filterMode);
|
|
}
|
|
|
|
SortingCriteria sortingCritera = settings.sortBackToFront ? SortingCriteria.BackToFront | SortingCriteria.OptimizeStateChanges : SortingCriteria.OptimizeStateChanges;
|
|
|
|
float momentBias = 0;
|
|
Vector2 viewDepthMinMax = Vector2.zero;
|
|
if (isHalfPrecision)
|
|
{
|
|
if (settings.momentsCount == MomentsCount._4)
|
|
momentBias = settings.trigonometric ? biasSettings.Trigonometric2Half : biasSettings.Moments4Half;
|
|
else if (settings.momentsCount == MomentsCount._6)
|
|
momentBias = settings.trigonometric ? biasSettings.Trigonometric3Half : biasSettings.Moments6Half;
|
|
else
|
|
momentBias = settings.trigonometric ? biasSettings.Trigonometric4Half : biasSettings.Moments8Half;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (settings.momentsCount == MomentsCount._4)
|
|
momentBias = settings.trigonometric ? biasSettings.Trigonometric2Single : biasSettings.Moments4Single;
|
|
else if (settings.momentsCount == MomentsCount._6)
|
|
momentBias = settings.trigonometric ? biasSettings.Trigonometric3Single : biasSettings.Moments6Single;
|
|
else
|
|
momentBias = settings.trigonometric ? biasSettings.Trigonometric4Single : biasSettings.Moments8Single;
|
|
}
|
|
|
|
DrawingSettings drawSettings = CreateDrawingSettings(shaderTagIdGenerateMoments, ref renderingData, sortingCritera);
|
|
|
|
cmd.BeginSample("GenerateMoments");
|
|
mrts2[0] = mrts3[0] = b0;
|
|
mrts2[1] = mrts3[1] = b1;
|
|
cmd.SetGlobalTexture(b0TextureID, b0);
|
|
cmd.SetGlobalTexture(b1TextureID, b1);
|
|
if (settings.momentsCount != MomentsCount._4)
|
|
{
|
|
mrts3[2] = b2;
|
|
cmd.SetRenderTarget(mrts3, rdr.cameraDepthTargetHandle);
|
|
cmd.SetGlobalTexture(b2TextureID, b2);
|
|
}
|
|
else
|
|
{
|
|
cmd.SetRenderTarget(mrts2, rdr.cameraDepthTargetHandle);
|
|
}
|
|
|
|
var isYFlipped = cameraData.IsRenderTargetProjectionMatrixFlipped(rdr.cameraColorTargetHandle);
|
|
float flipSign = isYFlipped ? -1.0f : 1.0f;
|
|
// scaleBias.x = flipSign
|
|
// scaleBias.y = scale
|
|
// scaleBias.z = bias
|
|
// scaleBias.w = unused
|
|
Vector4 scaleBias = (flipSign < 0.0f)
|
|
? new Vector4(flipSign, 1.0f, -1.0f, 1.0f)
|
|
: new Vector4(flipSign, 0.0f, 1.0f, 1.0f);
|
|
cmd.SetGlobalVector(scaleBiasRt, scaleBias);
|
|
|
|
// setup keywords
|
|
CoreUtils.SetKeyword(cmd, "_MOMENT6", settings.momentsCount == MomentsCount._6);
|
|
CoreUtils.SetKeyword(cmd, "_MOMENT8", settings.momentsCount == MomentsCount._8);
|
|
//CoreUtils.SetKeyword(cmd, "_MOMENT_HALF_PRECISION", data.momentsPrecision == FloatPrecision._Half);
|
|
CoreUtils.SetKeyword(cmd, "_MOMENT_HALF_PRECISION", isHalfPrecision);
|
|
CoreUtils.SetKeyword(cmd, "_MOMENT_SINGLE_PRECISION", !isHalfPrecision);
|
|
CoreUtils.SetKeyword(cmd, "_TRIGONOMETRIC", settings.trigonometric);
|
|
|
|
Vector2 logViewDepthMinDelta = new Vector2(Mathf.Log(viewDepthMinMax.x), Mathf.Log(viewDepthMinMax.y));
|
|
logViewDepthMinDelta.y = logViewDepthMinDelta.y - logViewDepthMinDelta.x;
|
|
cmd.SetGlobalVector(logViewMinDeltaID, logViewDepthMinDelta);
|
|
|
|
if (settings.trigonometric)
|
|
{
|
|
cmd.SetGlobalVector(wrappingZoneParametersID, wrappingZoneParameters);
|
|
}
|
|
cmd.SetGlobalFloat(biasID, momentBias);
|
|
cmd.SetGlobalFloat(alphaToMaskAvailableID, 0.0f);
|
|
cmd.ClearRenderTarget(false, true, Color.clear);
|
|
var renderQueue = new RenderQueueRange()
|
|
{
|
|
lowerBound = settings.renderQueueMin,
|
|
upperBound = settings.renderQueueMax
|
|
};
|
|
var param = new RendererListParams(renderingData.cullResults, drawSettings, new FilteringSettings(renderQueue, settings.layerMask));
|
|
cmd.DrawRendererList(context.CreateRendererList(ref param));
|
|
cmd.EndSample("GenerateMoments");
|
|
|
|
cmd.BeginSample("ResolveMoments");
|
|
if (settings.debugMakeMOITTexGlobal)
|
|
{
|
|
cmd.SetGlobalTexture("_MOIT", moitHandle);
|
|
}
|
|
cmd.SetRenderTarget(moitHandle, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare);
|
|
cmd.ClearRenderTarget(false, true, Color.clear);
|
|
|
|
drawSettings = CreateDrawingSettings(shaderTagIdResolveMoments, ref renderingData, sortingCritera);
|
|
param = new RendererListParams(renderingData.cullResults, drawSettings, new FilteringSettings(renderQueue, settings.layerMask));
|
|
cmd.DrawRendererList(context.CreateRendererList(ref param));
|
|
cmd.EndSample("ResolveMoments");
|
|
|
|
cmd.BeginSample("Composite");
|
|
cmd.Blit(moitHandle, rdr.cameraColorTargetHandle, settings.compositeMaterial, 0);
|
|
cmd.EndSample("Composite");
|
|
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Clear();
|
|
}
|
|
}
|
|
}
|