This commit is contained in:
StarBeats 2026-02-02 21:07:49 +08:00
parent 5d3b01ec25
commit 0bbac083b5
27 changed files with 1907 additions and 11 deletions

View File

@ -21,7 +21,7 @@ Material:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_Name: fish_red_tail_barracuda_mat_ll m_Name: fish_red_tail_barracuda_mat_ll
m_Shader: {fileID: 4800000, guid: 5626fadf5ea58ad448d468c2362fadfb, type: 3} m_Shader: {fileID: 4800000, guid: 8e32954102c0fc647a751090ef5e40be, type: 3}
m_Parent: {fileID: 0} m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0 m_ModifiedSerializedProperties: 0
m_ValidKeywords: [] m_ValidKeywords: []

View File

@ -22509,7 +22509,7 @@ MeshRenderer:
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: 82f089674b537fe42b84f66f22c4b13f, type: 2} - {fileID: 2100000, guid: 82f089674b537fe42b84f66f22c4b13f, type: 2}
- {fileID: 2100000, guid: 2d10684597fead649b048a865154ad79, type: 2} - {fileID: 2100000, guid: 872b4d67fd3bbd14caa9aea720bd1b75, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0
@ -22548,7 +22548,7 @@ Transform:
m_GameObject: {fileID: 1335403876} m_GameObject: {fileID: 1335403876}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0.000000018075937, y: -0.5621158, z: -0.000000012285431, w: 0.8270586} m_LocalRotation: {x: 0.000000018075937, y: -0.5621158, z: -0.000000012285431, w: 0.8270586}
m_LocalPosition: {x: -1.386, y: 1.0068, z: -116.992} m_LocalPosition: {x: -1.3689, y: 1, z: -116.9988}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -29247,7 +29247,7 @@ Transform:
m_GameObject: {fileID: 1829397597} m_GameObject: {fileID: 1829397597}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -3.221, y: 0.89, z: -116.13} m_LocalPosition: {x: -3.221, y: 1.712, z: -116.13}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -33661,7 +33661,7 @@ GameObject:
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0
m_StaticEditorFlags: 0 m_StaticEditorFlags: 0
m_IsActive: 0 m_IsActive: 1
--- !u!4 &2128448439 --- !u!4 &2128448439
Transform: Transform:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@ -0,0 +1,110 @@
#include "MomentOutput.hlsl"
// DONTBLOWUP is NOT part of the original paper, this is a hack i had to do to make this implementation usable
//#define ABSORPTION_EPSILON max(REAL_MIN, 1e-5)
#if _MOMENT_HALF_PRECISION
//#define DONTBLOWUP HALF_MIN
#define DONTBLOWUP HALF_EPS
#else
//#define DONTBLOWUP FLT_MIN
#define DONTBLOWUP FLT_EPS
#endif
float4 _WrappingZoneParameters;
/*! This function implements complex multiplication.*/
float2 Multiply(float2 LHS, float2 RHS) {
return float2(LHS.x * RHS.x - LHS.y * RHS.y, LHS.x * RHS.y + LHS.y * RHS.x);
}
// if ROV, set to the stored moments from the rasterizer order view and write the new moments back
// no ROVs so only single precision works - needs further changes for quantized to read/write moments
MomentOutput GenerateMoments(float vd, float t)
{
t = max(t, DONTBLOWUP); // we have a blowup issue when t (1 - alpha) is close to 0, empirically it happens above 0.99 alpha so let's lock it around there (no visual issues, at that point its pretty indiscernable from opaque)
// Return early if the surface is fully transparent
clip(0.9999999f - t);
float a = -log(t);
float d = WarpDepth(vd);
MomentOutput output;
output.b0 = a;
#ifdef _TRIGONOMETRIC
float p = mad(d, _WrappingZoneParameters.y, _WrappingZoneParameters.y);
float2 c;
sincos(p, c.y, c.x);
float2 c2 = Multiply(c, c);
output.b1 = float4(c, c2) * a;
#ifdef _MOMENT8
output.b2 = float4(Multiply(c, c2), Multiply(c2, c2)) * a;
#elif defined(_MOMENT6)
output.b2 = Multiply(c, c2) * a;
#endif
#else // not _TRIGONOMETRIC
float d2 = d * d;
float d4 = d2 * d2;
#if _MOMENT_HALF_PRECISION // QUANTIZE (ROVs ONLY)
#ifdef _MOMENT8
float4 b_even = (float4) 0;
float4 b_odd = (float4) 0;;
offsetMoments(b_even, b_odd, -1.0);
b_even *= output.b0;
b_odd *= output.b0;
float d6 = d4 * d2;
float4 b_even_new = float4(d2, d4, d6, d6 * d2);
float4 b_odd_new = float4(d, d2 * d, d4 * d, d6 * d);
float4 b_even_new_q, b_odd_new_q;
#elif defined(_MOMENT6)
float3 b_even = (float3) 0;
float3 b_odd = (float3) 0;
offsetMoments(b_even, b_odd, -1.0);
b_even *= output.b0;
b_odd *= output.b0;
float3 b_even_new = float3(d2, d4, d4 * d2);
float3 b_odd_new = float3(d, d2 * d, d4 * d);
float3 b_even_new_q, b_odd_new_q;
#else // _MOMENT4
float2 b_even = (float2) 0;
float2 b_odd = (float2) 0;
offsetMoments(b_even, b_odd, -1.0);
b_even *= output.b0;
b_odd *= output.b0;
float2 b_even_new = float2(d2, d4);
float2 b_odd_new = float2(d, d2 * d);
float2 b_even_new_q, b_odd_new_q;
#endif
quantizeMoments(b_even_new_q, b_odd_new_q, b_even_new, b_odd_new);
// combine moments
b_even += b_even_new_q * a;
b_odd += b_odd_new_q * a;
// go back to interval [0, 1]
b_even /= a;
b_odd /= a;
offsetMoments(b_even, b_odd, 1.0);
output.b1 = float4(b_odd.x, b_even.x, b_odd.y, b_even.y);
#ifdef _MOMENT8
output.b2 = float4(b_odd.z, b_even.z, b_odd.w, b_even.w);
#elif defined(_MOMENT6)
output.b2 = float2(b_odd.z, b_even.z);
#endif
#else // _MOMENT_SINGLE_PRECISION
output.b1 = float4(d, d2, d2 * d, d4) * a;
#ifdef _MOMENT8
output.b2 = output.b1 * d4;
#elif defined(_MOMENT6)
output.b2 = output.b1.xy * d4;
#endif
#endif // precision end
#endif // TRIGONOMETRIC end
return output;
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d94ff48de7281de41b27523cbe5b4b82
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,30 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Hidden_MOITComposite
m_Shader: {fileID: 4800000, guid: 1e94033f658200a4698edae134542fa2, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs: []
m_Ints: []
m_Floats:
- _CATCHBIASERRORS: 0
m_Colors: []
m_BuildTextureStacks: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f5b0014f90114064c8200b5c9ac93932
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,36 @@
using System;
using UnityEngine;
[Serializable]
public class MOITBias
{
// see supplementary document from the original paper : https://momentsingraphics.de/I3D2018.html ; Table 1
[Header("Power Moments")]
[Tooltip("Recommended : 6*10-5")]
public float Moments4Half = 0.00006f;
[Tooltip("Recommended : 5*10-7")]
public float Moments4Single = 0.0000005f;
[Tooltip("Recommended : 6*10-4")]
public float Moments6Half = 0.0006f;
[Tooltip("Recommended : 5*10-6")]
public float Moments6Single = 0.000005f;
[Tooltip("Recommended : 2.5*10-3")]
public float Moments8Half = 0.0025f;
[Tooltip("Recommended : 5*10-5")]
public float Moments8Single = 0.00005f;
[Header("Trigonometric Moments")]
[Tooltip("(4 moments) Recommended : 4*10-4")]
public float Trigonometric2Half = 0.0004f;
[Tooltip("(4 moments) Recommended : 4*10-7")]
public float Trigonometric2Single = 0.0000004f;
[Tooltip("(6 moments) Recommended : 6.5*10-4")]
public float Trigonometric3Half = 0.00065f;
[Tooltip("(6 moments) Recommended : 8*10-7")]
public float Trigonometric3Single = 0.0000008f;
[Tooltip("(8 moments) Recommended : 8.5*10-4")]
public float Trigonometric4Half = 0.00085f;
[Tooltip("(8 moments) Recommended : 1.5*10-6")]
public float Trigonometric4Single = 0.0000015f;
}

View File

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

View File

@ -0,0 +1,113 @@
Shader "Hidden/MOITComposite"
{
Properties
{
[Toggle] _CATCHBIASERRORS ("Catch Bias Errors", Float) = 0 // set this on if you see bloom fireflies and are not able to fix it with the other controls / are scared to see it again in build
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// The Blit.hlsl file provides the vertex shader (Vert),
// the input structure (Attributes), and the output structure (Varyings)
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#ifdef _MOMENT_SINGLE_PRECISION
TEXTURE2D_FLOAT(_B0);
#else
TEXTURE2D_HALF(_B0);
#endif
//TEXTURE2D(_MOIT); // _MOIT is _BlitTexture since we don't need to sample screen tex (alpha blended)
// sampler_PointClamp sampler_LinearClamp
#define singleSampler sampler_LinearClamp
half4 MOITComposite(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
//float4 moit = SAMPLE_TEXTURE2D(_BlitTexture, singleSampler, input.texcoord);
half4 moit = SAMPLE_TEXTURE2D(_BlitTexture, singleSampler, input.texcoord);
#ifdef _MOMENT_SINGLE_PRECISION
float b0 = SAMPLE_TEXTURE2D(_B0, singleSampler, input.texcoord).r;
#else
half b0 = SAMPLE_TEXTURE2D(_B0, singleSampler, input.texcoord).r;
#endif
moit.rgb /= moit.a;
#if _CATCHBIASERRORS_ON
// catch negative color values when alpha is very close to 1 (>0.99)
// TODO: explore why the non shader graph tmpro shader writes negatives values in moit texture (in game view only)
return half4(max(moit.rgb, 0.0), exp(-b0));
#else
return half4(moit.rgb, exp(-b0));
#endif
}
ENDHLSL
SubShader
{
Tags{ "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
ZWrite Off Cull Off
Pass
{
Name "Composite"
ZTest Always
Blend OneMinusSrcAlpha SrcAlpha
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment MOITComposite
#pragma shader_feature _CATCHBIASERRORS_ON
ENDHLSL
}
// Pass
// {
// Name "CompositeFrameBuffer"
//
// ZTest Always
// Blend OneMinusSrcAlpha SrcAlpha
//
// HLSLPROGRAM
//
// #pragma vertex Vert
// #pragma fragment MOITCompositeFB
//
// #pragma shader_feature _CATCHBIASERRORS_ON
//
// FRAMEBUFFER_INPUT_X_FLOAT(0);
//
// half4 MOITCompositeFB(Varyings input) : SV_Target
// {
// UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
//
// //float4 moit = SAMPLE_TEXTURE2D(_BlitTexture, singleSampler, input.texcoord);
// //float4 moit = LOAD_FRAMEBUFFER_X_INPUT(0, input.positionCS.xy);
// half4 moit = LOAD_FRAMEBUFFER_X_INPUT(0, input.positionCS.xy);
// #ifdef _MOMENT_SINGLE_PRECISION
// float b0 = SAMPLE_TEXTURE2D(_B0, singleSampler, input.texcoord).r;
// #else
// half b0 = SAMPLE_TEXTURE2D(_B0, singleSampler, input.texcoord).r;
// #endif
// moit.rgb /= moit.a;
//
// #if _CATCHBIASERRORS_ON
// // catch negative color values when alpha is very close to 1 (>0.99)
// // TODO: explore why the non shader graph tmpro shader writes negatives values in moit texture (in game view only)
// return half4(max(moit.rgb, 0.0), exp(-b0));
// #else
// return half4(moit.rgb, exp(-b0));
// #endif
// }
//
// ENDHLSL
// }
}
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1e94033f658200a4698edae134542fa2
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,14 +1,45 @@
using System; using System;
using System.Drawing.Drawing2D;
using System.Runtime.ConstrainedExecution;
using System.Security.Cryptography;
using UnityEngine; using UnityEngine;
using UnityEngine.Experimental.Rendering.RenderGraphModule;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.Universal;
[DisallowMultipleRendererFeature("Moment Based Order-Independent Transparency")] [DisallowMultipleRendererFeature("Moment Based Order-Independent Transparency")]
public class MOITFeature : ScriptableRendererFeature 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] [Serializable]
class Settings class Settings
{ {
[SerializeField]
internal RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
[SerializeField]
internal MOITSettings settings;
[SerializeField]
internal MOITBias biasSettings;
} }
[SerializeField] [SerializeField]
@ -32,17 +63,252 @@ public class MOITFeature : ScriptableRendererFeature
pass = new(settings); pass = new(settings);
} }
class MOITPass : ScriptableRenderPass protected override void Dispose(bool disposing)
{ {
private Settings settings; 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) public MOITPass(Settings settings)
{ {
this.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) 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();
} }
} }
} }

