Files
ZHGD_Web/Assets/Art/Art Plugins/Horizon Based Ambient Occlusion/Runtime/HBAO.cs

1824 lines
77 KiB
C#
Raw Normal View History

2025-07-13 23:16:20 +08:00
using UnityEngine;
using UnityEngine.Rendering;
#if ENABLE_VR_MODULE && ENABLE_VR
using XRSettings = UnityEngine.XR.XRSettings;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using System.Collections.Generic;
namespace HorizonBasedAmbientOcclusion
{
[ExecuteInEditMode, ImageEffectAllowedInSceneView, AddComponentMenu("Image Effects/HBAO")]
[RequireComponent(typeof(Camera))]
public class HBAO : MonoBehaviour
{
public enum Preset
{
FastestPerformance,
FastPerformance,
Normal,
HighQuality,
HighestQuality,
Custom
}
public enum PipelineStage
{
BeforeImageEffectsOpaque,
AfterLighting,
BeforeReflections
}
public enum Quality
{
Lowest,
Low,
Medium,
High,
Highest
}
public enum Resolution
{
Full,
Half
}
public enum NoiseType
{
Dither,
InterleavedGradientNoise,
SpatialDistribution
}
public enum Deinterleaving
{
Disabled,
x4
}
public enum DebugMode
{
Disabled,
AOOnly,
ColorBleedingOnly,
SplitWithoutAOAndWithAO,
SplitWithAOAndAOOnly,
SplitWithoutAOAndAOOnly,
ViewNormals
}
public enum BlurType
{
None,
Narrow,
Medium,
Wide,
ExtraWide
}
public enum PerPixelNormals
{
GBuffer,
Camera,
Reconstruct
}
public enum VarianceClipping
{
Disabled,
_4Tap,
_8Tap
}
public Shader hbaoShader;
[Serializable]
public struct Presets
{
public Preset preset;
[SerializeField]
public static Presets defaults
{
get
{
return new Presets
{
preset = Preset.Normal
};
}
}
}
[Serializable]
public struct GeneralSettings
{
[Tooltip("The stage the AO is injected into the rendering pipeline.")]
[Space(6)]
public PipelineStage pipelineStage;
[Tooltip("The quality of the AO.")]
[Space(10)]
public Quality quality;
[Tooltip("The deinterleaving factor.")]
public Deinterleaving deinterleaving;
[Tooltip("The resolution at which the AO is calculated.")]
public Resolution resolution;
[Tooltip("The type of noise to use.")]
[Space(10)]
public NoiseType noiseType;
[Tooltip("The debug mode actually displayed on screen.")]
[Space(10)]
public DebugMode debugMode;
[SerializeField]
public static GeneralSettings defaults
{
get
{
return new GeneralSettings
{
pipelineStage = PipelineStage.BeforeImageEffectsOpaque,
quality = Quality.Medium,
deinterleaving = Deinterleaving.Disabled,
resolution = Resolution.Full,
noiseType = NoiseType.Dither,
debugMode = DebugMode.Disabled
};
}
}
}
[Serializable]
public struct AOSettings
{
[Tooltip("AO radius: this is the distance outside which occluders are ignored.")]
[Space(6), Range(0.25f, 5f)]
public float radius;
[Tooltip("Maximum radius in pixels: this prevents the radius to grow too much with close-up " +
"object and impact on performances.")]
[Range(16, 256)]
public float maxRadiusPixels;
[Tooltip("For low-tessellated geometry, occlusion variations tend to appear at creases and " +
"ridges, which betray the underlying tessellation. To remove these artifacts, we use " +
"an angle bias parameter which restricts the hemisphere.")]
[Range(0, 0.5f)]
public float bias;
[Tooltip("This value allows to scale up the ambient occlusion values.")]
[Range(0, 4)]
public float intensity;
[Tooltip("Enable/disable MultiBounce approximation.")]
public bool useMultiBounce;
[Tooltip("MultiBounce approximation influence.")]
[Range(0, 1)]
public float multiBounceInfluence;
[Tooltip("The amount of AO offscreen samples are contributing.")]
[Range(0, 1)]
public float offscreenSamplesContribution;
[Tooltip("The max distance to display AO.")]
[Space(10)]
public float maxDistance;
[Tooltip("The distance before max distance at which AO start to decrease.")]
public float distanceFalloff;
[Tooltip("The type of per pixel normals to use.")]
[Space(10)]
public PerPixelNormals perPixelNormals;
[Tooltip("This setting allow you to set the base color if the AO, the alpha channel value is unused.")]
[Space(10)]
public Color baseColor;
[SerializeField]
public static AOSettings defaults
{
get
{
return new AOSettings
{
radius = 0.8f,
maxRadiusPixels = 128f,
bias = 0.05f,
intensity = 1f,
useMultiBounce = false,
multiBounceInfluence = 1f,
offscreenSamplesContribution = 0f,
maxDistance = 150f,
distanceFalloff = 50f,
perPixelNormals = PerPixelNormals.GBuffer,
baseColor = Color.black
};
}
}
}
[Serializable]
public struct TemporalFilterSettings
{
[Space(6)]
public bool enabled;
[Tooltip("The type of variance clipping to use.")]
public VarianceClipping varianceClipping;
[SerializeField]
public static TemporalFilterSettings defaults
{
get
{
return new TemporalFilterSettings
{
enabled = false,
varianceClipping = VarianceClipping._4Tap
};
}
}
}
[Serializable]
public struct BlurSettings
{
[Tooltip("The type of blur to use.")]
[Space(6)]
public BlurType type;
[Tooltip("This parameter controls the depth-dependent weight of the bilateral filter, to " +
"avoid bleeding across edges. A zero sharpness is a pure Gaussian blur. Increasing " +
"the blur sharpness removes bleeding by using lower weights for samples with large " +
"depth delta from the current pixel.")]
[Space(10), Range(0, 16)]
public float sharpness;
[SerializeField]
public static BlurSettings defaults
{
get
{
return new BlurSettings
{
type = BlurType.Medium,
sharpness = 8f
};
}
}
}
[Serializable]
public struct ColorBleedingSettings
{
[Space(6)]
public bool enabled;
[Tooltip("This value allows to control the saturation of the color bleeding.")]
[Space(10), Range(0, 4)]
public float saturation;
[Tooltip("This value allows to scale the contribution of the color bleeding samples.")]
[Range(0, 32)]
public float albedoMultiplier;
[Tooltip("Use masking on emissive pixels")]
[Range(0, 1)]
public float brightnessMask;
[Tooltip("Brightness level where masking starts/ends")]
[MinMaxSlider(0, 2)]
public Vector2 brightnessMaskRange;
[SerializeField]
public static ColorBleedingSettings defaults
{
get
{
return new ColorBleedingSettings
{
enabled = false,
saturation = 1f,
albedoMultiplier = 4f,
brightnessMask = 1f,
brightnessMaskRange = new Vector2(0.0f, 0.5f)
};
}
}
}
[AttributeUsage(AttributeTargets.Field)]
public class SettingsGroup : Attribute { }
[SerializeField, SettingsGroup]
private Presets m_Presets = Presets.defaults;
public Presets presets
{
get { return m_Presets; }
set { m_Presets = value; }
}
[SerializeField, SettingsGroup]
private GeneralSettings m_GeneralSettings = GeneralSettings.defaults;
public GeneralSettings generalSettings
{
get { return m_GeneralSettings; }
set { m_GeneralSettings = value; }
}
[SerializeField, SettingsGroup]
private AOSettings m_AOSettings = AOSettings.defaults;
public AOSettings aoSettings
{
get { return m_AOSettings; }
set { m_AOSettings = value; }
}
[SerializeField, SettingsGroup]
private TemporalFilterSettings m_TemporalFilterSettings = TemporalFilterSettings.defaults;
public TemporalFilterSettings temporalFilterSettings
{
get { return m_TemporalFilterSettings; }
set { m_TemporalFilterSettings = value; }
}
[SerializeField, SettingsGroup]
private BlurSettings m_BlurSettings = BlurSettings.defaults;
public BlurSettings blurSettings
{
get { return m_BlurSettings; }
set { m_BlurSettings = value; }
}
[SerializeField, SettingsGroup]
private ColorBleedingSettings m_ColorBleedingSettings = ColorBleedingSettings.defaults;
public ColorBleedingSettings colorBleedingSettings
{
get { return m_ColorBleedingSettings; }
set { m_ColorBleedingSettings = value; }
}
public class MinMaxSliderAttribute : PropertyAttribute
{
public readonly float max;
public readonly float min;
public MinMaxSliderAttribute(float min, float max)
{
this.min = min;
this.max = max;
}
}
public Preset GetCurrentPreset()
{
return m_Presets.preset;
}
public void ApplyPreset(Preset preset)
{
if (preset == Preset.Custom)
{
m_Presets.preset = preset;
return;
}
var debugMode = generalSettings.debugMode;
m_GeneralSettings = GeneralSettings.defaults;
m_AOSettings = AOSettings.defaults;
m_ColorBleedingSettings = ColorBleedingSettings.defaults;
m_BlurSettings = BlurSettings.defaults;
SetDebugMode(debugMode);
switch (preset)
{
case Preset.FastestPerformance:
SetQuality(Quality.Lowest);
SetAoRadius(0.5f);
SetAoMaxRadiusPixels(64.0f);
SetBlurType(BlurType.ExtraWide);
break;
case Preset.FastPerformance:
SetQuality(Quality.Low);
SetAoRadius(0.5f);
SetAoMaxRadiusPixels(64.0f);
SetBlurType(BlurType.Wide);
break;
case Preset.HighQuality:
SetQuality(Quality.High);
SetAoRadius(1.0f);
break;
case Preset.HighestQuality:
SetQuality(Quality.Highest);
SetAoRadius(1.2f);
SetAoMaxRadiusPixels(256.0f);
SetBlurType(BlurType.Narrow);
break;
case Preset.Normal:
default:
break;
}
m_Presets.preset = preset;
}
public PipelineStage GetPipelineStage()
{
return m_GeneralSettings.pipelineStage;
}
public void SetPipelineStage(PipelineStage pipelineStage)
{
m_GeneralSettings.pipelineStage = pipelineStage;
}
public Quality GetQuality()
{
return m_GeneralSettings.quality;
}
public void SetQuality(Quality quality)
{
m_GeneralSettings.quality = quality;
}
public Deinterleaving GetDeinterleaving()
{
return m_GeneralSettings.deinterleaving;
}
public void SetDeinterleaving(Deinterleaving deinterleaving)
{
m_GeneralSettings.deinterleaving = deinterleaving;
}
public Resolution GetResolution()
{
return m_GeneralSettings.resolution;
}
public void SetResolution(Resolution resolution)
{
m_GeneralSettings.resolution = resolution;
}
public NoiseType GetNoiseType()
{
return m_GeneralSettings.noiseType;
}
public void SetNoiseType(NoiseType noiseType)
{
m_GeneralSettings.noiseType = noiseType;
}
public DebugMode GetDebugMode()
{
return m_GeneralSettings.debugMode;
}
public void SetDebugMode(DebugMode debugMode)
{
m_GeneralSettings.debugMode = debugMode;
}
public float GetAoRadius()
{
return m_AOSettings.radius;
}
public void SetAoRadius(float radius)
{
m_AOSettings.radius = Mathf.Clamp(radius, 0.25f, 5);
}
public float GetAoMaxRadiusPixels()
{
return m_AOSettings.maxRadiusPixels;
}
public void SetAoMaxRadiusPixels(float maxRadiusPixels)
{
m_AOSettings.maxRadiusPixels = Mathf.Clamp(maxRadiusPixels, 16, 256);
}
public float GetAoBias()
{
return m_AOSettings.bias;
}
public void SetAoBias(float bias)
{
m_AOSettings.bias = Mathf.Clamp(bias, 0, 0.5f);
}
public float GetAoOffscreenSamplesContribution()
{
return m_AOSettings.offscreenSamplesContribution;
}
public void SetAoOffscreenSamplesContribution(float contribution)
{
m_AOSettings.offscreenSamplesContribution = Mathf.Clamp01(contribution);
}
public float GetAoMaxDistance()
{
return m_AOSettings.maxDistance;
}
public void SetAoMaxDistance(float maxDistance)
{
m_AOSettings.maxDistance = maxDistance;
}
public float GetAoDistanceFalloff()
{
return m_AOSettings.distanceFalloff;
}
public void SetAoDistanceFalloff(float distanceFalloff)
{
m_AOSettings.distanceFalloff = distanceFalloff;
}
public PerPixelNormals GetAoPerPixelNormals()
{
return m_AOSettings.perPixelNormals;
}
public void SetAoPerPixelNormals(PerPixelNormals perPixelNormals)
{
m_AOSettings.perPixelNormals = perPixelNormals;
}
public Color GetAoColor()
{
return m_AOSettings.baseColor;
}
public void SetAoColor(Color color)
{
m_AOSettings.baseColor = color;
}
public float GetAoIntensity()
{
return m_AOSettings.intensity;
}
public void SetAoIntensity(float intensity)
{
m_AOSettings.intensity = Mathf.Clamp(intensity, 0, 4);
}
public bool UseMultiBounce()
{
return m_AOSettings.useMultiBounce;
}
public void EnableMultiBounce(bool enabled = true)
{
m_AOSettings.useMultiBounce = enabled;
}
public float GetAoMultiBounceInfluence()
{
return m_AOSettings.multiBounceInfluence;
}
public void SetAoMultiBounceInfluence(float multiBounceInfluence)
{
m_AOSettings.multiBounceInfluence = Mathf.Clamp01(multiBounceInfluence);
}
public bool IsTemporalFilterEnabled()
{
return m_TemporalFilterSettings.enabled;
}
public void EnableTemporalFilter(bool enabled = true)
{
m_TemporalFilterSettings.enabled = enabled;
}
public VarianceClipping GetTemporalFilterVarianceClipping()
{
return m_TemporalFilterSettings.varianceClipping;
}
public void SetTemporalFilterVarianceClipping(VarianceClipping varianceClipping)
{
m_TemporalFilterSettings.varianceClipping = varianceClipping;
}
public BlurType GetBlurType()
{
return m_BlurSettings.type;
}
public void SetBlurType(BlurType blurType)
{
m_BlurSettings.type = blurType;
}
public float GetBlurSharpness()
{
return m_BlurSettings.sharpness;
}
public void SetBlurSharpness(float sharpness)
{
m_BlurSettings.sharpness = Mathf.Clamp(sharpness, 0, 16);
}
public bool IsColorBleedingEnabled()
{
return m_ColorBleedingSettings.enabled;
}
public void EnableColorBleeding(bool enabled = true)
{
m_ColorBleedingSettings.enabled = enabled;
}
public float GetColorBleedingSaturation()
{
return m_ColorBleedingSettings.saturation;
}
public void SetColorBleedingSaturation(float saturation)
{
m_ColorBleedingSettings.saturation = Mathf.Clamp(saturation, 0, 4);
}
public float GetColorBleedingAlbedoMultiplier()
{
return m_ColorBleedingSettings.albedoMultiplier;
}
public void SetColorBleedingAlbedoMultiplier(float albedoMultiplier)
{
m_ColorBleedingSettings.albedoMultiplier = Mathf.Clamp(albedoMultiplier, 0, 32);
}
public float GetColorBleedingBrightnessMask()
{
return m_ColorBleedingSettings.brightnessMask;
}
public void SetColorBleedingBrightnessMask(float brightnessMask)
{
m_ColorBleedingSettings.brightnessMask = Mathf.Clamp01(brightnessMask);
}
public Vector2 GetColorBleedingBrightnessMaskRange()
{
return m_ColorBleedingSettings.brightnessMaskRange;
}
public void SetColorBleedingBrightnessMaskRange(Vector2 brightnessMaskRange)
{
brightnessMaskRange.x = Mathf.Clamp(brightnessMaskRange.x, 0, 2);
brightnessMaskRange.y = Mathf.Clamp(brightnessMaskRange.y, 0, 2);
brightnessMaskRange.x = Mathf.Min(brightnessMaskRange.x, brightnessMaskRange.y);
m_ColorBleedingSettings.brightnessMaskRange = brightnessMaskRange;
}
private static class Pass
{
public const int AO = 0;
public const int AO_Deinterleaved = 1;
public const int Deinterleave_Depth = 2;
public const int Deinterleave_Normals = 3;
public const int Atlas_AO_Deinterleaved = 4;
public const int Reinterleave_AO = 5;
public const int Blur = 6;
public const int Temporal_Filter = 7;
public const int Copy = 8;
public const int Composite = 9;
public const int Composite_AfterLighting = 10;
public const int Composite_BeforeReflections = 11;
public const int Composite_BlendAO = 12;
public const int Composite_BlendCB = 13;
public const int Debug_ViewNormals = 14;
}
private static class ShaderProperties
{
public static int mainTex;
public static int hbaoTex;
public static int tempTex;
public static int tempTex2;
public static int noiseTex;
public static int depthTex;
public static int normalsTex;
public static int[] depthSliceTex;
public static int[] normalsSliceTex;
public static int[] aoSliceTex;
public static int[] deinterleaveOffset;
public static int atlasOffset;
public static int jitter;
public static int uvTransform;
public static int inputTexelSize;
public static int aoTexelSize;
public static int deinterleavedAOTexelSize;
public static int reinterleavedAOTexelSize;
public static int uvToView;
//public static int worldToCameraMatrix;
public static int targetScale;
public static int radius;
public static int maxRadiusPixels;
public static int negInvRadius2;
public static int angleBias;
public static int aoMultiplier;
public static int intensity;
public static int multiBounceInfluence;
public static int offscreenSamplesContrib;
public static int maxDistance;
public static int distanceFalloff;
public static int baseColor;
public static int colorBleedSaturation;
public static int albedoMultiplier;
public static int colorBleedBrightnessMask;
public static int colorBleedBrightnessMaskRange;
public static int blurDeltaUV;
public static int blurSharpness;
public static int temporalParams;
static ShaderProperties()
{
mainTex = Shader.PropertyToID("_MainTex");
hbaoTex = Shader.PropertyToID("_HBAOTex");
tempTex = Shader.PropertyToID("_TempTex");
tempTex2 = Shader.PropertyToID("_TempTex2");
noiseTex = Shader.PropertyToID("_NoiseTex");
depthTex = Shader.PropertyToID("_DepthTex");
normalsTex = Shader.PropertyToID("_NormalsTex");
depthSliceTex = new int[4 * 4];
normalsSliceTex = new int[4 * 4];
aoSliceTex = new int[4 * 4];
for (int i = 0; i < 4 * 4; i++)
{
depthSliceTex[i] = Shader.PropertyToID("_DepthSliceTex" + i);
normalsSliceTex[i] = Shader.PropertyToID("_NormalsSliceTex" + i);
aoSliceTex[i] = Shader.PropertyToID("_AOSliceTex" + i);
}
deinterleaveOffset = new int[] {
Shader.PropertyToID("_Deinterleave_Offset00"),
Shader.PropertyToID("_Deinterleave_Offset10"),
Shader.PropertyToID("_Deinterleave_Offset01"),
Shader.PropertyToID("_Deinterleave_Offset11")
};
atlasOffset = Shader.PropertyToID("_AtlasOffset");
jitter = Shader.PropertyToID("_Jitter");
uvTransform = Shader.PropertyToID("_UVTransform");
inputTexelSize = Shader.PropertyToID("_Input_TexelSize");
aoTexelSize = Shader.PropertyToID("_AO_TexelSize");
deinterleavedAOTexelSize = Shader.PropertyToID("_DeinterleavedAO_TexelSize");
reinterleavedAOTexelSize = Shader.PropertyToID("_ReinterleavedAO_TexelSize");
uvToView = Shader.PropertyToID("_UVToView");
//worldToCameraMatrix = Shader.PropertyToID("_WorldToCameraMatrix");
targetScale = Shader.PropertyToID("_TargetScale");
radius = Shader.PropertyToID("_Radius");
maxRadiusPixels = Shader.PropertyToID("_MaxRadiusPixels");
negInvRadius2 = Shader.PropertyToID("_NegInvRadius2");
angleBias = Shader.PropertyToID("_AngleBias");
aoMultiplier = Shader.PropertyToID("_AOmultiplier");
intensity = Shader.PropertyToID("_Intensity");
multiBounceInfluence = Shader.PropertyToID("_MultiBounceInfluence");
offscreenSamplesContrib = Shader.PropertyToID("_OffscreenSamplesContrib");
maxDistance = Shader.PropertyToID("_MaxDistance");
distanceFalloff = Shader.PropertyToID("_DistanceFalloff");
baseColor = Shader.PropertyToID("_BaseColor");
colorBleedSaturation = Shader.PropertyToID("_ColorBleedSaturation");
albedoMultiplier = Shader.PropertyToID("_AlbedoMultiplier");
colorBleedBrightnessMask = Shader.PropertyToID("_ColorBleedBrightnessMask");
colorBleedBrightnessMaskRange = Shader.PropertyToID("_ColorBleedBrightnessMaskRange");
blurDeltaUV = Shader.PropertyToID("_BlurDeltaUV");
blurSharpness = Shader.PropertyToID("_BlurSharpness");
temporalParams = Shader.PropertyToID("_TemporalParams");
}
public static string GetOrthographicOrDeferredKeyword(bool orthographic, GeneralSettings settings)
{
// need to check that integrationStage is not BeforeImageEffectOpaque as Gbuffer0 is not available in this case
return orthographic ? "ORTHOGRAPHIC_PROJECTION" : settings.pipelineStage != PipelineStage.BeforeImageEffectsOpaque ? "DEFERRED_SHADING" : "__";
}
public static string GetQualityKeyword(GeneralSettings settings)
{
switch (settings.quality)
{
case Quality.Lowest:
return "QUALITY_LOWEST";
case Quality.Low:
return "QUALITY_LOW";
case Quality.Medium:
return "QUALITY_MEDIUM";
case Quality.High:
return "QUALITY_HIGH";
case Quality.Highest:
return "QUALITY_HIGHEST";
default:
return "QUALITY_MEDIUM";
}
}
public static string GetNoiseKeyword(GeneralSettings settings)
{
switch (settings.noiseType)
{
case NoiseType.InterleavedGradientNoise:
return "INTERLEAVED_GRADIENT_NOISE";
case NoiseType.Dither:
case NoiseType.SpatialDistribution:
default:
return "__";
}
}
public static string GetDeinterleavingKeyword(GeneralSettings settings)
{
switch (settings.deinterleaving)
{
case Deinterleaving.x4:
return "DEINTERLEAVED";
case Deinterleaving.Disabled:
default:
return "__";
}
}
public static string GetDebugKeyword(GeneralSettings settings)
{
switch (settings.debugMode)
{
case DebugMode.AOOnly:
return "DEBUG_AO";
case DebugMode.ColorBleedingOnly:
return "DEBUG_COLORBLEEDING";
case DebugMode.SplitWithoutAOAndWithAO:
return "DEBUG_NOAO_AO";
case DebugMode.SplitWithAOAndAOOnly:
return "DEBUG_AO_AOONLY";
case DebugMode.SplitWithoutAOAndAOOnly:
return "DEBUG_NOAO_AOONLY";
case DebugMode.Disabled:
default:
return "__";
}
}
public static string GetMultibounceKeyword(AOSettings settings)
{
return settings.useMultiBounce ? "MULTIBOUNCE" : "__";
}
public static string GetOffscreenSamplesContributionKeyword(AOSettings settings)
{
return settings.offscreenSamplesContribution > 0 ? "OFFSCREEN_SAMPLES_CONTRIBUTION" : "__";
}
public static string GetPerPixelNormalsKeyword(AOSettings settings)
{
switch (settings.perPixelNormals)
{
case PerPixelNormals.Camera:
return "NORMALS_CAMERA";
case PerPixelNormals.Reconstruct:
return "NORMALS_RECONSTRUCT";
case PerPixelNormals.GBuffer:
default:
return "__";
}
}
public static string GetBlurRadiusKeyword(BlurSettings settings)
{
switch (settings.type)
{
case BlurType.Narrow:
return "BLUR_RADIUS_2";
case BlurType.Medium:
return "BLUR_RADIUS_3";
case BlurType.Wide:
return "BLUR_RADIUS_4";
case BlurType.ExtraWide:
return "BLUR_RADIUS_5";
case BlurType.None:
default:
return "BLUR_RADIUS_3";
}
}
public static string GetVarianceClippingKeyword(TemporalFilterSettings settings)
{
switch (settings.varianceClipping)
{
case VarianceClipping._4Tap:
return "VARIANCE_CLIPPING_4TAP";
case VarianceClipping._8Tap:
return "VARIANCE_CLIPPING_8TAP";
case VarianceClipping.Disabled:
default:
return "__";
}
}
public static string GetColorBleedingKeyword(ColorBleedingSettings settings)
{
return settings.enabled ? "COLOR_BLEEDING" : "__";
}
public static string GetLightingLogEncodedKeyword(bool hdr)
{
return hdr ? "__" : "LIGHTING_LOG_ENCODED";
}
}
public enum StereoRenderingMode
{
MultiPass,
SinglePassInstanced
};
private static class MersenneTwister
{
// Mersenne-Twister random numbers in [0,1).
public static float[] Numbers = new float[] {
//0.463937f,0.340042f,0.223035f,0.468465f,0.322224f,0.979269f,0.031798f,0.973392f,0.778313f,0.456168f,0.258593f,0.330083f,0.387332f,0.380117f,0.179842f,0.910755f,
//0.511623f,0.092933f,0.180794f,0.620153f,0.101348f,0.556342f,0.642479f,0.442008f,0.215115f,0.475218f,0.157357f,0.568868f,0.501241f,0.629229f,0.699218f,0.707733f
0.556725f,0.005520f,0.708315f,0.583199f,0.236644f,0.992380f,0.981091f,0.119804f,0.510866f,0.560499f,0.961497f,0.557862f,0.539955f,0.332871f,0.417807f,0.920779f,
0.730747f,0.076690f,0.008562f,0.660104f,0.428921f,0.511342f,0.587871f,0.906406f,0.437980f,0.620309f,0.062196f,0.119485f,0.235646f,0.795892f,0.044437f,0.617311f
};
}
private static readonly Vector2[] s_jitter = new Vector2[4 * 4];
private static readonly float[] s_temporalRotations = { 60.0f, 300.0f, 180.0f, 240.0f, 120.0f, 0.0f };
private static readonly float[] s_temporalOffsets = { 0.0f, 0.5f, 0.25f, 0.75f };
private Material material { get; set; }
private Camera hbaoCamera { get; set; }
private CommandBuffer cmdBuffer { get; set; }
private int width { get; set; }
private int height { get; set; }
private bool stereoActive { get; set; }
private int xrActiveEye { get; set; }
private StereoRenderingMode stereoRenderingMode { get; set; }
private int screenWidth { get; set; }
private int screenHeight { get; set; }
private int aoWidth { get; set; }
private int aoHeight { get; set; }
private int reinterleavedAoWidth { get; set; }
private int reinterleavedAoHeight { get; set; }
private int deinterleavedAoWidth { get; set; }
private int deinterleavedAoHeight { get; set; }
private int frameCount { get; set; }
private bool motionVectorsSupported { get; set; }
private RenderTexture aoHistoryBuffer { get; set; }
private RenderTexture colorBleedingHistoryBuffer { get; set; }
private Texture2D noiseTex { get; set; }
private Mesh fullscreenTriangle
{
get
{
if (m_FullscreenTriangle != null)
return m_FullscreenTriangle;
m_FullscreenTriangle = new Mesh { name = "Fullscreen Triangle" };
// Because we have to support older platforms (GLES2/3, DX9 etc) we can't do all of
// this directly in the vertex shader using vertex ids :(
m_FullscreenTriangle.SetVertices(new List<Vector3>
{
new Vector3(-1f, -1f, 0f),
new Vector3(-1f, 3f, 0f),
new Vector3( 3f, -1f, 0f)
});
m_FullscreenTriangle.SetIndices(new[] { 0, 1, 2 }, MeshTopology.Triangles, 0, false);
m_FullscreenTriangle.UploadMeshData(false);
return m_FullscreenTriangle;
}
}
private CameraEvent cameraEvent
{
get
{
// Forces BeforeImageEffectsOpaque as soon as normal display mode isn't used,
// the component still display its integration stage but we force it here to ensure
// all debug modes display correctly.
if (generalSettings.debugMode != DebugMode.Disabled)
return CameraEvent.BeforeImageEffectsOpaque;
switch (generalSettings.pipelineStage)
{
case PipelineStage.BeforeReflections:
return CameraEvent.BeforeReflections;
case PipelineStage.AfterLighting:
return CameraEvent.AfterLighting;
case PipelineStage.BeforeImageEffectsOpaque:
default:
return CameraEvent.BeforeImageEffectsOpaque;
}
}
}
private bool isCommandBufferDirty
{
get
{
if (m_IsCommandBufferDirty || m_PreviousPipelineStage != generalSettings.pipelineStage || m_PreviousResolution != generalSettings.resolution ||
m_PreviousDebugMode != generalSettings.debugMode || m_PreviousAllowHDR != hbaoCamera.allowHDR || m_PreviousWidth != width || m_PreviousHeight != height ||
m_PreviousDeinterleaving != generalSettings.deinterleaving || m_PreviousBlurAmount != blurSettings.type || m_PreviousUseMultibounce != aoSettings.useMultiBounce ||
m_PreviousColorBleedingEnabled != colorBleedingSettings.enabled || m_PreviousTemporalFilterEnabled != temporalFilterSettings.enabled ||
m_PreviousRenderingPath != hbaoCamera.actualRenderingPath)
{
m_PreviousPipelineStage = generalSettings.pipelineStage;
m_PreviousResolution = generalSettings.resolution;
m_PreviousDebugMode = generalSettings.debugMode;
m_PreviousAllowHDR = hbaoCamera.allowHDR;
m_PreviousWidth = width;
m_PreviousHeight = height;
m_PreviousDeinterleaving = generalSettings.deinterleaving;
m_PreviousBlurAmount = blurSettings.type;
m_PreviousUseMultibounce = aoSettings.useMultiBounce;
m_PreviousColorBleedingEnabled = colorBleedingSettings.enabled;
m_PreviousTemporalFilterEnabled = temporalFilterSettings.enabled;
m_PreviousRenderingPath = hbaoCamera.actualRenderingPath;
return true;
}
return false;
}
set
{
m_IsCommandBufferDirty = value;
}
}
private bool isHistoryBufferDirty
{
get
{
if (aoHistoryBuffer == null || (colorBleedingSettings.enabled && colorBleedingHistoryBuffer == null) ||
m_PreviousTemporalFilterEnabled != temporalFilterSettings.enabled ||
m_PreviousResolution != generalSettings.resolution ||
m_PreviousColorBleedingEnabled != colorBleedingSettings.enabled ||
m_PrevStereoRenderingMode != stereoRenderingMode)
{
m_PreviousTemporalFilterEnabled = temporalFilterSettings.enabled;
m_PreviousResolution = generalSettings.resolution;
m_PreviousColorBleedingEnabled = colorBleedingSettings.enabled;
m_PrevStereoRenderingMode = stereoRenderingMode;
return true;
}
return false;
}
}
private static RenderTextureFormat defaultHDRRenderTextureFormat
{
get
{
#if UNITY_ANDROID || UNITY_IPHONE || UNITY_TVOS || UNITY_SWITCH || UNITY_EDITOR
RenderTextureFormat format = RenderTextureFormat.RGB111110Float;
#if UNITY_EDITOR
var target = EditorUserBuildSettings.activeBuildTarget;
if (target != BuildTarget.Android && target != BuildTarget.iOS && target != BuildTarget.tvOS && target != BuildTarget.Switch)
return RenderTextureFormat.DefaultHDR;
#endif // UNITY_EDITOR
if (SystemInfo.SupportsRenderTextureFormat(format))
return format;
#endif // UNITY_ANDROID || UNITY_IPHONE || UNITY_TVOS || UNITY_SWITCH || UNITY_EDITOR
return RenderTextureFormat.DefaultHDR;
}
}
private RenderTextureFormat sourceFormat { get { return hbaoCamera.allowHDR ? defaultHDRRenderTextureFormat : RenderTextureFormat.Default; } }
//private static RenderTextureFormat colorFormat { get { return SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB64) ? RenderTextureFormat.ARGB64 : RenderTextureFormat.ARGB32; } }
private static RenderTextureFormat colorFormat { get { return SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf) ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.Default; } }
private static RenderTextureFormat depthFormat { get { return SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RFloat) ? RenderTextureFormat.RFloat : RenderTextureFormat.RHalf; } }
private static RenderTextureFormat normalsFormat { get { return SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB2101010) ? RenderTextureFormat.ARGB2101010 : RenderTextureFormat.Default; } }
private static bool isLinearColorSpace { get { return QualitySettings.activeColorSpace == ColorSpace.Linear; } }
private bool renderingInSceneView { get { return hbaoCamera.cameraType == CameraType.SceneView; } }
private RenderTextureDescriptor m_sourceDescriptor;
private string[] m_ShaderKeywords;
private Vector4[] m_UVToViewPerEye = new Vector4[2];
private float[] m_RadiusPerEye = new float[2];
private bool m_IsCommandBufferDirty;
private Mesh m_FullscreenTriangle;
private PipelineStage? m_PreviousPipelineStage;
private Resolution? m_PreviousResolution;
private Deinterleaving? m_PreviousDeinterleaving;
private DebugMode? m_PreviousDebugMode;
private NoiseType? m_PreviousNoiseType;
private BlurType? m_PreviousBlurAmount;
private int m_PreviousWidth;
private int m_PreviousHeight;
private bool m_PreviousAllowHDR;
private bool m_PreviousUseMultibounce;
private bool m_PreviousColorBleedingEnabled;
private bool m_PreviousTemporalFilterEnabled;
private RenderingPath m_PreviousRenderingPath;
private StereoRenderingMode m_PrevStereoRenderingMode;
void OnEnable()
{
if (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.Depth))
{
Debug.LogWarning("HBAO shader is not supported on this platform.");
this.enabled = false;
return;
}
if (hbaoShader == null) hbaoShader = Shader.Find("Hidden/HBAO");
if (hbaoShader == null)
{
Debug.LogError("HBAO shader was not found...");
return;
}
if (!hbaoShader.isSupported)
{
Debug.LogWarning("HBAO shader is not supported on this platform.");
this.enabled = false;
return;
}
Initialize();
}
void OnDisable()
{
ClearCommandBuffer(cmdBuffer);
ReleaseHistoryBuffers();
if (material != null)
DestroyImmediate(material);
if (noiseTex != null)
DestroyImmediate(noiseTex);
if (fullscreenTriangle != null)
DestroyImmediate(fullscreenTriangle);
}
void OnPreRender()
{
if (hbaoShader == null || hbaoCamera == null) return;
FetchRenderParameters();
CheckParameters();
UpdateMaterialProperties();
UpdateShaderKeywords();
if (isCommandBufferDirty)
{
ClearCommandBuffer(cmdBuffer);
BuildCommandBuffer(cmdBuffer, cameraEvent);
hbaoCamera.AddCommandBuffer(cameraEvent, cmdBuffer);
isCommandBufferDirty = false;
}
}
void OnPostRender()
{
frameCount++;
}
void OnValidate()
{
if (hbaoShader == null || hbaoCamera == null) return;
CheckParameters();
}
private void Initialize()
{
m_sourceDescriptor = new RenderTextureDescriptor(0, 0);
hbaoCamera = GetComponent<Camera>();
hbaoCamera.forceIntoRenderTexture = true;
material = new Material(hbaoShader);
material.hideFlags = HideFlags.HideAndDontSave;
// For platforms not supporting motion vectors texture
// https://docs.unity3d.com/ScriptReference/DepthTextureMode.MotionVectors.html
motionVectorsSupported = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGHalf);
cmdBuffer = new CommandBuffer { name = "HBAO" };
isCommandBufferDirty = true;
}
private void FetchRenderParameters()
{
#if !UNITY_SWITCH && ENABLE_VR_MODULE && ENABLE_VR
if (hbaoCamera.stereoEnabled)
{
var xrDesc = XRSettings.eyeTextureDesc;
stereoRenderingMode = StereoRenderingMode.MultiPass;
#if UNITY_STANDALONE || UNITY_EDITOR || UNITY_PS4 || UNITY_PS5 || UNITY_ANDROID
if (xrDesc.dimension == TextureDimension.Tex2DArray)
stereoRenderingMode = StereoRenderingMode.SinglePassInstanced;
#endif
width = xrDesc.width;
height = xrDesc.height;
m_sourceDescriptor = xrDesc;
xrActiveEye = (int)hbaoCamera.stereoActiveEye;
screenWidth = XRSettings.eyeTextureWidth;
screenHeight = XRSettings.eyeTextureHeight;
stereoActive = true;
}
else
#endif
{
width = hbaoCamera.pixelWidth;
height = hbaoCamera.pixelHeight;
m_sourceDescriptor.width = width;
m_sourceDescriptor.height = height;
xrActiveEye = 0;
screenWidth = width;
screenHeight = height;
stereoActive = false;
}
var downsamplingFactor = generalSettings.resolution == Resolution.Full ? 1 : generalSettings.deinterleaving == Deinterleaving.Disabled ? 2 : 1;
if (downsamplingFactor > 1)
{
aoWidth = (width + width % 2) / downsamplingFactor;
aoHeight = (height + height % 2) / downsamplingFactor;
}
else
{
aoWidth = width;
aoHeight = height;
}
reinterleavedAoWidth = width + (width % 4 == 0 ? 0 : 4 - (width % 4));
reinterleavedAoHeight = height + (height % 4 == 0 ? 0 : 4 - (height % 4));
deinterleavedAoWidth = reinterleavedAoWidth / 4;
deinterleavedAoHeight = reinterleavedAoHeight / 4;
}
private void AllocateHistoryBuffers()
{
ReleaseHistoryBuffers();
aoHistoryBuffer = GetScreenSpaceRT(widthOverride: aoWidth, heightOverride: aoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
if (colorBleedingSettings.enabled)
colorBleedingHistoryBuffer = GetScreenSpaceRT(widthOverride: aoWidth, heightOverride: aoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
// Clear history buffers to default
var lastActive = RenderTexture.active;
RenderTexture.active = aoHistoryBuffer;
GL.Clear(false, true, Color.white);
if (colorBleedingSettings.enabled)
{
RenderTexture.active = colorBleedingHistoryBuffer;
GL.Clear(false, true, new Color(0, 0, 0, 1));
}
RenderTexture.active = lastActive;
frameCount = 0;
}
private void ReleaseHistoryBuffers()
{
if (aoHistoryBuffer != null)
aoHistoryBuffer.Release();
if (colorBleedingHistoryBuffer != null)
colorBleedingHistoryBuffer.Release();
}
private void ClearCommandBuffer(CommandBuffer cmd)
{
if (cmd != null)
{
if (hbaoCamera != null)
{
hbaoCamera.RemoveCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, cmd);
hbaoCamera.RemoveCommandBuffer(CameraEvent.AfterLighting, cmd);
hbaoCamera.RemoveCommandBuffer(CameraEvent.BeforeReflections, cmd);
}
cmd.Clear();
}
}
private void BuildCommandBuffer(CommandBuffer cmd, CameraEvent cameraEvent)
{
// AO
if (generalSettings.deinterleaving == Deinterleaving.Disabled)
{
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.hbaoTex, widthOverride: aoWidth, heightOverride: aoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
AO(cmd);
}
else
{
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.hbaoTex, widthOverride: reinterleavedAoWidth, heightOverride: reinterleavedAoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
DeinterleavedAO(cmd);
}
// Blur
Blur(cmd);
// Temporal Filter
TemporalFilter(cmd);
// Composite
Composite(cmd, cameraEvent);
ReleaseTemporaryRT(cmd, ShaderProperties.hbaoTex);
//Debug.Log("CommandBuffer has been rebuilt");
}
private void AO(CommandBuffer cmd)
{
BlitFullscreenTriangleWithClear(cmd, BuiltinRenderTextureType.CameraTarget, ShaderProperties.hbaoTex, material, new Color(0, 0, 0, 1), Pass.AO);
}
private void DeinterleavedAO(CommandBuffer cmd)
{
// Deinterleave depth & normals (4x4)
for (int i = 0; i < 4; i++)
{
var rtsDepth = new RenderTargetIdentifier[] {
ShaderProperties.depthSliceTex[(i << 2) + 0],
ShaderProperties.depthSliceTex[(i << 2) + 1],
ShaderProperties.depthSliceTex[(i << 2) + 2],
ShaderProperties.depthSliceTex[(i << 2) + 3]
};
var rtsNormals = new RenderTargetIdentifier[] {
ShaderProperties.normalsSliceTex[(i << 2) + 0],
ShaderProperties.normalsSliceTex[(i << 2) + 1],
ShaderProperties.normalsSliceTex[(i << 2) + 2],
ShaderProperties.normalsSliceTex[(i << 2) + 3]
};
int offsetX = (i & 1) << 1; int offsetY = (i >> 1) << 1;
cmd.SetGlobalVector(ShaderProperties.deinterleaveOffset[0], new Vector2(offsetX + 0, offsetY + 0));
cmd.SetGlobalVector(ShaderProperties.deinterleaveOffset[1], new Vector2(offsetX + 1, offsetY + 0));
cmd.SetGlobalVector(ShaderProperties.deinterleaveOffset[2], new Vector2(offsetX + 0, offsetY + 1));
cmd.SetGlobalVector(ShaderProperties.deinterleaveOffset[3], new Vector2(offsetX + 1, offsetY + 1));
for (int j = 0; j < 4; j++)
{
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.depthSliceTex[j + 4 * i], widthOverride: deinterleavedAoWidth, heightOverride: deinterleavedAoHeight, colorFormat: depthFormat, readWrite: RenderTextureReadWrite.Linear, filter: FilterMode.Point);
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.normalsSliceTex[j + 4 * i], widthOverride: deinterleavedAoWidth, heightOverride: deinterleavedAoHeight, colorFormat: normalsFormat, readWrite: RenderTextureReadWrite.Linear, filter: FilterMode.Point);
}
BlitFullscreenTriangle(cmd, BuiltinRenderTextureType.CameraTarget, rtsDepth, material, Pass.Deinterleave_Depth); // outputs 4 render textures
BlitFullscreenTriangle(cmd, BuiltinRenderTextureType.CameraTarget, rtsNormals, material, Pass.Deinterleave_Normals); // outputs 4 render textures
}
// AO on each layer
for (int i = 0; i < 4 * 4; i++)
{
cmd.SetGlobalTexture(ShaderProperties.depthTex, ShaderProperties.depthSliceTex[i]);
cmd.SetGlobalTexture(ShaderProperties.normalsTex, ShaderProperties.normalsSliceTex[i]);
cmd.SetGlobalVector(ShaderProperties.jitter, s_jitter[i]);
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.aoSliceTex[i], widthOverride: deinterleavedAoWidth, heightOverride: deinterleavedAoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear, filter: FilterMode.Point);
BlitFullscreenTriangleWithClear(cmd, BuiltinRenderTextureType.CameraTarget, ShaderProperties.aoSliceTex[i], material, new Color(0, 0, 0, 1), Pass.AO_Deinterleaved); // ao
ReleaseTemporaryRT(cmd, ShaderProperties.depthSliceTex[i]);
ReleaseTemporaryRT(cmd, ShaderProperties.normalsSliceTex[i]);
}
// Atlas Deinterleaved AO, 4x4
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, widthOverride: reinterleavedAoWidth, heightOverride: reinterleavedAoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
for (int i = 0; i < 4 * 4; i++)
{
cmd.SetGlobalVector(ShaderProperties.atlasOffset, new Vector2(((i & 1) + (((i & 7) >> 2) << 1)) * deinterleavedAoWidth, (((i & 3) >> 1) + ((i >> 3) << 1)) * deinterleavedAoHeight));
BlitFullscreenTriangle(cmd, ShaderProperties.aoSliceTex[i], ShaderProperties.tempTex, material, Pass.Atlas_AO_Deinterleaved); // atlassing
ReleaseTemporaryRT(cmd, ShaderProperties.aoSliceTex[i]);
}
// Reinterleave AO
ApplyFlip(cmd);
BlitFullscreenTriangle(cmd, ShaderProperties.tempTex, ShaderProperties.hbaoTex, material, Pass.Reinterleave_AO); // reinterleave
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
}
private void Blur(CommandBuffer cmd)
{
if (blurSettings.type != BlurType.None)
{
float width = aoWidth;
float height = aoHeight;
if (hbaoCamera.allowDynamicResolution)
{
width *= ScalableBufferManager.widthScaleFactor;
height *= ScalableBufferManager.heightScaleFactor;
}
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, widthOverride: aoWidth, heightOverride: aoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
cmd.SetGlobalVector(ShaderProperties.blurDeltaUV, new Vector2(1f / width, 0));
BlitFullscreenTriangle(cmd, ShaderProperties.hbaoTex, ShaderProperties.tempTex, material, Pass.Blur); // blur X
cmd.SetGlobalVector(ShaderProperties.blurDeltaUV, new Vector2(0, 1f / height));
BlitFullscreenTriangle(cmd, ShaderProperties.tempTex, ShaderProperties.hbaoTex, material, Pass.Blur); // blur Y
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
}
}
private void TemporalFilter(CommandBuffer cmd)
{
if (isHistoryBufferDirty && temporalFilterSettings.enabled)
AllocateHistoryBuffers();
if (temporalFilterSettings.enabled && !renderingInSceneView)
{
if (colorBleedingSettings.enabled)
{
// For Color Bleeding we have 2 history buffers to fill so there are 2 render targets.
// AO is still contained in Color Bleeding history buffer (alpha channel) so that we
// can use it as a render texture for the composite pass.
var rts = new RenderTargetIdentifier[] {
aoHistoryBuffer,
colorBleedingHistoryBuffer
};
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, widthOverride: aoWidth, heightOverride: aoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex2, widthOverride: aoWidth, heightOverride: aoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
BlitFullscreenTriangle(cmd, aoHistoryBuffer, ShaderProperties.tempTex2, material, Pass.Copy);
BlitFullscreenTriangle(cmd, colorBleedingHistoryBuffer, ShaderProperties.tempTex, material, Pass.Copy);
BlitFullscreenTriangle(cmd, ShaderProperties.tempTex2, rts, material, Pass.Temporal_Filter);
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex2);
cmd.SetGlobalTexture(ShaderProperties.hbaoTex, colorBleedingHistoryBuffer);
}
else
{
// AO history buffer contains ao in aplha channel so we can just use history as
// a render texture for the composite pass.
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, widthOverride: aoWidth, heightOverride: aoHeight, colorFormat: colorFormat, readWrite: RenderTextureReadWrite.Linear);
BlitFullscreenTriangle(cmd, aoHistoryBuffer, ShaderProperties.tempTex, material, Pass.Copy);
BlitFullscreenTriangle(cmd, ShaderProperties.tempTex, aoHistoryBuffer, material, Pass.Temporal_Filter);
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
cmd.SetGlobalTexture(ShaderProperties.hbaoTex, aoHistoryBuffer);
}
}
}
private void Composite(CommandBuffer cmd, CameraEvent cameraEvent)
{
if (generalSettings.debugMode == DebugMode.Disabled)
{
if (cameraEvent == CameraEvent.BeforeReflections)
CompositeBeforeReflections(cmd);
else if (cameraEvent == CameraEvent.AfterLighting)
CompositeAfterLighting(cmd);
else // if (BeforeImageEffectsOpaque)
CompositeBeforeImageEffectsOpaque(cmd);
}
else // debug mode
CompositeDebug(cmd, generalSettings.debugMode == DebugMode.ViewNormals ? Pass.Debug_ViewNormals : Pass.Composite);
}
// Cases of BeforeReflections & AfterLighting
// If using HDR theres no separate rendertarget being created for Emission+lighting buffer (RT3);
// instead the rendertarget that the Camera renders into (that is, the one that is passed to the image effects) is used as RT3.
// If non-HDR GBuffer3 texture is available. HDR uses ARGBHalf, non-HDR uses ARGB2101010
// Emission + lighting buffer(RT3) is logarithmically encoded to provide greater dynamic range than is usually possible with
// an ARGB32 texture, when the Camera is not using HDR.
// https://docs.unity3d.com/Manual/RenderTech-DeferredShading.html
private void CompositeBeforeReflections(CommandBuffer cmd)
{
var hdr = hbaoCamera.allowHDR;
var rts = new RenderTargetIdentifier[] {
BuiltinRenderTextureType.GBuffer0, // Albedo, Occ
hdr ? BuiltinRenderTextureType.CameraTarget : BuiltinRenderTextureType.GBuffer3 // Ambient
};
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, colorFormat: RenderTextureFormat.ARGB32);
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex2, colorFormat: hdr ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB2101010);
BlitFullscreenTriangle(cmd, rts[0], ShaderProperties.tempTex, material, Pass.Copy);
BlitFullscreenTriangle(cmd, rts[1], ShaderProperties.tempTex2, material, Pass.Copy);
BlitFullscreenTriangle(cmd, ShaderProperties.tempTex2, rts, material, Pass.Composite_BeforeReflections);
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex2);
}
private void CompositeAfterLighting(CommandBuffer cmd)
{
var hdr = hbaoCamera.allowHDR;
var rt3 = hdr ? BuiltinRenderTextureType.CameraTarget : BuiltinRenderTextureType.GBuffer3;
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, colorFormat: hdr ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB2101010);
BlitFullscreenTriangle(cmd, rt3, ShaderProperties.tempTex, material, Pass.Copy);
BlitFullscreenTriangle(cmd, ShaderProperties.tempTex, rt3, material, Pass.Composite_AfterLighting);
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
}
private void CompositeBeforeImageEffectsOpaque(CommandBuffer cmd)
{
if (aoSettings.useMultiBounce)
{
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, colorFormat: sourceFormat);
if (stereoActive && hbaoCamera.actualRenderingPath != RenderingPath.DeferredShading)
cmd.Blit(BuiltinRenderTextureType.CameraTarget, ShaderProperties.tempTex);
else
BlitFullscreenTriangle(cmd, BuiltinRenderTextureType.CameraTarget, ShaderProperties.tempTex, material, Pass.Copy);
}
ApplyFlip(cmd, SystemInfo.graphicsUVStartsAtTop);
BlitFullscreenTriangle(cmd, aoSettings.useMultiBounce ? (RenderTargetIdentifier)ShaderProperties.tempTex : BuiltinRenderTextureType.None, BuiltinRenderTextureType.CameraTarget, material, Pass.Composite_BlendAO);
if (colorBleedingSettings.enabled)
BlitFullscreenTriangle(cmd, BuiltinRenderTextureType.None, BuiltinRenderTextureType.CameraTarget, material, Pass.Composite_BlendCB);
if (aoSettings.useMultiBounce)
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
}
private void CompositeDebug(CommandBuffer cmd, int finalPassId = Pass.Composite)
{
GetScreenSpaceTemporaryRT(cmd, ShaderProperties.tempTex, colorFormat: sourceFormat);
if (stereoActive && hbaoCamera.actualRenderingPath != RenderingPath.DeferredShading)
cmd.Blit(BuiltinRenderTextureType.CameraTarget, ShaderProperties.tempTex);
else
BlitFullscreenTriangle(cmd, BuiltinRenderTextureType.CameraTarget, ShaderProperties.tempTex, material, Pass.Copy);
ApplyFlip(cmd, SystemInfo.graphicsUVStartsAtTop);
BlitFullscreenTriangle(cmd, ShaderProperties.tempTex, BuiltinRenderTextureType.CameraTarget, material, finalPassId);
ReleaseTemporaryRT(cmd, ShaderProperties.tempTex);
}
private void UpdateMaterialProperties()
{
int eyeCount = stereoActive && stereoRenderingMode == StereoRenderingMode.SinglePassInstanced && !renderingInSceneView ? 2 : 1;
for (int viewIndex = 0; viewIndex < eyeCount; viewIndex++)
{
var projMatrix = viewIndex == 0 ? hbaoCamera.projectionMatrix : hbaoCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Right);
float invTanHalfFOVxAR = projMatrix.m00; // m00 => 1.0f / (tanHalfFOV * aspectRatio)
float invTanHalfFOV = projMatrix.m11; // m11 => 1.0f / tanHalfFOV
m_UVToViewPerEye[viewIndex == 0 ? xrActiveEye : viewIndex] = new Vector4(2.0f / invTanHalfFOVxAR, -2.0f / invTanHalfFOV, -1.0f / invTanHalfFOVxAR, 1.0f / invTanHalfFOV);
m_RadiusPerEye[viewIndex == 0 ? xrActiveEye : viewIndex] = aoSettings.radius * 0.5f * (screenHeight / (generalSettings.deinterleaving == Deinterleaving.x4 ? 4 : 1) / (2.0f / invTanHalfFOV));
}
//float tanHalfFovY = Mathf.Tan(0.5f * hbaoCamera.fieldOfView * Mathf.Deg2Rad);
//float invFocalLenX = 1.0f / (1.0f / tanHalfFovY * (screenHeight / (float)screenWidth));
//float invFocalLenY = 1.0f / (1.0f / tanHalfFovY);
float maxRadInPixels = Mathf.Max(16, aoSettings.maxRadiusPixels * Mathf.Sqrt((screenWidth * screenHeight) / (1080.0f * 1920.0f)));
maxRadInPixels /= (generalSettings.deinterleaving == Deinterleaving.x4 ? 4 : 1);
var targetScale = generalSettings.deinterleaving == Deinterleaving.x4 ?
new Vector4(reinterleavedAoWidth / (float)width, reinterleavedAoHeight / (float)height, 1.0f / (reinterleavedAoWidth / (float)width), 1.0f / (reinterleavedAoHeight / (float)height)) :
generalSettings.resolution == Resolution.Half /*&& aoSettings.perPixelNormals == PerPixelNormals.Reconstruct*/ ?
new Vector4((width + 0.5f) / width, (height + 0.5f) / height, 1f, 1f) :
Vector4.one;
material.SetTexture(ShaderProperties.noiseTex, noiseTex);
material.SetVector(ShaderProperties.inputTexelSize, new Vector4(1f / width, 1f / height, width, height));
if (hbaoCamera.allowDynamicResolution)
material.SetVector(ShaderProperties.aoTexelSize, new Vector4(1f / (aoWidth * ScalableBufferManager.widthScaleFactor), 1f / (aoHeight * ScalableBufferManager.heightScaleFactor), aoWidth * ScalableBufferManager.widthScaleFactor, aoHeight * ScalableBufferManager.heightScaleFactor));
else
material.SetVector(ShaderProperties.aoTexelSize, new Vector4(1f / aoWidth, 1f / aoHeight, aoWidth, aoHeight));
material.SetVector(ShaderProperties.deinterleavedAOTexelSize, new Vector4(1.0f / deinterleavedAoWidth, 1.0f / deinterleavedAoHeight, deinterleavedAoWidth, deinterleavedAoHeight));
material.SetVector(ShaderProperties.reinterleavedAOTexelSize, new Vector4(1f / reinterleavedAoWidth, 1f / reinterleavedAoHeight, reinterleavedAoWidth, reinterleavedAoHeight));
material.SetVector(ShaderProperties.targetScale, targetScale);
//material.SetVector(ShaderProperties.uvToView, new Vector4(2.0f * invFocalLenX, -2.0f * invFocalLenY, -1.0f * invFocalLenX, 1.0f * invFocalLenY));
material.SetVectorArray(ShaderProperties.uvToView, m_UVToViewPerEye);
//material.SetMatrix(ShaderProperties.worldToCameraMatrix, hbaoCamera.worldToCameraMatrix);
//material.SetFloat(ShaderProperties.radius, aoSettings.radius * 0.5f * ((screenHeight / (generalSettings.deinterleaving == Deinterleaving.x4 ? 4 : 1)) / (tanHalfFovY * 2.0f)));
//material.SetFloat(ShaderProperties.radius, aoSettings.radius * 0.5f * ((screenHeight / (generalSettings.deinterleaving == Deinterleaving.x4 ? 4 : 1)) / (invFocalLenY * 2.0f)));
material.SetFloatArray(ShaderProperties.radius, m_RadiusPerEye);
material.SetFloat(ShaderProperties.maxRadiusPixels, maxRadInPixels);
material.SetFloat(ShaderProperties.negInvRadius2, -1.0f / (aoSettings.radius * aoSettings.radius));
material.SetFloat(ShaderProperties.angleBias, aoSettings.bias);
material.SetFloat(ShaderProperties.aoMultiplier, 2.0f * (1.0f / (1.0f - aoSettings.bias)));
material.SetFloat(ShaderProperties.intensity, isLinearColorSpace ? aoSettings.intensity : aoSettings.intensity * 0.454545454545455f);
material.SetColor(ShaderProperties.baseColor, aoSettings.baseColor);
material.SetFloat(ShaderProperties.multiBounceInfluence, aoSettings.multiBounceInfluence);
material.SetFloat(ShaderProperties.offscreenSamplesContrib, aoSettings.offscreenSamplesContribution);
material.SetFloat(ShaderProperties.maxDistance, aoSettings.maxDistance);
material.SetFloat(ShaderProperties.distanceFalloff, aoSettings.distanceFalloff);
material.SetFloat(ShaderProperties.blurSharpness, blurSettings.sharpness);
material.SetFloat(ShaderProperties.colorBleedSaturation, colorBleedingSettings.saturation);
material.SetFloat(ShaderProperties.albedoMultiplier, colorBleedingSettings.albedoMultiplier);
material.SetFloat(ShaderProperties.colorBleedBrightnessMask, colorBleedingSettings.brightnessMask);
material.SetVector(ShaderProperties.colorBleedBrightnessMaskRange, AdjustBrightnessMaskToGammaSpace(new Vector2(Mathf.Pow(colorBleedingSettings.brightnessMaskRange.x, 3), Mathf.Pow(colorBleedingSettings.brightnessMaskRange.y, 3))));
material.SetVector(ShaderProperties.temporalParams, temporalFilterSettings.enabled && !renderingInSceneView ? new Vector2(s_temporalRotations[frameCount % 6] / 360.0f, s_temporalOffsets[frameCount % 4]) : Vector2.zero);
}
private void UpdateShaderKeywords()
{
if (m_ShaderKeywords == null || m_ShaderKeywords.Length != 12) m_ShaderKeywords = new string[12];
m_ShaderKeywords[0] = ShaderProperties.GetOrthographicOrDeferredKeyword(hbaoCamera.orthographic, generalSettings);
m_ShaderKeywords[1] = ShaderProperties.GetQualityKeyword(generalSettings);
m_ShaderKeywords[2] = ShaderProperties.GetNoiseKeyword(generalSettings);
m_ShaderKeywords[3] = ShaderProperties.GetDeinterleavingKeyword(generalSettings);
m_ShaderKeywords[4] = ShaderProperties.GetDebugKeyword(generalSettings);
m_ShaderKeywords[5] = ShaderProperties.GetMultibounceKeyword(aoSettings);
m_ShaderKeywords[6] = ShaderProperties.GetOffscreenSamplesContributionKeyword(aoSettings);
m_ShaderKeywords[7] = ShaderProperties.GetPerPixelNormalsKeyword(aoSettings);
m_ShaderKeywords[8] = ShaderProperties.GetBlurRadiusKeyword(blurSettings);
m_ShaderKeywords[9] = ShaderProperties.GetVarianceClippingKeyword(temporalFilterSettings);
m_ShaderKeywords[10] = ShaderProperties.GetColorBleedingKeyword(colorBleedingSettings);
m_ShaderKeywords[11] = ShaderProperties.GetLightingLogEncodedKeyword(hbaoCamera.allowHDR);
material.shaderKeywords = m_ShaderKeywords;
}
private void CheckParameters()
{
// Camera textures
hbaoCamera.depthTextureMode |= DepthTextureMode.Depth;
if (aoSettings.perPixelNormals == PerPixelNormals.Camera)
hbaoCamera.depthTextureMode |= DepthTextureMode.DepthNormals;
if (temporalFilterSettings.enabled)
hbaoCamera.depthTextureMode |= DepthTextureMode.MotionVectors;
// Settings to force
if (hbaoCamera.actualRenderingPath != RenderingPath.DeferredShading && aoSettings.perPixelNormals == PerPixelNormals.GBuffer)
SetAoPerPixelNormals(PerPixelNormals.Camera);
if (generalSettings.deinterleaving != Deinterleaving.Disabled && SystemInfo.supportedRenderTargetCount < 4)
SetDeinterleaving(Deinterleaving.Disabled);
if (generalSettings.pipelineStage != PipelineStage.BeforeImageEffectsOpaque && hbaoCamera.actualRenderingPath != RenderingPath.DeferredShading)
SetPipelineStage(PipelineStage.BeforeImageEffectsOpaque);
if (generalSettings.pipelineStage != PipelineStage.BeforeImageEffectsOpaque && aoSettings.perPixelNormals == PerPixelNormals.Camera)
SetAoPerPixelNormals(PerPixelNormals.GBuffer);
if (stereoActive && hbaoCamera.actualRenderingPath != RenderingPath.DeferredShading && aoSettings.perPixelNormals != PerPixelNormals.Reconstruct)
SetAoPerPixelNormals(PerPixelNormals.Reconstruct);
if (temporalFilterSettings.enabled && !motionVectorsSupported)
EnableTemporalFilter(false);
if (colorBleedingSettings.enabled && temporalFilterSettings.enabled && SystemInfo.supportedRenderTargetCount < 2)
EnableTemporalFilter(false);
// Noise texture
if (noiseTex == null || m_PreviousNoiseType != generalSettings.noiseType)
{
if (noiseTex != null) DestroyImmediate(noiseTex);
CreateNoiseTexture();
m_PreviousNoiseType = generalSettings.noiseType;
}
}
private RenderTextureDescriptor GetDefaultDescriptor(int depthBufferBits = 0, RenderTextureFormat colorFormat = RenderTextureFormat.Default, RenderTextureReadWrite readWrite = RenderTextureReadWrite.Default)
{
var modifiedDesc = new RenderTextureDescriptor(m_sourceDescriptor.width, m_sourceDescriptor.height,
m_sourceDescriptor.colorFormat, depthBufferBits);
modifiedDesc.dimension = m_sourceDescriptor.dimension;
modifiedDesc.volumeDepth = m_sourceDescriptor.volumeDepth;
modifiedDesc.vrUsage = m_sourceDescriptor.vrUsage;
modifiedDesc.msaaSamples = m_sourceDescriptor.msaaSamples;
modifiedDesc.memoryless = m_sourceDescriptor.memoryless;
modifiedDesc.useMipMap = m_sourceDescriptor.useMipMap;
modifiedDesc.autoGenerateMips = m_sourceDescriptor.autoGenerateMips;
modifiedDesc.enableRandomWrite = m_sourceDescriptor.enableRandomWrite;
modifiedDesc.shadowSamplingMode = m_sourceDescriptor.shadowSamplingMode;
if (hbaoCamera.allowDynamicResolution)
modifiedDesc.useDynamicScale = true;
if (colorFormat != RenderTextureFormat.Default)
modifiedDesc.colorFormat = colorFormat;
if (readWrite == RenderTextureReadWrite.sRGB)
modifiedDesc.sRGB = true;
else if (readWrite == RenderTextureReadWrite.Linear)
modifiedDesc.sRGB = false;
else if (readWrite == RenderTextureReadWrite.Default)
modifiedDesc.sRGB = isLinearColorSpace;
return modifiedDesc;
}
private RenderTexture GetScreenSpaceRT(int depthBufferBits = 0, RenderTextureFormat colorFormat = RenderTextureFormat.Default, RenderTextureReadWrite readWrite = RenderTextureReadWrite.Default,
FilterMode filter = FilterMode.Bilinear, int widthOverride = 0, int heightOverride = 0)
{
var desc = GetDefaultDescriptor(depthBufferBits, colorFormat, readWrite);
if (widthOverride > 0)
desc.width = widthOverride;
if (heightOverride > 0)
desc.height = heightOverride;
var rt = new RenderTexture(desc);
rt.filterMode = filter;
return rt;
}
private void GetScreenSpaceTemporaryRT(CommandBuffer cmd, int nameID,
int depthBufferBits = 0, RenderTextureFormat colorFormat = RenderTextureFormat.Default, RenderTextureReadWrite readWrite = RenderTextureReadWrite.Default,
FilterMode filter = FilterMode.Bilinear, int widthOverride = 0, int heightOverride = 0)
{
var desc = GetDefaultDescriptor(depthBufferBits, colorFormat, readWrite);
if (widthOverride > 0)
desc.width = widthOverride;
if (heightOverride > 0)
desc.height = heightOverride;
cmd.GetTemporaryRT(nameID, desc, filter);
}
private void ReleaseTemporaryRT(CommandBuffer cmd, int nameID)
{
cmd.ReleaseTemporaryRT(nameID);
}
private void BlitFullscreenTriangle(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, int pass = 0)
{
cmd.SetGlobalTexture(ShaderProperties.mainTex, source);
cmd.SetRenderTarget(destination, 0, CubemapFace.Unknown, -1);
cmd.DrawMesh(fullscreenTriangle, Matrix4x4.identity, material, 0, pass);
}
private void BlitFullscreenTriangle(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier[] destinations, Material material, int pass = 0)
{
cmd.SetGlobalTexture(ShaderProperties.mainTex, source);
cmd.SetRenderTarget(destinations, destinations[0], 0, CubemapFace.Unknown, -1);
cmd.DrawMesh(fullscreenTriangle, Matrix4x4.identity, material, 0, pass);
}
private void BlitFullscreenTriangleWithClear(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, Color clearColor, int pass = 0)
{
cmd.SetGlobalTexture(ShaderProperties.mainTex, source);
cmd.SetRenderTarget(destination, 0, CubemapFace.Unknown, -1);
cmd.ClearRenderTarget(false, true, clearColor);
cmd.DrawMesh(fullscreenTriangle, Matrix4x4.identity, material, 0, pass);
}
private static void ApplyFlip(CommandBuffer cmd, bool flip = true)
{
if (flip)
cmd.SetGlobalVector(ShaderProperties.uvTransform, new Vector4(1f, -1f, 0f, 1f));
else
cmd.SetGlobalVector(ShaderProperties.uvTransform, new Vector4(1f, 1f, 0f, 0f));
}
private static Vector2 AdjustBrightnessMaskToGammaSpace(Vector2 v)
{
return isLinearColorSpace ? v : ToGammaSpace(v);
}
private static float ToGammaSpace(float v)
{
return Mathf.Pow(v, 0.454545454545455f);
}
private static Vector2 ToGammaSpace(Vector2 v)
{
return new Vector2(ToGammaSpace(v.x), ToGammaSpace(v.y));
}
private void CreateNoiseTexture()
{
noiseTex = new Texture2D(4, 4, SystemInfo.SupportsTextureFormat(TextureFormat.RGHalf) ? TextureFormat.RGHalf : TextureFormat.RGB24, false, true);
noiseTex.filterMode = FilterMode.Point;
noiseTex.wrapMode = TextureWrapMode.Repeat;
int z = 0;
for (int x = 0; x < 4; ++x)
{
for (int y = 0; y < 4; ++y)
{
float r1 = generalSettings.noiseType != NoiseType.Dither ? 0.25f * (0.0625f * ((x + y & 3) << 2) + (x & 3)) : MersenneTwister.Numbers[z++];
float r2 = generalSettings.noiseType != NoiseType.Dither ? 0.25f * ((y - x) & 3) : MersenneTwister.Numbers[z++];
Color color = new Color(r1, r2, 0);
noiseTex.SetPixel(x, y, color);
}
}
noiseTex.Apply();
for (int i = 0, j = 0; i < s_jitter.Length; ++i)
{
float r1 = MersenneTwister.Numbers[j++];
float r2 = MersenneTwister.Numbers[j++];
s_jitter[i] = new Vector2(r1, r2);
}
}
}
}