View File

@ -0,0 +1,31 @@
using System;
using UnityEngine;
[Serializable]
public class MOITSettings
{
[Header("Quality features")]
[Tooltip("4 is fine for most situations and the most performant.\nCan be upped to 6 if needed (lots of close objects with varying colors) and still stay somewhat performant\n8 has diminishing returns and trigonometric should be used instead at that point")]
public MOITFeature.MomentsCount momentsCount = MOITFeature.MomentsCount._4;
[Tooltip("Store moments in 16 (half) of 32bit precision (half is fine for most realtime uses)\nUNFORTUNATELY HALF POWER MOMENTS REQUIRE ROVs (Rasterizer Ordered Views) - out of scope for now, so will override to single precision unless trigonometric")]
public MOITFeature.FloatPrecision momentPrecision = MOITFeature.FloatPrecision._Single; //_Half;
[Tooltip("If better precision is needed and performance is not a concern, set trigonometric to true, use single precision and 6 moments (or 8)\n(4 moments = 2 trigonometric, 6m = 3t, 8m = 4t)")]
public bool trigonometric = false;
[Header("Bounds")]
[Tooltip("Method of finding MOIT renderers in order to build the conservative bounding sphere that we use to warp depth, lowering numerical errors\n- NearFarPlanes: just use near and far planes (essentially keep low precision)\n- FindObjects: not optimized but automatic (Renderers only)\n- Register: user needs to add a script to every transparent object (Renderer and VFX)")]
public MOITFeature.BoundsType boundsType = MOITFeature.BoundsType.FindObjects;
[Tooltip("Setting for BoundsType.FindObject and BoundType.Register :\nShould we check if each renderer is visible (by any camera) before adding its bounds?")]
public bool onlyVisibleRenderers = false;
[Header("Rendering")]
[Tooltip("Works with Everything but usually MOIT objects should be set on specific layers that are removed from the Transparent Layer Mask (in Universal Renderer Data) to prevent double rendering")]
public LayerMask layerMask = Physics.AllLayers;
[Tooltip("Set a different RenderQueueRange than default Transparent")]
public int renderQueueMin = 2501;
public int renderQueueMax = 3000;
public Material compositeMaterial;
[Tooltip("Back to front sorting does not matter in OIT techniques so we skip it, however this is probably desirable if you intend to write to depth\n(example : base DoF on the closest transparent object instead of the first opaque on the pixel)")]
public bool sortBackToFront = false;
[Header("Debug")]
[Tooltip("Set this to true to be able to visualize the MOIT texture in the fullscreen feature using RT_test_mat (else only B0 B1 and B2 (if applicable) are available)")]
public bool debugMakeMOITTexGlobal = false;
}

View File

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

View File

@ -0,0 +1,395 @@
#ifndef LIGHTWEIGHT_MOMENT_MATH_INCLUDED
#define LIGHTWEIGHT_MOMENT_MATH_INCLUDED
/*! Code taken from the blog "Moments in Graphics" by Christoph Peters.
http://momentsingraphics.de/?p=105
This function computes the three real roots of a cubic polynomial
Coefficient[0]+Coefficient[1]*x+Coefficient[2]*x^2+Coefficient[3]*x^3.*/
float3 SolveCubic(float4 Coefficient) {
// Normalize the polynomial
Coefficient.xyz /= Coefficient.w;
// Divide middle coefficients by three
Coefficient.yz /= 3.0f;
// Compute the Hessian and the discrimant
float3 Delta = float3(
mad(-Coefficient.z, Coefficient.z, Coefficient.y),
mad(-Coefficient.y, Coefficient.z, Coefficient.x),
dot(float2(Coefficient.z, -Coefficient.y), Coefficient.xy)
);
float Discriminant = dot(float2(4.0f * Delta.x, -Delta.y), Delta.zy);
// Compute coefficients of the depressed cubic
// (third is zero, fourth is one)
float2 Depressed = float2(
mad(-2.0f * Coefficient.z, Delta.x, Delta.y),
Delta.x
);
// Take the cubic root of a normalized complex number
float Theta = atan2(sqrt(Discriminant), -Depressed.x) / 3.0f;
float2 CubicRoot;
sincos(Theta, CubicRoot.y, CubicRoot.x);
// Compute the three roots, scale appropriately and
// revert the depression transform
float3 Root = float3(
CubicRoot.x,
dot(float2(-0.5f, -0.5f * sqrt(3.0f)), CubicRoot),
dot(float2(-0.5f, 0.5f * sqrt(3.0f)), CubicRoot)
);
Root = mad(2.0f * sqrt(-Depressed.y), Root, -Coefficient.z);
return Root;
}
/*! Given coefficients of a quadratic polynomial A*x^2+B*x+C, this function
outputs its two real roots.*/
float2 solveQuadratic(float3 coeffs)
{
coeffs[1] *= 0.5;
float x1, x2, tmp;
tmp = (coeffs[1] * coeffs[1] - coeffs[0] * coeffs[2]);
if (coeffs[1] >= 0) {
tmp = sqrt(tmp);
x1 = (-coeffs[2]) / (coeffs[1] + tmp);
x2 = (-coeffs[1] - tmp) / coeffs[0];
}
else {
tmp = sqrt(tmp);
x1 = (-coeffs[1] + tmp) / coeffs[0];
x2 = coeffs[2] / (-coeffs[1] + tmp);
}
return float2(x1, x2);
}
/*! Given coefficients of a cubic polynomial
coeffs[0]+coeffs[1]*x+coeffs[2]*x^2+coeffs[3]*x^3 with three real roots,
this function returns the root of least magnitude.*/
float solveCubicBlinnSmallest(float4 coeffs)
{
coeffs.xyz /= coeffs.w;
coeffs.yz /= 3.0;
float3 delta = float3(mad(-coeffs.z, coeffs.z, coeffs.y), mad(-coeffs.z, coeffs.y, coeffs.x), coeffs.z * coeffs.x - coeffs.y * coeffs.y);
float discriminant = 4.0 * delta.x * delta.z - delta.y * delta.y;
float2 depressed = float2(delta.z, -coeffs.x * delta.y + 2.0 * coeffs.y * delta.z);
float theta = abs(atan2(coeffs.x * sqrt(discriminant), -depressed.y)) / 3.0;
float2 sin_cos;
sincos(theta, sin_cos.x, sin_cos.y);
float tmp = 2.0 * sqrt(-depressed.x);
float2 x = float2(tmp * sin_cos.y, tmp * (-0.5 * sin_cos.y - 0.5 * sqrt(3.0) * sin_cos.x));
float2 s = (x.x + x.y < 2.0 * coeffs.y) ? float2(-coeffs.x, x.x + coeffs.y) : float2(-coeffs.x, x.y + coeffs.y);
return s.x / s.y;
}
/*! Given coefficients of a quartic polynomial
coeffs[0]+coeffs[1]*x+coeffs[2]*x^2+coeffs[3]*x^3+coeffs[4]*x^4 with four
real roots, this function returns all roots.*/
float4 solveQuarticNeumark(float coeffs[5])
{
// Normalization
float B = coeffs[3] / coeffs[4];
float C = coeffs[2] / coeffs[4];
float D = coeffs[1] / coeffs[4];
float E = coeffs[0] / coeffs[4];
// Compute coefficients of the cubic resolvent
float P = -2.0 * C;
float Q = C * C + B * D - 4.0 * E;
float R = D * D + B * B * E - B * C * D;
// Obtain the smallest cubic root
float y = solveCubicBlinnSmallest(float4(R, Q, P, 1.0));
float BB = B * B;
float fy = 4.0 * y;
float BB_fy = BB - fy;
float Z = C - y;
float ZZ = Z * Z;
float fE = 4.0 * E;
float ZZ_fE = ZZ - fE;
float G, g, H, h;
// Compute the coefficients of the quadratics adaptively using the two
// proposed factorizations by Neumark. Choose the appropriate
// factorizations using the heuristic proposed by Herbison-Evans.
if (y < 0 || (ZZ + fE) * BB_fy > ZZ_fE * (BB + fy)) {
float tmp = sqrt(BB_fy);
G = (B + tmp) * 0.5;
g = (B - tmp) * 0.5;
tmp = (B * Z - 2.0 * D) / (2.0 * tmp);
H = mad(Z, 0.5, tmp);
h = mad(Z, 0.5, -tmp);
}
else {
float tmp = sqrt(ZZ_fE);
H = (Z + tmp) * 0.5;
h = (Z - tmp) * 0.5;
tmp = (B * Z - 2.0 * D) / (2.0 * tmp);
G = mad(B, 0.5, tmp);
g = mad(B, 0.5, -tmp);
}
// Solve the quadratics
return float4(solveQuadratic(float3(1.0, G, H)), solveQuadratic(float3(1.0, g, h)));
}
/*! This function reconstructs the transmittance at the given depth from four
normalized power moments and the given zeroth moment.*/
float ComputeTransmittance(float b_0, float2 b_even, float2 b_odd, float depth, float bias, float overestimation, float4 bias_vector)
{
float4 b = float4(b_odd.x, b_even.x, b_odd.y, b_even.y);
// Bias input data to avoid artifacts
b = lerp(b, bias_vector, bias);
float3 z;
z[0] = depth;
// Compute a Cholesky factorization of the Hankel matrix B storing only non-
// trivial entries or related products
float L21D11 = mad(-b[0], b[1], b[2]);
float D11 = mad(-b[0], b[0], b[1]);
float InvD11 = 1.0f / D11;
float L21 = L21D11 * InvD11;
float SquaredDepthVariance = mad(-b[1], b[1], b[3]);
float D22 = mad(-L21D11, L21, SquaredDepthVariance);
// Obtain a scaled inverse image of bz=(1,z[0],z[0]*z[0])^T
float3 c = float3(1.0f, z[0], z[0] * z[0]);
// Forward substitution to solve L*c1=bz
c[1] -= b.x;
c[2] -= b.y + L21 * c[1];
// Scaling to solve D*c2=c1
c[1] *= InvD11;
c[2] /= D22;
// Backward substitution to solve L^T*c3=c2
c[1] -= L21 * c[2];
c[0] -= dot(c.yz, b.xy);
// Solve the quadratic equation c[0]+c[1]*z+c[2]*z^2 to obtain solutions
// z[1] and z[2]
float InvC2 = 1.0f / c[2];
float p = c[1] * InvC2;
float q = c[0] * InvC2;
float D = (p * p * 0.25f) - q;
float r = sqrt(D);
z[1] = -p * 0.5f - r;
z[2] = -p * 0.5f + r;
// Compute the absorbance by summing the appropriate weights
float3 polynomial;
float3 weight_factor = float3(overestimation, (z[1] < z[0]) ? 1.0f : 0.0f, (z[2] < z[0]) ? 1.0f : 0.0f);
float f0 = weight_factor[0];
float f1 = weight_factor[1];
float f2 = weight_factor[2];
float f01 = (f1 - f0) / (z[1] - z[0]);
float f12 = (f2 - f1) / (z[2] - z[1]);
float f012 = (f12 - f01) / (z[2] - z[0]);
polynomial[0] = f012;
polynomial[1] = polynomial[0];
polynomial[0] = f01 - polynomial[0] * z[1];
polynomial[2] = polynomial[1];
polynomial[1] = polynomial[0] - polynomial[1] * z[0];
polynomial[0] = f0 - polynomial[0] * z[0];
float absorbance = polynomial[0] + dot(b.xy, polynomial.yz);;
// Turn the normalized absorbance into transmittance
return saturate(exp(-b_0 * absorbance));
}
/*! This function reconstructs the transmittance at the given depth from six
normalized power moments and the given zeroth moment.*/
float ComputeTransmittance(float b_0, float3 b_even, float3 b_odd, float depth, float bias, float overestimation, float bias_vector[6])
{
float b[6] = { b_odd.x, b_even.x, b_odd.y, b_even.y, b_odd.z, b_even.z };
// Bias input data to avoid artifacts
//[unroll]
UNITY_UNROLL
for (int i = 0; i != 6; ++i) {
b[i] = lerp(b[i], bias_vector[i], bias);
}
float4 z;
z[0] = depth;
// Compute a Cholesky factorization of the Hankel matrix B storing only non-
// trivial entries or related products
float InvD11 = 1.0f / mad(-b[0], b[0], b[1]);
float L21D11 = mad(-b[0], b[1], b[2]);
float L21 = L21D11 * InvD11;
float D22 = mad(-L21D11, L21, mad(-b[1], b[1], b[3]));
float L31D11 = mad(-b[0], b[2], b[3]);
float L31 = L31D11 * InvD11;
float InvD22 = 1.0f / D22;
float L32D22 = mad(-L21D11, L31, mad(-b[1], b[2], b[4]));
float L32 = L32D22 * InvD22;
float D33 = mad(-b[2], b[2], b[5]) - dot(float2(L31D11, L32D22), float2(L31, L32));
float InvD33 = 1.0f / D33;
// Construct the polynomial whose roots have to be points of support of the
// canonical distribution: bz=(1,z[0],z[0]*z[0],z[0]*z[0]*z[0])^T
float4 c;
c[0] = 1.0f;
c[1] = z[0];
c[2] = c[1] * z[0];
c[3] = c[2] * z[0];
// Forward substitution to solve L*c1=bz
c[1] -= b[0];
c[2] -= mad(L21, c[1], b[1]);
c[3] -= b[2] + dot(float2(L31, L32), c.yz);
// Scaling to solve D*c2=c1
c.yzw *= float3(InvD11, InvD22, InvD33);
// Backward substitution to solve L^T*c3=c2
c[2] -= L32 * c[3];
c[1] -= dot(float2(L21, L31), c.zw);
c[0] -= dot(float3(b[0], b[1], b[2]), c.yzw);
// Solve the cubic equation
z.yzw = SolveCubic(c);
// Compute the absorbance by summing the appropriate weights
float4 weigth_factor;
weigth_factor[0] = overestimation;
weigth_factor.yzw = (z.yzw > z.xxx) ? float3 (0.0f, 0.0f, 0.0f) : float3 (1.0f, 1.0f, 1.0f);
// Construct an interpolation polynomial
float f0 = weigth_factor[0];
float f1 = weigth_factor[1];
float f2 = weigth_factor[2];
float f3 = weigth_factor[3];
float f01 = (f1 - f0) / (z[1] - z[0]);
float f12 = (f2 - f1) / (z[2] - z[1]);
float f23 = (f3 - f2) / (z[3] - z[2]);
float f012 = (f12 - f01) / (z[2] - z[0]);
float f123 = (f23 - f12) / (z[3] - z[1]);
float f0123 = (f123 - f012) / (z[3] - z[0]);
float4 polynomial;
// f012+f0123 *(z-z2)
polynomial[0] = mad(-f0123, z[2], f012);
polynomial[1] = f0123;
// *(z-z1) +f01
polynomial[2] = polynomial[1];
polynomial[1] = mad(polynomial[1], -z[1], polynomial[0]);
polynomial[0] = mad(polynomial[0], -z[1], f01);
// *(z-z0) +f0
polynomial[3] = polynomial[2];
polynomial[2] = mad(polynomial[2], -z[0], polynomial[1]);
polynomial[1] = mad(polynomial[1], -z[0], polynomial[0]);
polynomial[0] = mad(polynomial[0], -z[0], f0);
float absorbance = dot(polynomial, float4 (1.0, b[0], b[1], b[2]));
// Turn the normalized absorbance into transmittance
return saturate(exp(-b_0 * absorbance));
}
float ComputeTransmittance(float b_0, float4 b_even, float4 b_odd, float depth, float bias, float overestimation, float bias_vector[8])
{
float b[8] = { b_odd.x, b_even.x, b_odd.y, b_even.y, b_odd.z, b_even.z, b_odd.w, b_even.w };
// Bias input data to avoid artifacts
//[unroll]
UNITY_UNROLL
for (int i = 0; i != 8; ++i) {
b[i] = lerp(b[i], bias_vector[i], bias);
}
float z[5];
z[0] = depth;
// Compute a Cholesky factorization of the Hankel matrix B storing only non-trivial entries or related products
float D22 = mad(-b[0], b[0], b[1]);
float InvD22 = 1.0 / D22;
float L32D22 = mad(-b[1], b[0], b[2]);
float L32 = L32D22 * InvD22;
float L42D22 = mad(-b[2], b[0], b[3]);
float L42 = L42D22 * InvD22;
float L52D22 = mad(-b[3], b[0], b[4]);
float L52 = L52D22 * InvD22;
float D33 = mad(-L32, L32D22, mad(-b[1], b[1], b[3]));
float InvD33 = 1.0 / D33;
float L43D33 = mad(-L42, L32D22, mad(-b[2], b[1], b[4]));
float L43 = L43D33 * InvD33;
float L53D33 = mad(-L52, L32D22, mad(-b[3], b[1], b[5]));
float L53 = L53D33 * InvD33;
float D44 = mad(-b[2], b[2], b[5]) - dot(float2(L42, L43), float2(L42D22, L43D33));
float InvD44 = 1.0 / D44;
float L54D44 = mad(-b[3], b[2], b[6]) - dot(float2(L52, L53), float2(L42D22, L43D33));
float L54 = L54D44 * InvD44;
float D55 = mad(-b[3], b[3], b[7]) - dot(float3(L52, L53, L54), float3(L52D22, L53D33, L54D44));
float InvD55 = 1.0 / D55;
// Construct the polynomial whose roots have to be points of support of the
// Canonical distribution:
// bz = (1,z[0],z[0]^2,z[0]^3,z[0]^4)^T
float c[5];
c[0] = 1.0;
c[1] = z[0];
c[2] = c[1] * z[0];
c[3] = c[2] * z[0];
c[4] = c[3] * z[0];
// Forward substitution to solve L*c1 = bz
c[1] -= b[0];
c[2] -= mad(L32, c[1], b[1]);
c[3] -= b[2] + dot(float2(L42, L43), float2(c[1], c[2]));
c[4] -= b[3] + dot(float3(L52, L53, L54), float3(c[1], c[2], c[3]));
// Scaling to solve D*c2 = c1
//c = c .*[1, InvD22, InvD33, InvD44, InvD55];
c[1] *= InvD22;
c[2] *= InvD33;
c[3] *= InvD44;
c[4] *= InvD55;
// Backward substitution to solve L^T*c3 = c2
c[3] -= L54 * c[4];
c[2] -= dot(float2(L53, L43), float2(c[4], c[3]));
c[1] -= dot(float3(L52, L42, L32), float3(c[4], c[3], c[2]));
c[0] -= dot(float4(b[3], b[2], b[1], b[0]), float4(c[4], c[3], c[2], c[1]));
// Solve the quartic equation
float4 zz = solveQuarticNeumark(c);
z[1] = zz[0];
z[2] = zz[1];
z[3] = zz[2];
z[4] = zz[3];
// Compute the absorbance by summing the appropriate weights
float4 weigth_factor = (float4(z[1], z[2], z[3], z[4]) <= z[0].xxxx);
// Construct an interpolation polynomial
float f0 = overestimation;
float f1 = weigth_factor[0];
float f2 = weigth_factor[1];
float f3 = weigth_factor[2];
float f4 = weigth_factor[3];
float f01 = (f1 - f0) / (z[1] - z[0]);
float f12 = (f2 - f1) / (z[2] - z[1]);
float f23 = (f3 - f2) / (z[3] - z[2]);
float f34 = (f4 - f3) / (z[4] - z[3]);
float f012 = (f12 - f01) / (z[2] - z[0]);
float f123 = (f23 - f12) / (z[3] - z[1]);
float f234 = (f34 - f23) / (z[4] - z[2]);
float f0123 = (f123 - f012) / (z[3] - z[0]);
float f1234 = (f234 - f123) / (z[4] - z[1]);
float f01234 = (f1234 - f0123) / (z[4] - z[0]);
float Polynomial_0;
float4 Polynomial;
// f0123 + f01234 * (z - z3)
Polynomial_0 = mad(-f01234, z[3], f0123);
Polynomial[0] = f01234;
// * (z - z2) + f012
Polynomial[1] = Polynomial[0];
Polynomial[0] = mad(-Polynomial[0], z[2], Polynomial_0);
Polynomial_0 = mad(-Polynomial_0, z[2], f012);
// * (z - z1) + f01
Polynomial[2] = Polynomial[1];
Polynomial[1] = mad(-Polynomial[1], z[1], Polynomial[0]);
Polynomial[0] = mad(-Polynomial[0], z[1], Polynomial_0);
Polynomial_0 = mad(-Polynomial_0, z[1], f01);
// * (z - z0) + f1
Polynomial[3] = Polynomial[2];
Polynomial[2] = mad(-Polynomial[2], z[0], Polynomial[1]);
Polynomial[1] = mad(-Polynomial[1], z[0], Polynomial[0]);
Polynomial[0] = mad(-Polynomial[0], z[0], Polynomial_0);
Polynomial_0 = mad(-Polynomial_0, z[0], f0);
float absorbance = Polynomial_0 + dot(Polynomial, float4(b[0], b[1], b[2], b[3]));
// Turn the normalized absorbance into transmittance
return saturate(exp(-b_0 * absorbance));
}
// replace [unroll] with UNITY_UNROLL for metal etc compatibility
#endif

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 01ecfba35fc1e0743acd03dfde076125
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
#if _MOMENT_HALF_PRECISION
#define f1 half
#define f2 half2
#define f4 half4
#else // _MOMENT_SINGLE_PRECISION
#define f1 float
#define f2 float2
#define f4 float4
#endif
struct MomentOutput
{
f1 b0 : SV_Target0;
f4 b1 : SV_Target1;
#ifdef _MOMENT8
f4 b2 : SV_Target2;
#elif defined(_MOMENT6)
f2 b2 : SV_Target2;
#endif
};

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b726e2096ac821a4696aa328564a4e3c
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,98 @@
// we use this to prevent artifacts with half (16bit) moments
// this is only for power moments as trigonometric moments do not suffer as much from rounding errors (quote from the guys who actually did this technique)
// see https://momentsingraphics.de/I3D2018.html
// Definition of utility functions for quantization and dequantization of power moments stored in 16 bits per moment
// NOTE : It seems that ROVs are necessary for this to work, unfortunately this is out of scope for now
// 4 MOMENTS
void offsetMoments(inout float2 b_even, inout float2 b_odd, float sign)
{
b_odd += 0.5 * sign;
}
void quantizeMoments(out float2 b_even_q, out float2 b_odd_q, float2 b_even, float2 b_odd)
{
b_odd_q = mul(b_odd, float2x2(1.5f, sqrt(3.0f) * 0.5f, -2.0f, -sqrt(3.0f) * 2.0f / 9.0f));
b_even_q = mul(b_even, float2x2(4.0f, 0.5f, -4.0f, 0.5f));
}
void offsetAndDequantizeMoments(out float2 b_even, out float2 b_odd, float2 b_even_q, float2 b_odd_q)
{
offsetMoments(b_even_q, b_odd_q, -1.0);
b_odd = mul(b_odd_q, float2x2(-1.0f / 3.0f, -0.75f, sqrt(3.0f), 0.75f * sqrt(3.0f)));
b_even = mul(b_even_q, float2x2(0.125f, -0.125f, 1.0f, 1.0f));
}
// 6 MOMENTS
void offsetMoments(inout float3 b_even, inout float3 b_odd, float sign)
{
b_odd += 0.5 * sign;
b_even.z += 0.018888946f * sign;
}
void quantizeMoments(out float3 b_even_q, out float3 b_odd_q, float3 b_even, float3 b_odd)
{
const float3x3 QuantizationMatrixOdd = float3x3(
2.5f, -1.87499864450f, 1.26583039016f,
-10.0f, 4.20757543111f, -1.47644882902f,
8.0f, -1.83257678661f, 0.71061660238f);
const float3x3 QuantizationMatrixEven = float3x3(
4.0f, 9.0f, -0.57759806484f,
-4.0f, -24.0f, 4.61936647543f,
0.0f, 16.0f, -3.07953906655f);
b_odd_q = mul(b_odd, QuantizationMatrixOdd);
b_even_q = mul(b_even, QuantizationMatrixEven);
}
void offsetAndDequantizeMoments(out float3 b_even, out float3 b_odd, float3 b_even_q, float3 b_odd_q)
{
const float3x3 QuantizationMatrixOdd = float3x3(
-0.02877789192f, 0.09995235706f, 0.25893353755f,
0.47635550422f, 0.84532580931f, 0.90779616657f,
1.55242808973f, 1.05472570761f, 0.83327335647f);
const float3x3 QuantizationMatrixEven = float3x3(
0.00001253044f, -0.24998746956f, -0.37498825271f,
0.16668494186f, 0.16668494186f, 0.21876713299f,
0.86602540579f, 0.86602540579f, 0.81189881793f);
offsetMoments(b_even_q, b_odd_q, -1.0);
b_odd = mul(b_odd_q, QuantizationMatrixOdd);
b_even = mul(b_even_q, QuantizationMatrixEven);
}
// 8 MOMENTS
void offsetMoments(inout float4 b_even, inout float4 b_odd, float sign)
{
b_odd += 0.5 * sign;
b_even += float4(0.972481993925964, 1.0, 0.999179192513328, 0.991778293073131) * sign;
}
void quantizeMoments(out float4 b_even_q, out float4 b_odd_q, float4 b_even, float4 b_odd)
{
const float4x4 mat_odd = float4x4(3.48044635732474, -27.5760737514826, 55.1267384344761, -31.5311110403183,
1.26797185782836, -0.928755808743913, -2.07520453231032, 1.23598848322588,
-2.1671560004294, 6.17950199592966, -0.276515571579297, -4.23583042392097,
0.974332879165755, -0.443426830933027, -0.360491648368785, 0.310149466050223);
const float4x4 mat_even = float4x4(0.280504133158527, -0.757633844606942, 0.392179589334688, -0.887531871812237,
-2.01362265883247, 0.221551373038988, -1.06107954265125, 2.83887201588367,
-7.31010494985321, 13.9855979699139, -0.114305766176437, -7.4361899359832,
-15.8954215629556, 79.6186327084103, -127.457278992502, 63.7349456687829);
b_odd_q = mul(mat_odd, b_odd);
b_even_q = mul(mat_even, b_even);
}
void offsetAndDequantizeMoments(out float4 b_even, out float4 b_odd, float4 b_even_q, float4 b_odd_q)
{
const float4x4 mat_odd = float4x4(-0.00482399708502382, -0.423201508674231, 0.0348312382605129, 1.67179208266592,
-0.0233402218644408, -0.832829097046478, 0.0193406040499625, 1.21021509068975,
-0.010888537031885, -0.926393772997063, -0.11723394414779, 0.983723301818275,
-0.0308713357806732, -0.937989172670245, -0.218033377677099, 0.845991731322996);
const float4x4 mat_even = float4x4(-0.976220278891035, -0.456139260269401, -0.0504335521016742, 0.000838800390651085,
-1.04828341778299, -0.229726640510149, 0.0259608334616091, -0.00133632693205861,
-1.03115268628604, -0.077844420809897, 0.00443408851014257, -0.0103744938457406,
-0.996038443434636, 0.0175438624416783, -0.0361414253243963, -0.00317839994022725);
offsetMoments(b_even_q, b_odd_q, -1.0);
b_odd = mul(mat_odd, b_odd_q);
b_even = mul(mat_even, b_even_q);
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 280699927ed265b4a8641989996cdec3
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,131 @@
#ifdef _MOMENT_SINGLE_PRECISION
TEXTURE2D_X_FLOAT(_B0);
TEXTURE2D_X_FLOAT(_B1);
#if defined(_MOMENT8) || defined(_MOMENT6)
TEXTURE2D_X_FLOAT(_B2);
#endif
#else
TEXTURE2D_X_HALF(_B0);
TEXTURE2D_X_HALF(_B1);
#if defined(_MOMENT8) || defined(_MOMENT6)
TEXTURE2D_X_HALF(_B2);
#endif
#endif
float4 _B0_TexelSize;
float _MOIT_MomentBias;
//float _MomentBias; // switched to global
float _Overestimation = 0.25f; // overestimation can be declared properties in the shader, but the 0.25f default is the recommended value in the paper
float4 _WrappingZoneParameters;
// sampler_PointClamp sampler_LinearClamp
#define singleSampler sampler_LinearClamp
#define sampler_B0 singleSampler
#define sampler_B1 singleSampler
#if defined(_MOMENT8) || defined(_MOMENT6)
#define sampler_B2 singleSampler
#endif
// TODO: check if can use LOAD_FRAMEBUFFER_X_INPUT(index, positionCS.xy); (float2 p = positionCS.xy)
void ResolveMoments(out float td, out float tt, float vd, float2 p)
{
float d = WarpDepth(vd);
td = 1;
tt = 1;
float b0 = SAMPLE_TEXTURE2D_X(_B0, sampler_B0, p).r;
// Return early if the surface is fully transparent
clip(b0 - 0.00100050033f);
tt = exp(-b0);
float4 b1 = SAMPLE_TEXTURE2D_X(_B1, sampler_B1, p);
//b1 /= b0; // had to move as quantized does things different
#ifdef _MOMENT8
float4 b2 = SAMPLE_TEXTURE2D_X(_B2, sampler_B2, p);
//b2 /= b0;
#ifdef _TRIGONOMETRIC
b1 /= b0;
b2 /= b0;
float2 tb[4];
tb[0] = b1.xy;
tb[1] = b1.zw;
tb[2] = b2.xy;
tb[3] = b2.zw;
td = ComputeTransmittanceTrigonometric(b0, tb, d, _MOIT_MomentBias, _Overestimation, _WrappingZoneParameters);
#else // 8 POWER MOMENTS
#if _MOMENT_SINGLE_PRECISION
b1 /= b0;
b2 /= b0;
float4 be = float4(b1.yw, b2.yw);
float4 bo = float4(b1.xz, b2.xz);
const float bias[8] = { 0, 0.75, 0, 0.67666666666666664, 0, 0.64, 0, 0.60030303030303034 };
//td = ComputeTransmittance(b0, be, bo, d, _MOIT_MomentBias, _Overestimation, bias);
#else // HALF (DEQUANTIZE)
float4 beq = float4(b1.yw, b2.yw);
float4 boq = float4(b1.xz, b2.xz);
float4 be, bo;
offsetAndDequantizeMoments(be, bo, beq, boq);
const float bias[8] = { 0, 0.42474916387959866, 0, 0.22407802675585284, 0, 0.15369230769230768, 0, 0.12900440529089119 };
#endif
td = ComputeTransmittance(b0, be, bo, d, _MOIT_MomentBias, _Overestimation, bias);
#endif
#elif defined(_MOMENT6)
float2 b2 = SAMPLE_TEXTURE2D_X(_B2, sampler_B2, p).rg;
//b2 /= b0;
#ifdef _TRIGONOMETRIC
b1 /= b0;
b2 /= b0;
float2 tb[3];
tb[0] = b1.xy;
tb[1] = b1.zw;
tb[2] = b2.xy;
td = ComputeTransmittanceTrigonometric(b0, tb, d, _MOIT_MomentBias, _Overestimation, _WrappingZoneParameters);
#else // 6 POWER MOMENTS
#if _MOMENT_SINGLE_PRECISION
b1 /= b0;
b2 /= b0;
float3 be = float3(b1.yw, b2.y);
float3 bo = float3(b1.xz, b2.x);
const float bias[6] = { 0, 0.48, 0, 0.451, 0, 0.45 };
//td = ComputeTransmittance(b0, be, bo, d, _MOIT_MomentBias, _Overestimation, bias);
#else // DEQUANTIZE
float3 beq = float3(b1.yw, b2.y);
float3 boq = float3(b1.xz, b2.x);
float3 be, bo;
offsetAndDequantizeMoments(be, bo, beq, boq);
const float bias[6] = { 0, 0.5566, 0, 0.489, 0, 0.47869382 };
#endif
td = ComputeTransmittance(b0, be, bo, d, _MOIT_MomentBias, _Overestimation, bias);
#endif
#else // _MOMENT4
#ifdef _TRIGONOMETRIC
b1 /= b0;
float2 tb[2];
tb[0] = b1.xy;
tb[1] = b1.zw;
td = ComputeTransmittanceTrigonometric(b0, tb, d, _MOIT_MomentBias, _Overestimation, _WrappingZoneParameters);
#else // 4 POWER MOMENTS
#if _MOMENT_SINGLE_PRECISION
b1 /= b0;
float2 be = b1.yw;
float2 bo = b1.xz;
const float4 bias = float4 (0, 0.375, 0, 0.375);
//td = ComputeTransmittance(b0, be, bo, d, _MOIT_MomentBias, _Overestimation, bias);
#else // QUANTIZED
float2 beq = b1.yw;
float2 boq = b1.xz;
float2 be, bo;
offsetAndDequantizeMoments(be, bo, beq, boq);
const float4 bias = float4(0, 0.628, 0, 0.628);
#endif
td = ComputeTransmittance(b0, be, bo, d, _MOIT_MomentBias, _Overestimation, bias);
#endif
#endif
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3ebfeb4e0588c2e4c9740481884ec4b5
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,510 @@
#ifndef LIGHTWEIGHT_TRIGONOMETRIC_MOMENT_MATH_INCLUDED
#define LIGHTWEIGHT_TRIGONOMETRIC_MOMENT_MATH_INCLUDED
/*! Returns the complex conjugate of the given complex number (i.e. it changes
the sign of the y-component).*/
float2 Conjugate(float2 Z) {
return float2(Z.x, -Z.y);
}
/*! This function implements complex multiplication.*/
float2 Multiply(float2 LHS, float2 RHS) {
return float2(LHS.x * RHS.x - LHS.y * RHS.y, LHS.x * RHS.y + LHS.y * RHS.x);
}
/*! This function computes the magnitude of the given complex number.*/
float Magnitude(float2 Z) {
return sqrt(dot(Z, Z));
}
/*! This function computes the quotient of two complex numbers. The denominator
must not be zero.*/
float2 Divide(float2 Numerator, float2 Denominator) {
return float2(Numerator.x * Denominator.x + Numerator.y * Denominator.y, -Numerator.x * Denominator.y + Numerator.y * Denominator.x) / dot(Denominator, Denominator);
}
/*! This function divides a real number by a complex number. The denominator
must not be zero.*/
float2 Divide(float Numerator, float2 Denominator) {
return float2(Numerator * Denominator.x, -Numerator * Denominator.y) / dot(Denominator, Denominator);
}
/*! This function implements computation of the reciprocal of the given non-
zero complex number.*/
float2 Reciprocal(float2 Z) {
return float2(Z.x, -Z.y) / dot(Z, Z);
}
/*! This utility function implements complex squaring.*/
float2 Square(float2 Z) {
return float2(Z.x * Z.x - Z.y * Z.y, 2.0f * Z.x * Z.y);
}
/*! This utility function implements complex computation of the third power.*/
float2 Cube(float2 Z) {
return Multiply(Square(Z), Z);
}
/*! This utility function computes one square root of the given complex value.
The other one can be found using the unary minus operator.
\warning This function is continuous but not defined on the negative real
axis (and cannot be continued continuously there).
\sa SquareRoot() */
float2 SquareRootUnsafe(float2 Z) {
float ZLengthSq = dot(Z, Z);
float ZLengthInv = rsqrt(ZLengthSq);
float2 UnnormalizedRoot = Z * ZLengthInv + float2(1.0f, 0.0f);
float UnnormalizedRootLengthSq = dot(UnnormalizedRoot, UnnormalizedRoot);
float NormalizationFactorInvSq = UnnormalizedRootLengthSq * ZLengthInv;
float NormalizationFactor = rsqrt(NormalizationFactorInvSq);
return NormalizationFactor * UnnormalizedRoot;
}
/*! This utility function computes one square root of the given complex value.
The other one can be found using the unary minus operator.
\note This function has discontinuities for values with real part zero.
\sa SquareRootUnsafe() */
float2 SquareRoot(float2 Z) {
float2 ZPositiveRealPart = float2(abs(Z.x), Z.y);
float2 ComputedRoot = SquareRootUnsafe(ZPositiveRealPart);
return (Z.x >= 0.0) ? ComputedRoot : ComputedRoot.yx;
}
/*! This utility function computes one cubic root of the given complex value. The
other roots can be found by multiplication by cubic roots of unity.
\note This function has various discontinuities.*/
float2 CubicRoot(float2 Z) {
float Argument = atan2(Z.y, Z.x);
float NewArgument = Argument / 3.0f;
float2 NormalizedRoot;
sincos(NewArgument, NormalizedRoot.y, NormalizedRoot.x);
return NormalizedRoot * pow(dot(Z, Z), 1.0f / 6.0f);
}
/*! @{
Returns the complex conjugate of the given complex vector (i.e. it changes the
second column resp the y-component).*/
float2x2 Conjugate(float2x2 Vector) {
return float2x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y);
}
float3x2 Conjugate(float3x2 Vector) {
return float3x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y, Vector[2].x, -Vector[2].y);
}
float4x2 Conjugate(float4x2 Vector) {
return float4x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y, Vector[2].x, -Vector[2].y, Vector[3].x, -Vector[3].y);
}
void Conjugate(out float2 OutConjugateVector[5], float2 Vector[5]) {
[unroll] for (int i = 0; i != 5; ++i) {
OutConjugateVector[i] = float2(Vector[i].x, -Vector[i].x);
}
}
//!@}
/*! Returns the real part of a complex number as real.*/
float RealPart(float2 Z) {
return Z.x;
}
/*! Given coefficients of a quadratic polynomial A*x^2+B*x+C, this function
outputs its two complex roots.*/
void SolveQuadratic(out float2 pOutRoot[2], float2 A, float2 B, float2 C)
{
// Normalize the coefficients
float2 InvA = Reciprocal(A);
B = Multiply(B, InvA);
C = Multiply(C, InvA);
// Divide the middle coefficient by two
B *= 0.5f;
// Apply the quadratic formula
float2 DiscriminantRoot = SquareRoot(Square(B) - C);
pOutRoot[0] = -B - DiscriminantRoot;
pOutRoot[1] = -B + DiscriminantRoot;
}
/*! Given coefficients of a cubic polynomial A*x^3+B*x^2+C*x+D, this function
outputs its three complex roots.*/
void SolveCubicBlinn(out float2 pOutRoot[3], float2 A, float2 B, float2 C, float2 D)
{
// Normalize the polynomial
float2 InvA = Reciprocal(A);
B = Multiply(B, InvA);
C = Multiply(C, InvA);
D = Multiply(D, InvA);
// Divide middle coefficients by three
B /= 3.0f;
C /= 3.0f;
// Compute the Hessian and the discriminant
float2 Delta00 = -Square(B) + C;
float2 Delta01 = -Multiply(C, B) + D;
float2 Delta11 = Multiply(B, D) - Square(C);
float2 Discriminant = 4.0f * Multiply(Delta00, Delta11) - Square(Delta01);
// Compute coefficients of the depressed cubic
// (third is zero, fourth is one)
float2 DepressedD = -2.0f * Multiply(B, Delta00) + Delta01;
float2 DepressedC = Delta00;
// Take the cubic root of a complex number avoiding cancellation
float2 DiscriminantRoot = SquareRoot(-Discriminant);
DiscriminantRoot = faceforward(DiscriminantRoot, DiscriminantRoot, DepressedD);
float2 CubedRoot = DiscriminantRoot - DepressedD;
float2 FirstRoot = CubicRoot(0.5f * CubedRoot);
float2 pCubicRoot[3] = {
FirstRoot,
Multiply(float2(-0.5f,-0.5f * sqrt(3.0f)),FirstRoot),
Multiply(float2(-0.5f, 0.5f * sqrt(3.0f)),FirstRoot)
};
// Also compute the reciprocal cubic roots
float2 InvFirstRoot = Reciprocal(FirstRoot);
float2 pInvCubicRoot[3] = {
InvFirstRoot,
Multiply(float2(-0.5f, 0.5f * sqrt(3.0f)),InvFirstRoot),
Multiply(float2(-0.5f,-0.5f * sqrt(3.0f)),InvFirstRoot)
};
// Turn them into roots of the depressed cubic and revert the depression
// transform
[unroll]
for (int i = 0; i != 3; ++i)
{
pOutRoot[i] = pCubicRoot[i] - Multiply(DepressedC, pInvCubicRoot[i]) - B;
}
}
/*! Given coefficients of a quartic polynomial A*x^4+B*x^3+C*x^2+D*x+E, this
function outputs its four complex roots.*/
void SolveQuarticNeumark(out float2 pOutRoot[4], float2 A, float2 B, float2 C, float2 D, float2 E)
{
// Normalize the polynomial
float2 InvA = Reciprocal(A);
B = Multiply(B, InvA);
C = Multiply(C, InvA);
D = Multiply(D, InvA);
E = Multiply(E, InvA);
// Construct a normalized cubic
float2 P = -2.0f * C;
float2 Q = Square(C) + Multiply(B, D) - 4.0f * E;
float2 R = Square(D) + Multiply(Square(B), E) - Multiply(Multiply(B, C), D);
// Compute a root that is not the smallest of the cubic
float2 pCubicRoot[3];
SolveCubicBlinn(pCubicRoot, float2(1.0f, 0.0f), P, Q, R);
float2 y = (dot(pCubicRoot[1], pCubicRoot[1]) > dot(pCubicRoot[0], pCubicRoot[0])) ? pCubicRoot[1] : pCubicRoot[0];
// Solve a quadratic to obtain linear coefficients for quadratic polynomials
float2 BB = Square(B);
float2 fy = 4.0f * y;
float2 BB_fy = BB - fy;
float2 tmp = SquareRoot(BB_fy);
float2 G = (B + tmp) * 0.5f;
float2 g = (B - tmp) * 0.5f;
// Construct the corresponding constant coefficients
float2 Z = C - y;
tmp = Divide(0.5f * Multiply(B, Z) - D, tmp);
float2 H = Z * 0.5f + tmp;
float2 h = Z * 0.5f - tmp;
// Compute the roots
float2 pQuadraticRoot[2];
SolveQuadratic(pQuadraticRoot, float2(1.0f, 0.0f), G, H);
pOutRoot[0] = pQuadraticRoot[0];
pOutRoot[1] = pQuadraticRoot[1];
SolveQuadratic(pQuadraticRoot, float2(1.0f, 0.0f), g, h);
pOutRoot[2] = pQuadraticRoot[0];
pOutRoot[3] = pQuadraticRoot[1];
}
/*! This utility function turns a point on the unit circle into a scalar
parameter. It is guaranteed to grow monotonically for (cos(phi),sin(phi))
with phi in 0 to 2*pi. There are no other guarantees. In particular it is
not an arclength parametrization. If you change this function, you must
also change circleToParameter() in MomentOIT.cpp.*/
float circleToParameter(float2 circle_point) {
float result = abs(circle_point.y) - abs(circle_point.x);
result = (circle_point.x < 0.0f) ? (2.0f - result) : result;
return (circle_point.y < 0.0f) ? (6.0f - result) : result;
}
/*! This utility function returns the appropriate weight factor for a root at
the given location. Both inputs are supposed to be unit vectors. If a
circular arc going counter clockwise from (1.0,0.0) meets root first, it
returns 1.0, otherwise 0.0 or a linear ramp in the wrapping zone.*/
float getRootWeightFactor(float reference_parameter, float root_parameter, float4 wrapping_zone_parameters) {
float binary_weight_factor = (root_parameter < reference_parameter) ? 1.0f : 0.0f;
float linear_weight_factor = saturate(mad(root_parameter, wrapping_zone_parameters.z, wrapping_zone_parameters.w));
return binary_weight_factor + linear_weight_factor;
}
/*! This function reconstructs the transmittance at the given depth from two
normalized trigonometric moments.*/
float ComputeTransmittanceTrigonometric(float b_0, float2 trig_b[2], float depth, float bias, float overestimation, float4 wrapping_zone_parameters)
{
// Apply biasing and reformat the inputs a little bit
float moment_scale = 1.0f - bias;
float2 b[3] = {
float2(1.0f, 0.0f),
trig_b[0] * moment_scale,
trig_b[1] * moment_scale
};
// Compute a Cholesky factorization of the Toeplitz matrix
float D00 = RealPart(b[0]);
float InvD00 = 1.0f / D00;
float2 L10 = (b[1]) * InvD00;
float D11 = RealPart(b[0] - D00 * Multiply(L10, Conjugate(L10)));
float InvD11 = 1.0f / D11;
float2 L20 = (b[2]) * InvD00;
float2 L21 = (b[1] - D00 * Multiply(L20, Conjugate(L10))) * InvD11;
float D22 = RealPart(b[0] - D00 * Multiply(L20, Conjugate(L20)) - D11 * Multiply(L21, Conjugate(L21)));
float InvD22 = 1.0f / D22;
// Solve a linear system to get the relevant polynomial
float phase = mad(depth, wrapping_zone_parameters.y, wrapping_zone_parameters.y);
float2 circle_point;
sincos(phase, circle_point.y, circle_point.x);
float2 c[3] = {
float2(1.0f,0.0f),
circle_point,
Multiply(circle_point, circle_point)
};
c[1] -= Multiply(L10, c[0]);
c[2] -= Multiply(L20, c[0]) + Multiply(L21, c[1]);
c[0] *= InvD00;
c[1] *= InvD11;
c[2] *= InvD22;
c[1] -= Multiply(Conjugate(L21), c[2]);
c[0] -= Multiply(Conjugate(L10), c[1]) + Multiply(Conjugate(L20), c[2]);
// Compute roots of the polynomial
float2 pRoot[2];
SolveQuadratic(pRoot, Conjugate(c[2]), Conjugate(c[1]), Conjugate(c[0]));
// Figure out how to weight the weights
float depth_parameter = circleToParameter(circle_point);
float3 weight_factor;
weight_factor[0] = overestimation;
[unroll]
for (int i = 0; i != 2; ++i)
{
float root_parameter = circleToParameter(pRoot[i]);
weight_factor[i + 1] = getRootWeightFactor(depth_parameter, root_parameter, wrapping_zone_parameters);
}
// Compute the appropriate linear combination of weights
float2 z[3] = { circle_point, pRoot[0], pRoot[1] };
float f0 = weight_factor[0];
float f1 = weight_factor[1];
float f2 = weight_factor[2];
float2 f01 = Divide(f1 - f0, z[1] - z[0]);
float2 f12 = Divide(f2 - f1, z[2] - z[1]);
float2 f012 = Divide(f12 - f01, z[2] - z[0]);
float2 polynomial[3];
polynomial[0] = f012;
polynomial[1] = polynomial[0];
polynomial[0] = f01 - Multiply(polynomial[0], z[1]);
polynomial[2] = polynomial[1];
polynomial[1] = polynomial[0] - Multiply(polynomial[1], z[0]);
polynomial[0] = f0 - Multiply(polynomial[0], z[0]);
float weight_sum = 0.0f;
weight_sum += RealPart(Multiply(b[0], polynomial[0]));
weight_sum += RealPart(Multiply(b[1], polynomial[1]));
weight_sum += RealPart(Multiply(b[2], polynomial[2]));
// Turn the normalized absorbance into transmittance
return exp(-b_0 * weight_sum);
}
/*! This function reconstructs the transmittance at the given depth from three
normalized trigonometric moments. */
float ComputeTransmittanceTrigonometric(float b_0, float2 trig_b[3], float depth, float bias, float overestimation, float4 wrapping_zone_parameters)
{
// Apply biasing and reformat the inputs a little bit
float moment_scale = 1.0f - bias;
float2 b[4] = {
float2(1.0f, 0.0f),
trig_b[0] * moment_scale,
trig_b[1] * moment_scale,
trig_b[2] * moment_scale
};
// Compute a Cholesky factorization of the Toeplitz matrix
float D00 = RealPart(b[0]);
float InvD00 = 1.0f / D00;
float2 L10 = (b[1]) * InvD00;
float D11 = RealPart(b[0] - D00 * Multiply(L10, Conjugate(L10)));
float InvD11 = 1.0f / D11;
float2 L20 = (b[2]) * InvD00;
float2 L21 = (b[1] - D00 * Multiply(L20, Conjugate(L10))) * InvD11;
float D22 = RealPart(b[0] - D00 * Multiply(L20, Conjugate(L20)) - D11 * Multiply(L21, Conjugate(L21)));
float InvD22 = 1.0f / D22;
float2 L30 = (b[3]) * InvD00;
float2 L31 = (b[2] - D00 * Multiply(L30, Conjugate(L10))) * InvD11;
float2 L32 = (b[1] - D00 * Multiply(L30, Conjugate(L20)) - D11 * Multiply(L31, Conjugate(L21))) * InvD22;
float D33 = RealPart(b[0] - D00 * Multiply(L30, Conjugate(L30)) - D11 * Multiply(L31, Conjugate(L31)) - D22 * Multiply(L32, Conjugate(L32)));
float InvD33 = 1.0f / D33;
// Solve a linear system to get the relevant polynomial
float phase = mad(depth, wrapping_zone_parameters.y, wrapping_zone_parameters.y);
float2 circle_point;
sincos(phase, circle_point.y, circle_point.x);
float2 circle_point_pow2 = Multiply(circle_point, circle_point);
float2 c[4] = {
float2(1.0f,0.0f),
circle_point,
circle_point_pow2,
Multiply(circle_point, circle_point_pow2)
};
c[1] -= Multiply(L10, c[0]);
c[2] -= Multiply(L20, c[0]) + Multiply(L21, c[1]);
c[3] -= Multiply(L30, c[0]) + Multiply(L31, c[1]) + Multiply(L32, c[2]);
c[0] *= InvD00;
c[1] *= InvD11;
c[2] *= InvD22;
c[3] *= InvD33;
c[2] -= Multiply(Conjugate(L32), c[3]);
c[1] -= Multiply(Conjugate(L21), c[2]) + Multiply(Conjugate(L31), c[3]);
c[0] -= Multiply(Conjugate(L10), c[1]) + Multiply(Conjugate(L20), c[2]) + Multiply(Conjugate(L30), c[3]);
// Compute roots of the polynomial
float2 pRoot[3];
SolveCubicBlinn(pRoot, Conjugate(c[3]), Conjugate(c[2]), Conjugate(c[1]), Conjugate(c[0]));
// The roots are known to be normalized but for reasons of numerical
// stability it can be better to enforce that
//pRoot[0]=normalize(pRoot[0]);
//pRoot[1]=normalize(pRoot[1]);
//pRoot[2]=normalize(pRoot[2]);
// Figure out how to weight the weights
float depth_parameter = circleToParameter(circle_point);
float4 weight_factor;
weight_factor[0] = overestimation;
[unroll]
for (int i = 0; i != 3; ++i)
{
float root_parameter = circleToParameter(pRoot[i]);
weight_factor[i + 1] = getRootWeightFactor(depth_parameter, root_parameter, wrapping_zone_parameters);
}
// Compute the appropriate linear combination of weights
float2 z[4] = { circle_point, pRoot[0], pRoot[1], pRoot[2] };
float f0 = weight_factor[0];
float f1 = weight_factor[1];
float f2 = weight_factor[2];
float f3 = weight_factor[3];
float2 f01 = Divide(f1 - f0, z[1] - z[0]);
float2 f12 = Divide(f2 - f1, z[2] - z[1]);
float2 f23 = Divide(f3 - f2, z[3] - z[2]);
float2 f012 = Divide(f12 - f01, z[2] - z[0]);
float2 f123 = Divide(f23 - f12, z[3] - z[1]);
float2 f0123 = Divide(f123 - f012, z[3] - z[0]);
float2 polynomial[4];
polynomial[0] = f0123;
polynomial[1] = polynomial[0];
polynomial[0] = f012 - Multiply(polynomial[0], z[2]);
polynomial[2] = polynomial[1];
polynomial[1] = polynomial[0] - Multiply(polynomial[1], z[1]);
polynomial[0] = f01 - Multiply(polynomial[0], z[1]);
polynomial[3] = polynomial[2];
polynomial[2] = polynomial[1] - Multiply(polynomial[2], z[0]);
polynomial[1] = polynomial[0] - Multiply(polynomial[1], z[0]);
polynomial[0] = f0 - Multiply(polynomial[0], z[0]);
float weight_sum = 0;
weight_sum += RealPart(Multiply(b[0], polynomial[0]));
weight_sum += RealPart(Multiply(b[1], polynomial[1]));
weight_sum += RealPart(Multiply(b[2], polynomial[2]));
weight_sum += RealPart(Multiply(b[3], polynomial[3]));
// Turn the normalized absorbance into transmittance
return exp(-b_0 * weight_sum);
}
/*! This function reconstructs the transmittance at the given depth from four
normalized trigonometric moments.*/
float ComputeTransmittanceTrigonometric(float b_0, float2 trig_b[4], float depth, float bias, float overestimation, float4 wrapping_zone_parameters)
{
// Apply biasing and reformat the inputs a little bit
float moment_scale = 1.0f - bias;
float2 b[5] = {
float2(1.0f, 0.0f),
trig_b[0] * moment_scale,
trig_b[1] * moment_scale,
trig_b[2] * moment_scale,
trig_b[3] * moment_scale
};
// Compute a Cholesky factorization of the Toeplitz matrix
float D00 = RealPart(b[0]);
float InvD00 = 1.0 / D00;
float2 L10 = (b[1]) * InvD00;
float D11 = RealPart(b[0] - D00 * Multiply(L10, Conjugate(L10)));
float InvD11 = 1.0 / D11;
float2 L20 = (b[2]) * InvD00;
float2 L21 = (b[1] - D00 * Multiply(L20, Conjugate(L10))) * InvD11;
float D22 = RealPart(b[0] - D00 * Multiply(L20, Conjugate(L20)) - D11 * Multiply(L21, Conjugate(L21)));
float InvD22 = 1.0 / D22;
float2 L30 = (b[3]) * InvD00;
float2 L31 = (b[2] - D00 * Multiply(L30, Conjugate(L10))) * InvD11;
float2 L32 = (b[1] - D00 * Multiply(L30, Conjugate(L20)) - D11 * Multiply(L31, Conjugate(L21))) * InvD22;
float D33 = RealPart(b[0] - D00 * Multiply(L30, Conjugate(L30)) - D11 * Multiply(L31, Conjugate(L31)) - D22 * Multiply(L32, Conjugate(L32)));
float InvD33 = 1.0 / D33;
float2 L40 = (b[4]) * InvD00;
float2 L41 = (b[3] - D00 * Multiply(L40, Conjugate(L10))) * InvD11;
float2 L42 = (b[2] - D00 * Multiply(L40, Conjugate(L20)) - D11 * Multiply(L41, Conjugate(L21))) * InvD22;
float2 L43 = (b[1] - D00 * Multiply(L40, Conjugate(L30)) - D11 * Multiply(L41, Conjugate(L31)) - D22 * Multiply(L42, Conjugate(L32))) * InvD33;
float D44 = RealPart(b[0] - D00 * Multiply(L40, Conjugate(L40)) - D11 * Multiply(L41, Conjugate(L41)) - D22 * Multiply(L42, Conjugate(L42)) - D33 * Multiply(L43, Conjugate(L43)));
float InvD44 = 1.0 / D44;
// Solve a linear system to get the relevant polynomial
float phase = mad(depth, wrapping_zone_parameters.y, wrapping_zone_parameters.y);
float2 circle_point;
sincos(phase, circle_point.y, circle_point.x);
float2 circle_point_pow2 = Multiply(circle_point, circle_point);
float2 c[5] = {
float2(1.0f,0.0f),
circle_point,
circle_point_pow2,
Multiply(circle_point, circle_point_pow2),
Multiply(circle_point_pow2, circle_point_pow2)
};
c[1] -= Multiply(L10, c[0]);
c[2] -= Multiply(L20, c[0]) + Multiply(L21, c[1]);
c[3] -= Multiply(L30, c[0]) + Multiply(L31, c[1]) + Multiply(L32, c[2]);
c[4] -= Multiply(L40, c[0]) + Multiply(L41, c[1]) + Multiply(L42, c[2]) + Multiply(L43, c[3]);
c[0] *= InvD00;
c[1] *= InvD11;
c[2] *= InvD22;
c[3] *= InvD33;
c[4] *= InvD44;
c[3] -= Multiply(Conjugate(L43), c[4]);
c[2] -= Multiply(Conjugate(L32), c[3]) + Multiply(Conjugate(L42), c[4]);
c[1] -= Multiply(Conjugate(L21), c[2]) + Multiply(Conjugate(L31), c[3]) + Multiply(Conjugate(L41), c[4]);
c[0] -= Multiply(Conjugate(L10), c[1]) + Multiply(Conjugate(L20), c[2]) + Multiply(Conjugate(L30), c[3]) + Multiply(Conjugate(L40), c[4]);
// Compute roots of the polynomial
float2 pRoot[4];
SolveQuarticNeumark(pRoot, Conjugate(c[4]), Conjugate(c[3]), Conjugate(c[2]), Conjugate(c[1]), Conjugate(c[0]));
// Figure out how to weight the weights
float depth_parameter = circleToParameter(circle_point);
float weight_factor[5];
weight_factor[0] = overestimation;
[unroll]
for (int i = 0; i != 4; ++i)
{
float root_parameter = circleToParameter(pRoot[i]);
weight_factor[i + 1] = getRootWeightFactor(depth_parameter, root_parameter, wrapping_zone_parameters);
}
// Compute the appropriate linear combination of weights
float2 z[5] = { circle_point, pRoot[0], pRoot[1], pRoot[2], pRoot[3] };
float f0 = weight_factor[0];
float f1 = weight_factor[1];
float f2 = weight_factor[2];
float f3 = weight_factor[3];
float f4 = weight_factor[4];
float2 f01 = Divide(f1 - f0, z[1] - z[0]);
float2 f12 = Divide(f2 - f1, z[2] - z[1]);
float2 f23 = Divide(f3 - f2, z[3] - z[2]);
float2 f34 = Divide(f4 - f3, z[4] - z[3]);
float2 f012 = Divide(f12 - f01, z[2] - z[0]);
float2 f123 = Divide(f23 - f12, z[3] - z[1]);
float2 f234 = Divide(f34 - f23, z[4] - z[2]);
float2 f0123 = Divide(f123 - f012, z[3] - z[0]);
float2 f1234 = Divide(f234 - f123, z[4] - z[1]);
float2 f01234 = Divide(f1234 - f0123, z[4] - z[0]);
float2 polynomial[5];
polynomial[0] = f01234;
polynomial[1] = polynomial[0];
polynomial[0] = f0123 - Multiply(polynomial[0], z[3]);
polynomial[2] = polynomial[1];
polynomial[1] = polynomial[0] - Multiply(polynomial[1], z[2]);
polynomial[0] = f012 - Multiply(polynomial[0], z[2]);
polynomial[3] = polynomial[2];
polynomial[2] = polynomial[1] - Multiply(polynomial[2], z[1]);
polynomial[1] = polynomial[0] - Multiply(polynomial[1], z[1]);
polynomial[0] = f01 - Multiply(polynomial[0], z[1]);
polynomial[4] = polynomial[3];
polynomial[3] = polynomial[2] - Multiply(polynomial[3], z[0]);
polynomial[2] = polynomial[1] - Multiply(polynomial[2], z[0]);
polynomial[1] = polynomial[0] - Multiply(polynomial[1], z[0]);
polynomial[0] = f0 - Multiply(polynomial[0], z[0]);
float weight_sum = 0;
weight_sum += RealPart(Multiply(b[0], polynomial[0]));
weight_sum += RealPart(Multiply(b[1], polynomial[1]));
weight_sum += RealPart(Multiply(b[2], polynomial[2]));
weight_sum += RealPart(Multiply(b[3], polynomial[3]));
weight_sum += RealPart(Multiply(b[4], polynomial[4]));
// Turn the normalized absorbance into transmittance
return exp(-b_0 * weight_sum);
}
#endif

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6aa37c869516b6b4abf1ddc58653e893
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
float2 _LogViewDepthMinDelta;
float WarpDepth(float vd)
{
//return (log(vd) - _LogViewDepthMinDelta.x) / _LogViewDepthMinDelta.y * 2 - 1;
//return (log(vd) - _LogViewDepthMinDelta.x) / _LogViewDepthMinDelta.y;
//return vd;
//return log(vd) * 2 - 1;
//return (vd - _LogViewDepthMinDelta.x) / _LogViewDepthMinDelta.y * 2 - 1;
//return vd * 2 - 1;
//return (log(vd * 2 - 1) - _LogViewDepthMinDelta.x) / _LogViewDepthMinDelta.y * 2 - 1;
//return (log(vd) - _LogViewDepthMinDelta.x) / _LogViewDepthMinDelta.y * 2 - 1;
return saturate((log(vd) - _LogViewDepthMinDelta.x) / _LogViewDepthMinDelta.y) * 2 - 1;
}
// NOTE: vd (z/depth) are computed from linear view-space depth values

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 174d761f60c0b3543996519b15424503
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 53073e24ceed8514ea066c180cf5cdd8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -26,7 +26,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 2f83dd6eb48aa6848950f8a0f61a70de, type: 3} m_Script: {fileID: 11500000, guid: 2f83dd6eb48aa6848950f8a0f61a70de, type: 3}
m_Name: LinkedListOITFeature m_Name: LinkedListOITFeature
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_Active: 1 m_Active: 0
settings: settings:
RenderPassEvent: 500 RenderPassEvent: 500
ComputeShader: {fileID: 7200000, guid: eb30737736968d64e99cd6b7e2cc9efb, type: 3} ComputeShader: {fileID: 7200000, guid: eb30737736968d64e99cd6b7e2cc9efb, type: 3}
@ -125,6 +125,48 @@ MonoBehaviour:
cutoffThreshold: 0.2 cutoffThreshold: 0.2
binaryValue: 0.9 binaryValue: 0.9
flags: 13 flags: 13
--- !u!114 &-7198069835786880352
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4854a869b24c78844a4c077509f99b4f, type: 3}
m_Name: MOITFeature
m_EditorClassIdentifier:
m_Active: 1
settings:
renderPassEvent: 450
settings:
momentsCount: 4
momentPrecision: 32
trigonometric: 0
boundsType: 1
onlyVisibleRenderers: 0
layerMask:
serializedVersion: 2
m_Bits: 4294967295
renderQueueMin: 2501
renderQueueMax: 3000
compositeMaterial: {fileID: 2100000, guid: f5b0014f90114064c8200b5c9ac93932, type: 2}
sortBackToFront: 0
debugMakeMOITTexGlobal: 0
biasSettings:
Moments4Half: 0.00006
Moments4Single: 0.0000005
Moments6Half: 0.0006
Moments6Single: 0.000005
Moments8Half: 0.0025
Moments8Single: 0.00005
Trigonometric2Half: 0.0004
Trigonometric2Single: 0.0000004
Trigonometric3Half: 0.00065
Trigonometric3Single: 0.0000008
Trigonometric4Half: 0.00085
Trigonometric4Single: 0.0000015
--- !u!114 &-7143664486661302651 --- !u!114 &-7143664486661302651
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -376,8 +418,9 @@ MonoBehaviour:
- {fileID: -7143664486661302651} - {fileID: -7143664486661302651}
- {fileID: 654879339943129409} - {fileID: 654879339943129409}
- {fileID: -8044648393505963816} - {fileID: -8044648393505963816}
m_RendererFeatureMap: bc3f630842f2e70dd6a559c442a94bfd4529d15534f2d3de228858dca8d12222716523fbf3439fdb7a327b7bff4bdd446ac59dfa966ffa88ca6373cd5da9013d6cff55ca297e5e908a7b3653203b82383b2141bb05fbe69aec5704e48e2763e90bc6ff9f19caa7686c79a6bb3bb89a50faad0fe75217cdb485d6fa85ff9adc9c41710d3c4a991609d8002992e8aa5b90 - {fileID: -7198069835786880352}
m_UseNativeRenderPass: 0 m_RendererFeatureMap: bc3f630842f2e70dd6a559c442a94bfd4529d15534f2d3de228858dca8d12222716523fbf3439fdb7a327b7bff4bdd446ac59dfa966ffa88ca6373cd5da9013d6cff55ca297e5e908a7b3653203b82383b2141bb05fbe69aec5704e48e2763e90bc6ff9f19caa7686c79a6bb3bb89a50faad0fe75217cdb485d6fa85ff9adc9c41710d3c4a991609d8002992e8aa5b90a0fa77119f511b9c
m_UseNativeRenderPass: 1
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2} postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
shaders: shaders:
blitPS: {fileID: 4800000, guid: c17132b1f77d20942aa75f8429c0f8bc, type: 3} blitPS: {fileID: 4800000, guid: c17132b1f77d20942aa75f8429c0f8bc, type: 3}