3529 lines
175 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine.Assertions;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.HighDefinition
{
using AntialiasingMode = HDAdditionalCameraData.AntialiasingMode;
// Main class for all post-processing related features - only includes camera effects, no
// lighting/surface effect like SSR/AO
sealed partial class PostProcessSystem
{
private enum SMAAStage
{
EdgeDetection = 0,
BlendWeights = 1,
NeighborhoodBlending = 2
}
GraphicsFormat m_ColorFormat = GraphicsFormat.B10G11R11_UFloatPack32;
const GraphicsFormat k_CoCFormat = GraphicsFormat.R16_SFloat;
internal const GraphicsFormat k_ExposureFormat = GraphicsFormat.R32G32_SFloat;
readonly RenderPipelineResources m_Resources;
Material m_FinalPassMaterial;
Material m_ClearBlackMaterial;
Material m_SMAAMaterial;
Material m_TemporalAAMaterial;
MaterialPropertyBlock m_TAAHistoryBlitPropertyBlock = new MaterialPropertyBlock();
MaterialPropertyBlock m_TAAPropertyBlock = new MaterialPropertyBlock();
// Exposure data
const int k_ExposureCurvePrecision = 128;
const int k_HistogramBins = 128; // Important! If this changes, need to change HistogramExposure.compute
const int k_DebugImageHistogramBins = 256; // Important! If this changes, need to change HistogramExposure.compute
readonly Color[] m_ExposureCurveColorArray = new Color[k_ExposureCurvePrecision];
readonly int[] m_ExposureVariants = new int[4];
Texture2D m_ExposureCurveTexture;
RTHandle m_EmptyExposureTexture; // RGHalf
RTHandle m_DebugExposureData;
ComputeBuffer m_HistogramBuffer;
ComputeBuffer m_DebugImageHistogramBuffer;
readonly int[] m_EmptyHistogram = new int[k_HistogramBins];
readonly int[] m_EmptyDebugImageHistogram = new int[k_DebugImageHistogramBins * 4];
// Depth of field data
ComputeBuffer m_BokehNearKernel;
ComputeBuffer m_BokehFarKernel;
ComputeBuffer m_BokehIndirectCmd;
ComputeBuffer m_NearBokehTileList;
ComputeBuffer m_FarBokehTileList;
// AMD-CAS data
ComputeBuffer m_ContrastAdaptiveSharpen;
// Bloom data
const int k_MaxBloomMipCount = 16;
readonly Vector4[] m_BloomMipsInfo = new Vector4[k_MaxBloomMipCount + 1]; // xy: size, zw: scale
int m_BloomMipCount = k_MaxBloomMipCount;
// Chromatic aberration data
Texture2D m_InternalSpectralLut;
// Color grading data
readonly int m_LutSize;
readonly GraphicsFormat m_LutFormat;
readonly HableCurve m_HableCurve;
// Prefetched components (updated on every frame)
Exposure m_Exposure;
DepthOfField m_DepthOfField;
MotionBlur m_MotionBlur;
PaniniProjection m_PaniniProjection;
Bloom m_Bloom;
ChromaticAberration m_ChromaticAberration;
LensDistortion m_LensDistortion;
Vignette m_Vignette;
Tonemapping m_Tonemapping;
WhiteBalance m_WhiteBalance;
ColorAdjustments m_ColorAdjustments;
ChannelMixer m_ChannelMixer;
SplitToning m_SplitToning;
LiftGammaGain m_LiftGammaGain;
ShadowsMidtonesHighlights m_ShadowsMidtonesHighlights;
ColorCurves m_Curves;
FilmGrain m_FilmGrain;
PathTracing m_PathTracing;
// Prefetched frame settings (updated on every frame)
bool m_StopNaNFS;
bool m_DepthOfFieldFS;
bool m_MotionBlurFS;
bool m_PaniniProjectionFS;
bool m_BloomFS;
bool m_ChromaticAberrationFS;
bool m_LensDistortionFS;
bool m_VignetteFS;
bool m_ColorGradingFS;
bool m_TonemappingFS;
bool m_FilmGrainFS;
bool m_DitheringFS;
bool m_AntialiasingFS;
// Debug Exposure compensation (Drive by debug menu) to add to all exposure processed value
float m_DebugExposureCompensation;
// Physical camera ref
HDPhysicalCamera m_PhysicalCamera;
static readonly HDPhysicalCamera m_DefaultPhysicalCamera = new HDPhysicalCamera();
// HDRP has the following behavior regarding alpha:
// - If post processing is disabled, the alpha channel of the rendering passes (if any) will be passed to the frame buffer by the final pass
// - If post processing is enabled, then post processing passes will either copy (exposure, color grading, etc) or process (DoF, TAA, etc) the alpha channel, if one exists.
// If the user explicitly requests a color buffer without alpha for post-processing (for performance reasons) but the rendering passes have alpha, then the alpha will be copied.
readonly bool m_EnableAlpha;
readonly bool m_KeepAlpha;
readonly bool m_UseSafePath;
bool m_PostProcessEnabled;
bool m_AnimatedMaterialsEnabled;
bool m_MotionBlurSupportsScattering;
bool m_NonRenderGraphResourcesAvailable;
// Max guard band size is assumed to be 8 pixels
const int k_RTGuardBandSize = 4;
readonly System.Random m_Random;
HDRenderPipeline m_HDInstance;
internal static void SetExposureTextureToEmpty(RTHandle exposureTexture)
{
var tex = new Texture2D(1, 1, TextureFormat.RGHalf, false, true);
tex.SetPixel(0, 0, new Color(1f, ColorUtils.ConvertExposureToEV100(1f), 0f, 0f));
tex.Apply();
Graphics.Blit(tex, exposureTexture);
CoreUtils.Destroy(tex);
}
public PostProcessSystem(HDRenderPipelineAsset hdAsset, RenderPipelineResources defaultResources)
{
m_Resources = defaultResources;
m_FinalPassMaterial = CoreUtils.CreateEngineMaterial(m_Resources.shaders.finalPassPS);
m_ClearBlackMaterial = CoreUtils.CreateEngineMaterial(m_Resources.shaders.clearBlackPS);
m_SMAAMaterial = CoreUtils.CreateEngineMaterial(m_Resources.shaders.SMAAPS);
m_TemporalAAMaterial = CoreUtils.CreateEngineMaterial(m_Resources.shaders.temporalAntialiasingPS);
// Some compute shaders fail on specific hardware or vendors so we'll have to use a
// safer but slower code path for them
m_UseSafePath = SystemInfo.graphicsDeviceVendor
.ToLowerInvariant().Contains("intel");
// Project-wide LUT size for all grading operations - meaning that internal LUTs and
// user-provided LUTs will have to be this size
var postProcessSettings = hdAsset.currentPlatformRenderPipelineSettings.postProcessSettings;
m_LutSize = postProcessSettings.lutSize;
m_LutFormat = (GraphicsFormat)postProcessSettings.lutFormat;
// Grading specific
m_HableCurve = new HableCurve();
m_MotionBlurSupportsScattering = SystemInfo.IsFormatSupported(GraphicsFormat.R32_UInt, FormatUsage.LoadStore) && SystemInfo.IsFormatSupported(GraphicsFormat.R16_UInt, FormatUsage.LoadStore);
// TODO: Remove this line when atomic bug in HLSLcc is fixed.
m_MotionBlurSupportsScattering = m_MotionBlurSupportsScattering && (SystemInfo.graphicsDeviceType != GraphicsDeviceType.Vulkan);
// TODO: Write a version that uses structured buffer instead of texture to do atomic as Metal doesn't support atomics on textures.
m_MotionBlurSupportsScattering = m_MotionBlurSupportsScattering && (SystemInfo.graphicsDeviceType != GraphicsDeviceType.Metal);
// Use a custom RNG, we don't want to mess with the Unity one that the users might be
// relying on (breaks determinism in their code)
m_Random = new System.Random();
m_ColorFormat = (GraphicsFormat)postProcessSettings.bufferFormat;
m_KeepAlpha = false;
// if both rendering and post-processing support an alpha channel, then post-processing will process (or copy) the alpha
m_EnableAlpha = hdAsset.currentPlatformRenderPipelineSettings.supportsAlpha && postProcessSettings.supportsAlpha;
if (m_EnableAlpha == false)
{
// if only rendering has an alpha channel (and not post-processing), then we just copy the alpha to the output (but we don't process it).
m_KeepAlpha = hdAsset.currentPlatformRenderPipelineSettings.supportsAlpha;
}
// Setup a default exposure textures and clear it to neutral values so that the exposure
// multiplier is 1 and thus has no effect
// Beware that 0 in EV100 maps to a multiplier of 0.833 so the EV100 value in this
// neutral exposure texture isn't 0
m_EmptyExposureTexture = RTHandles.Alloc(1, 1, colorFormat: k_ExposureFormat,
enableRandomWrite: true, name: "Empty EV100 Exposure");
m_DebugExposureData = RTHandles.Alloc(1, 1, colorFormat: k_ExposureFormat,
enableRandomWrite: true, name: "Debug Exposure Info");
m_ExposureCurveTexture = new Texture2D(k_ExposureCurvePrecision, 1, GraphicsFormat.R16G16B16A16_SFloat, TextureCreationFlags.None)
{
name = "Exposure Curve",
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp
};
m_ExposureCurveTexture.hideFlags = HideFlags.HideAndDontSave;
SetExposureTextureToEmpty(m_EmptyExposureTexture);
}
public void Cleanup()
{
RTHandles.Release(m_EmptyExposureTexture);
m_EmptyExposureTexture = null;
CoreUtils.Destroy(m_ExposureCurveTexture);
CoreUtils.Destroy(m_InternalSpectralLut);
CoreUtils.Destroy(m_FinalPassMaterial);
CoreUtils.Destroy(m_ClearBlackMaterial);
CoreUtils.Destroy(m_SMAAMaterial);
CoreUtils.Destroy(m_TemporalAAMaterial);
CoreUtils.SafeRelease(m_HistogramBuffer);
CoreUtils.SafeRelease(m_DebugImageHistogramBuffer);
RTHandles.Release(m_DebugExposureData);
m_ExposureCurveTexture = null;
m_InternalSpectralLut = null;
m_FinalPassMaterial = null;
m_ClearBlackMaterial = null;
m_SMAAMaterial = null;
m_TemporalAAMaterial = null;
m_HistogramBuffer = null;
m_DebugImageHistogramBuffer = null;
m_DebugExposureData = null;
}
// In some cases, the internal buffer of render textures might be invalid.
// Usually when using these textures with API such as SetRenderTarget, they are recreated internally.
// This is not the case when these textures are used exclusively with Compute Shaders. So to make sure they work in this case, we recreate them here.
void CheckRenderTexturesValidity()
{
if (!m_EmptyExposureTexture.rt.IsCreated())
SetExposureTextureToEmpty(m_EmptyExposureTexture);
HDUtils.CheckRTCreated(m_DebugExposureData.rt);
}
public void BeginFrame(CommandBuffer cmd, HDCamera camera, HDRenderPipeline hdInstance)
{
m_HDInstance = hdInstance;
m_PostProcessEnabled = camera.frameSettings.IsEnabled(FrameSettingsField.Postprocess) && CoreUtils.ArePostProcessesEnabled(camera.camera);
m_AnimatedMaterialsEnabled = camera.animateMaterials;
// Grab physical camera settings or a default instance if it's null (should only happen
// in rare occasions due to how HDAdditionalCameraData is added to the camera)
m_PhysicalCamera = camera.physicalParameters ?? m_DefaultPhysicalCamera;
// Prefetch all the volume components we need to save some cycles as most of these will
// be needed in multiple places
var stack = camera.volumeStack;
m_Exposure = stack.GetComponent<Exposure>();
m_DepthOfField = stack.GetComponent<DepthOfField>();
m_MotionBlur = stack.GetComponent<MotionBlur>();
m_PaniniProjection = stack.GetComponent<PaniniProjection>();
m_Bloom = stack.GetComponent<Bloom>();
m_ChromaticAberration = stack.GetComponent<ChromaticAberration>();
m_LensDistortion = stack.GetComponent<LensDistortion>();
m_Vignette = stack.GetComponent<Vignette>();
m_Tonemapping = stack.GetComponent<Tonemapping>();
m_WhiteBalance = stack.GetComponent<WhiteBalance>();
m_ColorAdjustments = stack.GetComponent<ColorAdjustments>();
m_ChannelMixer = stack.GetComponent<ChannelMixer>();
m_SplitToning = stack.GetComponent<SplitToning>();
m_LiftGammaGain = stack.GetComponent<LiftGammaGain>();
m_ShadowsMidtonesHighlights = stack.GetComponent<ShadowsMidtonesHighlights>();
m_Curves = stack.GetComponent<ColorCurves>();
m_FilmGrain = stack.GetComponent<FilmGrain>();
m_PathTracing = stack.GetComponent<PathTracing>();
// Prefetch frame settings - these aren't free to pull so we want to do it only once
// per frame
var frameSettings = camera.frameSettings;
m_StopNaNFS = frameSettings.IsEnabled(FrameSettingsField.StopNaN);
m_DepthOfFieldFS = frameSettings.IsEnabled(FrameSettingsField.DepthOfField);
m_MotionBlurFS = frameSettings.IsEnabled(FrameSettingsField.MotionBlur);
m_PaniniProjectionFS = frameSettings.IsEnabled(FrameSettingsField.PaniniProjection);
m_BloomFS = frameSettings.IsEnabled(FrameSettingsField.Bloom);
m_ChromaticAberrationFS = frameSettings.IsEnabled(FrameSettingsField.ChromaticAberration);
m_LensDistortionFS = frameSettings.IsEnabled(FrameSettingsField.LensDistortion);
m_VignetteFS = frameSettings.IsEnabled(FrameSettingsField.Vignette);
m_ColorGradingFS = frameSettings.IsEnabled(FrameSettingsField.ColorGrading);
m_TonemappingFS = frameSettings.IsEnabled(FrameSettingsField.Tonemapping);
m_FilmGrainFS = frameSettings.IsEnabled(FrameSettingsField.FilmGrain);
m_DitheringFS = frameSettings.IsEnabled(FrameSettingsField.Dithering);
m_AntialiasingFS = frameSettings.IsEnabled(FrameSettingsField.Antialiasing);
// Override full screen anti-aliasing when doing path tracing (which is naturally anti-aliased already)
m_AntialiasingFS &= !m_PathTracing.enable.value;
m_DebugExposureCompensation = m_HDInstance.m_CurrentDebugDisplaySettings.data.lightingDebugSettings.debugExposure;
CheckRenderTexturesValidity();
// Handle fixed exposure & disabled pre-exposure by forcing an exposure multiplier of 1
{
// Fix exposure is store in Exposure Textures at the beginning of the frame as there is no need for color buffer
// Dynamic exposure (Auto, curve) is store in Exposure Textures at the end of the frame (as it rely on color buffer)
// Texture current and previous are swapped at the beginning of the frame.
if (CanRunFixedExposurePass(camera))
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.FixedExposure)))
{
DoFixedExposure(PrepareExposureParameters(camera), cmd, camera.currentExposureTextures.current);
}
}
cmd.SetGlobalTexture(HDShaderIDs._ExposureTexture, GetExposureTexture(camera));
cmd.SetGlobalTexture(HDShaderIDs._PrevExposureTexture, GetPreviousExposureTexture(camera));
}
}
struct UberPostParameters
{
public ComputeShader uberPostCS;
public int uberPostKernel;
public bool outputColorLog;
public int width;
public int height;
public int viewCount;
public Vector4 logLutSettings;
public Vector4 lensDistortionParams1;
public Vector4 lensDistortionParams2;
public Texture spectralLut;
public Vector4 chromaticAberrationParameters;
public Vector4 vignetteParams1;
public Vector4 vignetteParams2;
public Vector4 vignetteColor;
public Texture vignetteMask;
public Texture bloomDirtTexture;
public Vector4 bloomParams;
public Vector4 bloomTint;
public Vector4 bloomBicubicParams;
public Vector4 bloomDirtTileOffset;
public Vector4 bloomThreshold;
public Vector4 alphaScaleBias;
}
UberPostParameters PrepareUberPostParameters(HDCamera hdCamera, bool isSceneView)
{
var parameters = new UberPostParameters();
// Feature flags are passed to all effects and it's their responsibility to check
// if they are used or not so they can set default values if needed
parameters.uberPostCS = m_Resources.shaders.uberPostCS;
parameters.uberPostCS.shaderKeywords = null;
var featureFlags = GetUberFeatureFlags(isSceneView);
parameters.uberPostKernel = parameters.uberPostCS.FindKernel("Uber");
if (m_EnableAlpha)
{
parameters.uberPostCS.EnableKeyword("ENABLE_ALPHA");
}
parameters.outputColorLog = m_HDInstance.m_CurrentDebugDisplaySettings.data.fullScreenDebugMode == FullScreenDebugMode.ColorLog;
parameters.width = hdCamera.actualWidth;
parameters.height = hdCamera.actualHeight;
parameters.viewCount = hdCamera.viewCount;
// Color grading
// This should be EV100 instead of EV but given that EV100(0) isn't equal to 1, it means
// we can't use 0 as the default neutral value which would be confusing to users
float postExposureLinear = Mathf.Pow(2f, m_ColorAdjustments.postExposure.value);
parameters.logLutSettings = new Vector4(1f / m_LutSize, m_LutSize - 1f, postExposureLinear, 0f);
// Setup the rest of the effects
PrepareLensDistortionParameters(ref parameters, featureFlags);
PrepareChromaticAberrationParameters(ref parameters, featureFlags);
PrepareVignetteParameters(ref parameters, featureFlags);
PrepareUberBloomParameters(ref parameters, hdCamera);
PrepareAlphaScaleParameters(ref parameters, hdCamera);
return parameters;
}
void PrepareAlphaScaleParameters(ref UberPostParameters parameters, HDCamera camera)
{
if (m_EnableAlpha)
{
parameters.alphaScaleBias = Compositor.CompositionManager.GetAlphaScaleAndBiasForCamera(camera);
}
else
{
parameters.alphaScaleBias = new Vector4(1.0f, 0.0f, 0.0f, 0.0f);
}
}
static void DoUberPostProcess(in UberPostParameters parameters,
RTHandle source,
RTHandle destination,
RTHandle logLut,
RTHandle bloomTexture,
CommandBuffer cmd)
{
// Color grading
cmd.SetComputeTextureParam(parameters.uberPostCS, parameters.uberPostKernel, HDShaderIDs._LogLut3D, logLut);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._LogLut3D_Params, parameters.logLutSettings);
// Lens distortion
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._DistortionParams1, parameters.lensDistortionParams1);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._DistortionParams2, parameters.lensDistortionParams2);
// Chromatic aberration
cmd.SetComputeTextureParam(parameters.uberPostCS, parameters.uberPostKernel, HDShaderIDs._ChromaSpectralLut, parameters.spectralLut);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._ChromaParams, parameters.chromaticAberrationParameters);
// Vignette
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._VignetteParams1, parameters.vignetteParams1);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._VignetteParams2, parameters.vignetteParams2);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._VignetteColor, parameters.vignetteColor);
cmd.SetComputeTextureParam(parameters.uberPostCS, parameters.uberPostKernel, HDShaderIDs._VignetteMask, parameters.vignetteMask);
// Bloom
cmd.SetComputeTextureParam(parameters.uberPostCS, parameters.uberPostKernel, HDShaderIDs._BloomTexture, bloomTexture);
cmd.SetComputeTextureParam(parameters.uberPostCS, parameters.uberPostKernel, HDShaderIDs._BloomDirtTexture, parameters.bloomDirtTexture);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._BloomParams, parameters.bloomParams);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._BloomTint, parameters.bloomTint);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._BloomBicubicParams, parameters.bloomBicubicParams);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._BloomDirtScaleOffset, parameters.bloomDirtTileOffset);
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._BloomThreshold, parameters.bloomThreshold);
// Alpha scale and bias (only used when alpha is enabled)
cmd.SetComputeVectorParam(parameters.uberPostCS, HDShaderIDs._AlphaScaleBias, parameters.alphaScaleBias);
// Dispatch uber post
cmd.SetComputeVectorParam(parameters.uberPostCS, "_DebugFlags", new Vector4(parameters.outputColorLog ? 1 : 0, 0, 0, 0));
cmd.SetComputeTextureParam(parameters.uberPostCS, parameters.uberPostKernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(parameters.uberPostCS, parameters.uberPostKernel, HDShaderIDs._OutputTexture, destination);
cmd.DispatchCompute(parameters.uberPostCS, parameters.uberPostKernel, (parameters.width + 7) / 8, (parameters.height + 7) / 8, parameters.viewCount);
}
// Grabs all active feature flags
UberPostFeatureFlags GetUberFeatureFlags(bool isSceneView)
{
var flags = UberPostFeatureFlags.None;
if (m_ChromaticAberration.IsActive() && m_ChromaticAberrationFS)
flags |= UberPostFeatureFlags.ChromaticAberration;
if (m_Vignette.IsActive() && m_VignetteFS)
flags |= UberPostFeatureFlags.Vignette;
if (m_LensDistortion.IsActive() && !isSceneView && m_LensDistortionFS)
flags |= UberPostFeatureFlags.LensDistortion;
if (m_EnableAlpha)
{
flags |= UberPostFeatureFlags.EnableAlpha;
}
return flags;
}
static void ValidateComputeBuffer(ref ComputeBuffer cb, int size, int stride, ComputeBufferType type = ComputeBufferType.Default)
{
if (cb == null || cb.count < size)
{
CoreUtils.SafeRelease(cb);
cb = new ComputeBuffer(size, stride, type);
}
}
#region NaN Killer
struct StopNaNParameters
{
public ComputeShader nanKillerCS;
public int nanKillerKernel;
public int width;
public int height;
public int viewCount;
}
StopNaNParameters PrepareStopNaNParameters(HDCamera camera)
{
StopNaNParameters stopNanParams = new StopNaNParameters();
stopNanParams.nanKillerCS = m_Resources.shaders.nanKillerCS;
stopNanParams.nanKillerKernel = stopNanParams.nanKillerCS.FindKernel("KMain");
stopNanParams.width = camera.actualWidth;
stopNanParams.height = camera.actualHeight;
stopNanParams.viewCount = camera.viewCount;
stopNanParams.nanKillerCS.shaderKeywords = null;
if (m_EnableAlpha)
{
stopNanParams.nanKillerCS.EnableKeyword("ENABLE_ALPHA");
}
return stopNanParams;
}
static void DoStopNaNs(in StopNaNParameters stopNanParameters, CommandBuffer cmd, RTHandle source, RTHandle destination)
{
var cs = stopNanParameters.nanKillerCS;
int kernel = stopNanParameters.nanKillerKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.DispatchCompute(cs, kernel, (stopNanParameters.width + 7) / 8, (stopNanParameters.height + 7) / 8, stopNanParameters.viewCount);
}
#endregion
#region Copy Alpha
DoCopyAlphaParameters PrepareCopyAlphaParameters(HDCamera hdCamera)
{
var parameters = new DoCopyAlphaParameters();
parameters.hdCamera = hdCamera;
parameters.copyAlphaCS = m_Resources.shaders.copyAlphaCS;
parameters.copyAlphaKernel = parameters.copyAlphaCS.FindKernel("KMain");
return parameters;
}
struct DoCopyAlphaParameters
{
public ComputeShader copyAlphaCS;
public int copyAlphaKernel;
public HDCamera hdCamera;
}
static void DoCopyAlpha(in DoCopyAlphaParameters parameters, RTHandle source, RTHandle outputAlphaTexture, CommandBuffer cmd)
{
cmd.SetComputeTextureParam(parameters.copyAlphaCS, parameters.copyAlphaKernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(parameters.copyAlphaCS, parameters.copyAlphaKernel, HDShaderIDs._OutputTexture, outputAlphaTexture);
cmd.DispatchCompute(parameters.copyAlphaCS, parameters.copyAlphaKernel, (parameters.hdCamera.actualWidth + 7) / 8, (parameters.hdCamera.actualHeight + 7) / 8, parameters.hdCamera.viewCount);
}
#endregion
#region Exposure
struct ApplyExposureParameters
{
public ComputeShader applyExposureCS;
public int applyExposureKernel;
public int width;
public int height;
public int viewCount;
}
ApplyExposureParameters PrepareApplyExposureParameters(HDCamera camera)
{
ApplyExposureParameters parameters = new ApplyExposureParameters();
parameters.applyExposureCS = m_Resources.shaders.applyExposureCS;
parameters.applyExposureKernel = parameters.applyExposureCS.FindKernel("KMain");
parameters.width = camera.actualWidth;
parameters.height = camera.actualHeight;
parameters.viewCount = camera.viewCount;
return parameters;
}
static void ApplyExposure(in ApplyExposureParameters parameters, CommandBuffer cmd, RTHandle source, RTHandle destination, RTHandle prevExposure)
{
var cs = parameters.applyExposureCS;
int kernel = parameters.applyExposureKernel;
// Note: we use previous instead of current because the textures
// are swapped internally as the system expects the texture will be used
// on the next frame. So the actual "current" for this frame is in
// "previous".
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureTexture, prevExposure);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.DispatchCompute(cs, kernel, (parameters.width + 7) / 8, (parameters.height + 7) / 8, parameters.viewCount);
}
struct ExposureParameters
{
public ComputeShader exposureCS;
public ComputeShader histogramExposureCS;
public int exposurePreparationKernel;
public int exposureReductionKernel;
public Texture textureMeteringMask;
public Texture exposureCurve;
public HDCamera camera;
public ComputeBuffer histogramBuffer;
public ExposureMode exposureMode;
public bool histogramUsesCurve;
public bool histogramOutputDebugData;
public int[] exposureVariants;
public Vector4 exposureParams;
public Vector4 exposureParams2;
public Vector4 proceduralMaskParams;
public Vector4 proceduralMaskParams2;
public Vector4 histogramExposureParams;
public Vector4 adaptationParams;
}
ExposureParameters PrepareExposureParameters(HDCamera hdCamera)
{
var parameters = new ExposureParameters();
parameters.exposureCS = m_Resources.shaders.exposureCS;
parameters.histogramExposureCS = m_Resources.shaders.histogramExposureCS;
parameters.histogramExposureCS.shaderKeywords = null;
parameters.camera = hdCamera;
bool isFixed = IsExposureFixed(hdCamera);
if (isFixed)
{
parameters.exposureParams2 = new Vector4(0.0f, 0.0f, ColorUtils.lensImperfectionExposureScale, ColorUtils.s_LightMeterCalibrationConstant);
if (m_Exposure.mode.value == ExposureMode.Fixed
#if UNITY_EDITOR
|| HDAdditionalSceneViewSettings.sceneExposureOverriden && hdCamera.camera.cameraType == CameraType.SceneView
#endif
)
{
parameters.exposureReductionKernel = parameters.exposureCS.FindKernel("KFixedExposure");
parameters.exposureParams = new Vector4(m_Exposure.compensation.value + m_DebugExposureCompensation, m_Exposure.fixedExposure.value, 0f, 0f);
#if UNITY_EDITOR
if (HDAdditionalSceneViewSettings.sceneExposureOverriden && hdCamera.camera.cameraType == CameraType.SceneView)
{
parameters.exposureParams = new Vector4(0.0f, HDAdditionalSceneViewSettings.sceneExposure, 0f, 0f);
}
#endif
}
else if (m_Exposure.mode == ExposureMode.UsePhysicalCamera)
{
parameters.exposureReductionKernel = parameters.exposureCS.FindKernel("KManualCameraExposure");
parameters.exposureParams = new Vector4(m_Exposure.compensation.value + m_DebugExposureCompensation, m_PhysicalCamera.aperture, m_PhysicalCamera.shutterSpeed, m_PhysicalCamera.iso);
}
}
else
{
// Setup variants
var adaptationMode = m_Exposure.adaptationMode.value;
if (!Application.isPlaying || hdCamera.resetPostProcessingHistory)
adaptationMode = AdaptationMode.Fixed;
parameters.exposureVariants = m_ExposureVariants;
parameters.exposureVariants[0] = 1; // (int)exposureSettings.luminanceSource.value;
parameters.exposureVariants[1] = (int)m_Exposure.meteringMode.value;
parameters.exposureVariants[2] = (int)adaptationMode;
parameters.exposureVariants[3] = 0;
bool useTextureMask = m_Exposure.meteringMode.value == MeteringMode.MaskWeighted && m_Exposure.weightTextureMask.value != null;
parameters.textureMeteringMask = useTextureMask ? m_Exposure.weightTextureMask.value : Texture2D.whiteTexture;
ComputeProceduralMeteringParams(hdCamera, out parameters.proceduralMaskParams, out parameters.proceduralMaskParams2);
bool isHistogramBased = m_Exposure.mode.value == ExposureMode.AutomaticHistogram;
bool needsCurve = (isHistogramBased && m_Exposure.histogramUseCurveRemapping.value) || m_Exposure.mode.value == ExposureMode.CurveMapping;
parameters.histogramUsesCurve = m_Exposure.histogramUseCurveRemapping.value;
parameters.adaptationParams = new Vector4(m_Exposure.adaptationSpeedLightToDark.value, m_Exposure.adaptationSpeedDarkToLight.value, 0.0f, 0.0f);
parameters.exposureMode = m_Exposure.mode.value;
float limitMax = m_Exposure.limitMax.value;
float limitMin = m_Exposure.limitMin.value;
float curveMin = 0.0f;
float curveMax = 0.0f;
if (needsCurve)
{
PrepareExposureCurveData(out curveMin, out curveMax);
limitMin = curveMin;
limitMax = curveMax;
}
parameters.exposureParams = new Vector4(m_Exposure.compensation.value + m_DebugExposureCompensation, limitMin, limitMax, 0f);
parameters.exposureParams2 = new Vector4(curveMin, curveMax, ColorUtils.lensImperfectionExposureScale, ColorUtils.s_LightMeterCalibrationConstant);
parameters.exposureCurve = m_ExposureCurveTexture;
if (isHistogramBased)
{
ValidateComputeBuffer(ref m_HistogramBuffer, k_HistogramBins, sizeof(uint));
m_HistogramBuffer.SetData(m_EmptyHistogram); // Clear the histogram
Vector2 histogramFraction = m_Exposure.histogramPercentages.value / 100.0f;
float evRange = limitMax - limitMin;
float histScale = 1.0f / Mathf.Max(1e-5f, evRange);
float histBias = -limitMin * histScale;
parameters.histogramExposureParams = new Vector4(histScale, histBias, histogramFraction.x, histogramFraction.y);
parameters.histogramBuffer = m_HistogramBuffer;
parameters.histogramOutputDebugData = m_HDInstance.m_CurrentDebugDisplaySettings.data.lightingDebugSettings.exposureDebugMode == ExposureDebugMode.HistogramView;
if (parameters.histogramOutputDebugData)
{
parameters.histogramExposureCS.EnableKeyword("OUTPUT_DEBUG_DATA");
}
parameters.exposurePreparationKernel = parameters.histogramExposureCS.FindKernel("KHistogramGen");
parameters.exposureReductionKernel = parameters.histogramExposureCS.FindKernel("KHistogramReduce");
}
else
{
parameters.exposurePreparationKernel = parameters.exposureCS.FindKernel("KPrePass");
parameters.exposureReductionKernel = parameters.exposureCS.FindKernel("KReduction");
}
}
return parameters;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool IsExposureFixed(HDCamera camera) => m_Exposure.mode.value == ExposureMode.Fixed || m_Exposure.mode.value == ExposureMode.UsePhysicalCamera
#if UNITY_EDITOR
|| (camera.camera.cameraType == CameraType.SceneView && HDAdditionalSceneViewSettings.sceneExposureOverriden)
#endif
;
//if exposure comes from the parent camera, it means we dont have to calculate / force it.
//Its already been done in the parent camera.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool CanRunFixedExposurePass(HDCamera camera) => IsExposureFixed(camera)
&& camera.exposureControlFS && camera.currentExposureTextures.useCurrentCamera
&& camera.currentExposureTextures.current != null;
public RTHandle GetExposureTexture(HDCamera camera)
{
// Note: GetExposureTexture(camera) must be call AFTER the call of DoFixedExposure to be correctly taken into account
// When we use Dynamic Exposure and we reset history we can't use pre-exposure (as there is no information)
// For this reasons we put neutral value at the beginning of the frame in Exposure textures and
// apply processed exposure from color buffer at the end of the Frame, only for a single frame.
// After that we re-use the pre-exposure system
if (m_Exposure != null && (camera.resetPostProcessingHistory && camera.currentExposureTextures.useCurrentCamera) && !IsExposureFixed(camera))
return m_EmptyExposureTexture;
// 1x1 pixel, holds the current exposure multiplied in the red channel and EV100 value
// in the green channel
// One frame delay + history RTs being flipped at the beginning of the frame means we
// have to grab the exposure marked as "previous"
return GetExposureTextureHandle(camera.currentExposureTextures.current);
}
public RTHandle GetExposureTextureHandle(RTHandle rt)
{
return rt ?? m_EmptyExposureTexture;
}
public RTHandle GetPreviousExposureTexture(HDCamera camera)
{
// See GetExposureTexture
return GetExposureTextureHandle(camera.currentExposureTextures.previous);
}
internal RTHandle GetExposureDebugData()
{
return m_DebugExposureData;
}
internal HableCurve GetCustomToneMapCurve()
{
return m_HableCurve;
}
internal int GetLutSize()
{
return m_LutSize;
}
internal ComputeBuffer GetHistogramBuffer()
{
return m_HistogramBuffer;
}
internal void ComputeProceduralMeteringParams(HDCamera camera, out Vector4 proceduralParams1, out Vector4 proceduralParams2)
{
Vector2 proceduralCenter = m_Exposure.proceduralCenter.value;
if (camera.exposureTarget != null && m_Exposure.centerAroundExposureTarget.value)
{
var transform = camera.exposureTarget.transform;
// Transform in screen space
Vector3 targetLocation = transform.position;
if (ShaderConfig.s_CameraRelativeRendering != 0)
{
targetLocation -= camera.camera.transform.position;
}
var ndcLoc = camera.mainViewConstants.viewProjMatrix * (targetLocation);
ndcLoc.x /= ndcLoc.w;
ndcLoc.y /= ndcLoc.w;
Vector2 targetUV = new Vector2(ndcLoc.x, ndcLoc.y) * 0.5f + new Vector2(0.5f, 0.5f);
targetUV.y = 1.0f - targetUV.y;
proceduralCenter += targetUV;
}
proceduralCenter.x = Mathf.Clamp01(proceduralCenter.x);
proceduralCenter.y = Mathf.Clamp01(proceduralCenter.y);
proceduralCenter.x *= camera.actualWidth;
proceduralCenter.y *= camera.actualHeight;
float screenDiagonal = 0.5f * (camera.actualHeight + camera.actualWidth);
proceduralParams1 = new Vector4(proceduralCenter.x, proceduralCenter.y,
m_Exposure.proceduralRadii.value.x * camera.actualWidth,
m_Exposure.proceduralRadii.value.y * camera.actualHeight);
proceduralParams2 = new Vector4(1.0f / m_Exposure.proceduralSoftness.value, LightUtils.ConvertEvToLuminance(m_Exposure.maskMinIntensity.value), LightUtils.ConvertEvToLuminance(m_Exposure.maskMaxIntensity.value), 0.0f);
}
internal ComputeBuffer GetDebugImageHistogramBuffer()
{
return m_DebugImageHistogramBuffer;
}
void DoFixedExposure(in ExposureParameters parameters, CommandBuffer cmd, RTHandle prevExposure)
{
var cs = parameters.exposureCS;
int kernel = parameters.exposureReductionKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams, parameters.exposureParams);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams2, parameters.exposureParams2);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, prevExposure);
cmd.DispatchCompute(cs, kernel, 1, 1, 1);
}
void PrepareExposureCurveData(out float min, out float max)
{
var curve = m_Exposure.curveMap.value;
var minCurve = m_Exposure.limitMinCurveMap.value;
var maxCurve = m_Exposure.limitMaxCurveMap.value;
if (m_ExposureCurveTexture == null)
{
m_ExposureCurveTexture = new Texture2D(k_ExposureCurvePrecision, 1, TextureFormat.RGBAHalf, false, true)
{
name = "Exposure Curve",
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp
};
m_ExposureCurveTexture.hideFlags = HideFlags.HideAndDontSave;
}
bool minCurveHasPoints = minCurve.length > 0;
bool maxCurveHasPoints = maxCurve.length > 0;
float defaultMin = -100.0f;
float defaultMax = 100.0f;
var pixels = m_ExposureCurveColorArray;
// Fail safe in case the curve is deleted / has 0 point
if (curve == null || curve.length == 0)
{
min = 0f;
max = 0f;
for (int i = 0; i < k_ExposureCurvePrecision; i++)
pixels[i] = Color.clear;
}
else
{
min = curve[0].time;
max = curve[curve.length - 1].time;
float step = (max - min) / (k_ExposureCurvePrecision - 1f);
for (int i = 0; i < k_ExposureCurvePrecision; i++)
{
float currTime = min + step * i;
pixels[i] = new Color(curve.Evaluate(currTime),
minCurveHasPoints ? minCurve.Evaluate(currTime) : defaultMin,
maxCurveHasPoints ? maxCurve.Evaluate(currTime) : defaultMax,
0f);
}
}
m_ExposureCurveTexture.SetPixels(pixels);
m_ExposureCurveTexture.Apply();
}
void GrabExposureRequiredTextures(HDCamera camera, out RTHandle prevExposure, out RTHandle nextExposure)
{
prevExposure = camera.currentExposureTextures.current;
nextExposure = camera.currentExposureTextures.previous;
if (camera.resetPostProcessingHistory)
{
// For Dynamic Exposure, we need to undo the pre-exposure from the color buffer to calculate the correct one
// When we reset history we must setup neutral value
prevExposure = m_EmptyExposureTexture; // Use neutral texture
}
}
static void DoDynamicExposure(in ExposureParameters exposureParameters, CommandBuffer cmd, RTHandle colorBuffer, RTHandle prevExposure, RTHandle nextExposure, RTHandle tmpRenderTarget1024, RTHandle tmpRenderTarget32)
{
var cs = exposureParameters.exposureCS;
int kernel;
var sourceTex = colorBuffer;
kernel = exposureParameters.exposurePreparationKernel;
cmd.SetComputeIntParams(cs, HDShaderIDs._Variants, exposureParameters.exposureVariants);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._PreviousExposureTexture, prevExposure);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._SourceTexture, sourceTex);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams2, exposureParameters.exposureParams2);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureWeightMask, exposureParameters.textureMeteringMask);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ProceduralMaskParams, exposureParameters.proceduralMaskParams);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ProceduralMaskParams2, exposureParameters.proceduralMaskParams2);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, tmpRenderTarget1024);
cmd.DispatchCompute(cs, kernel, 1024 / 8, 1024 / 8, 1);
// Reduction: 1st pass (1024 -> 32)
kernel = exposureParameters.exposureReductionKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._PreviousExposureTexture, prevExposure);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureCurveTexture, Texture2D.blackTexture);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, tmpRenderTarget1024);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, tmpRenderTarget32);
cmd.DispatchCompute(cs, kernel, 32, 32, 1);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams, exposureParameters.exposureParams);
// Reduction: 2nd pass (32 -> 1) + evaluate exposure
if (exposureParameters.exposureMode == ExposureMode.Automatic)
{
exposureParameters.exposureVariants[3] = 1;
}
else if (exposureParameters.exposureMode == ExposureMode.CurveMapping)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureCurveTexture, exposureParameters.exposureCurve);
exposureParameters.exposureVariants[3] = 2;
}
cmd.SetComputeVectorParam(cs, HDShaderIDs._AdaptationParams, exposureParameters.adaptationParams);
cmd.SetComputeIntParams(cs, HDShaderIDs._Variants, exposureParameters.exposureVariants);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._PreviousExposureTexture, prevExposure);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, tmpRenderTarget32);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, nextExposure);
cmd.DispatchCompute(cs, kernel, 1, 1, 1);
}
static void DoHistogramBasedExposure(in ExposureParameters exposureParameters, CommandBuffer cmd, RTHandle sourceTexture, RTHandle prevExposure, RTHandle nextExposure, RTHandle debugData)
{
var cs = exposureParameters.histogramExposureCS;
int kernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._ProceduralMaskParams, exposureParameters.proceduralMaskParams);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ProceduralMaskParams2, exposureParameters.proceduralMaskParams2);
cmd.SetComputeVectorParam(cs, HDShaderIDs._HistogramExposureParams, exposureParameters.histogramExposureParams);
// Generate histogram.
kernel = exposureParameters.exposurePreparationKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._PreviousExposureTexture, prevExposure);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._SourceTexture, sourceTexture);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureWeightMask, exposureParameters.textureMeteringMask);
cmd.SetComputeIntParams(cs, HDShaderIDs._Variants, exposureParameters.exposureVariants);
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._HistogramBuffer, exposureParameters.histogramBuffer);
int threadGroupSizeX = 16;
int threadGroupSizeY = 8;
int dispatchSizeX = HDUtils.DivRoundUp(exposureParameters.camera.actualWidth / 2, threadGroupSizeX);
int dispatchSizeY = HDUtils.DivRoundUp(exposureParameters.camera.actualHeight / 2, threadGroupSizeY);
cmd.DispatchCompute(cs, kernel, dispatchSizeX, dispatchSizeY, 1);
// Now read the histogram
kernel = exposureParameters.exposureReductionKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams, exposureParameters.exposureParams);
cmd.SetComputeVectorParam(cs, HDShaderIDs._ExposureParams2, exposureParameters.exposureParams2);
cmd.SetComputeVectorParam(cs, HDShaderIDs._AdaptationParams, exposureParameters.adaptationParams);
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._HistogramBuffer, exposureParameters.histogramBuffer);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._PreviousExposureTexture, prevExposure);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, nextExposure);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureCurveTexture, exposureParameters.exposureCurve);
exposureParameters.exposureVariants[3] = 0;
if (exposureParameters.histogramUsesCurve)
{
exposureParameters.exposureVariants[3] = 2;
}
cmd.SetComputeIntParams(cs, HDShaderIDs._Variants, exposureParameters.exposureVariants);
if (exposureParameters.histogramOutputDebugData)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._ExposureDebugTexture, debugData);
}
cmd.DispatchCompute(cs, kernel, 1, 1, 1);
}
internal struct DebugImageHistogramParameters
{
public ComputeShader debugImageHistogramCS;
public ComputeBuffer imageHistogram;
public int debugImageHistogramKernel;
public int cameraWidth;
public int cameraHeight;
}
internal DebugImageHistogramParameters PrepareDebugImageHistogramParameters(HDCamera camera)
{
DebugImageHistogramParameters parameters = new DebugImageHistogramParameters();
parameters.debugImageHistogramCS = m_Resources.shaders.debugImageHistogramCS;
parameters.debugImageHistogramKernel = parameters.debugImageHistogramCS.FindKernel("KHistogramGen");
ValidateComputeBuffer(ref m_DebugImageHistogramBuffer, k_DebugImageHistogramBins * 4, sizeof(uint));
m_DebugImageHistogramBuffer.SetData(m_EmptyDebugImageHistogram); // Clear the histogram
parameters.imageHistogram = m_DebugImageHistogramBuffer;
parameters.cameraWidth = camera.actualWidth;
parameters.cameraHeight = camera.actualHeight;
return parameters;
}
static internal void GenerateDebugImageHistogram(in DebugImageHistogramParameters parameters, CommandBuffer cmd, RTHandle sourceTexture)
{
var cs = parameters.debugImageHistogramCS;
int kernel = parameters.debugImageHistogramKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._SourceTexture, sourceTexture);
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._HistogramBuffer, parameters.imageHistogram);
int threadGroupSizeX = 16;
int threadGroupSizeY = 16;
int dispatchSizeX = HDUtils.DivRoundUp(parameters.cameraWidth / 2, threadGroupSizeX);
int dispatchSizeY = HDUtils.DivRoundUp(parameters.cameraHeight / 2, threadGroupSizeY);
int totalPixels = parameters.cameraWidth * parameters.cameraHeight;
cmd.DispatchCompute(cs, kernel, dispatchSizeX, dispatchSizeY, 1);
}
#endregion
#region Temporal Anti-aliasing
struct TemporalAntiAliasingParameters
{
public Material temporalAAMaterial;
public MaterialPropertyBlock taaHistoryPropertyBlock;
public MaterialPropertyBlock taaPropertyBlock;
public bool resetPostProcessingHistory;
public Vector4 previousScreenSize;
public Vector4 taaParameters;
public Vector4 taaFilterWeights;
public bool motionVectorRejection;
}
TemporalAntiAliasingParameters PrepareTAAParameters(HDCamera camera, bool PostDOF = false)
{
TemporalAntiAliasingParameters parameters = new TemporalAntiAliasingParameters();
parameters.resetPostProcessingHistory = camera.resetPostProcessingHistory;
float minAntiflicker = 0.0f;
float maxAntiflicker = 3.5f;
float motionRejectionMultiplier = Mathf.Lerp(0.0f, 250.0f, camera.taaMotionVectorRejection * camera.taaMotionVectorRejection * camera.taaMotionVectorRejection);
// The anti flicker becomes much more aggressive on higher values
float temporalContrastForMaxAntiFlicker = 0.7f - Mathf.Lerp(0.0f, 0.3f, Mathf.SmoothStep(0.5f, 1.0f, camera.taaAntiFlicker));
parameters.taaParameters = new Vector4(camera.taaHistorySharpening, PostDOF ? maxAntiflicker : Mathf.Lerp(minAntiflicker, maxAntiflicker, camera.taaAntiFlicker), motionRejectionMultiplier, temporalContrastForMaxAntiFlicker);
// Precompute weights used for the Blackman-Harris filter. TODO: Note that these are slightly wrong as they don't take into account the jitter size. This needs to be fixed at some point.
float crossWeights = Mathf.Exp(-2.29f * 2);
float plusWeights = Mathf.Exp(-2.29f);
float centerWeight = 1;
float totalWeight = centerWeight + (4 * plusWeights);
if (camera.TAAQuality == HDAdditionalCameraData.TAAQualityLevel.High)
{
totalWeight += crossWeights * 4;
}
// Weights will be x: central, y: plus neighbours, z: cross neighbours, w: total
parameters.taaFilterWeights = new Vector4(centerWeight / totalWeight, plusWeights / totalWeight, crossWeights / totalWeight, totalWeight);
parameters.temporalAAMaterial = m_TemporalAAMaterial;
parameters.temporalAAMaterial.shaderKeywords = null;
if (m_EnableAlpha)
{
parameters.temporalAAMaterial.EnableKeyword("ENABLE_ALPHA");
}
if (camera.taaHistorySharpening == 0)
{
parameters.temporalAAMaterial.EnableKeyword("FORCE_BILINEAR_HISTORY");
}
if (camera.taaHistorySharpening != 0 && camera.taaAntiRinging && camera.TAAQuality == HDAdditionalCameraData.TAAQualityLevel.High)
{
parameters.temporalAAMaterial.EnableKeyword("ANTI_RINGING");
}
parameters.motionVectorRejection = camera.taaMotionVectorRejection > 0;
if (parameters.motionVectorRejection)
{
parameters.temporalAAMaterial.EnableKeyword("ENABLE_MV_REJECTION");
}
if (PostDOF)
{
parameters.temporalAAMaterial.EnableKeyword("POST_DOF");
}
else
{
switch (camera.TAAQuality)
{
case HDAdditionalCameraData.TAAQualityLevel.Low:
parameters.temporalAAMaterial.EnableKeyword("LOW_QUALITY");
break;
case HDAdditionalCameraData.TAAQualityLevel.Medium:
parameters.temporalAAMaterial.EnableKeyword("MEDIUM_QUALITY");
break;
case HDAdditionalCameraData.TAAQualityLevel.High:
parameters.temporalAAMaterial.EnableKeyword("HIGH_QUALITY");
break;
default:
parameters.temporalAAMaterial.EnableKeyword("MEDIUM_QUALITY");
break;
}
}
parameters.taaHistoryPropertyBlock = m_TAAHistoryBlitPropertyBlock;
parameters.taaPropertyBlock = m_TAAPropertyBlock;
Vector2Int prevViewPort = camera.historyRTHandleProperties.previousViewportSize;
parameters.previousScreenSize = new Vector4(prevViewPort.x, prevViewPort.y, 1.0f / prevViewPort.x, 1.0f / prevViewPort.y);
return parameters;
}
static void DoTemporalAntialiasing(in TemporalAntiAliasingParameters taaParams,
CommandBuffer cmd,
RTHandle source,
RTHandle destination,
RTHandle motionVecTexture,
RTHandle depthBuffer,
RTHandle depthMipChain,
RTHandle prevHistory,
RTHandle nextHistory,
RTHandle prevMVLen,
RTHandle nextMVLen)
{
if (taaParams.resetPostProcessingHistory)
{
taaParams.taaHistoryPropertyBlock.SetTexture(HDShaderIDs._BlitTexture, source);
var rtScaleSource = source.rtHandleProperties.rtHandleScale;
taaParams.taaHistoryPropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, new Vector4(rtScaleSource.x, rtScaleSource.y, 0.0f, 0.0f));
taaParams.taaHistoryPropertyBlock.SetFloat(HDShaderIDs._BlitMipLevel, 0);
HDUtils.DrawFullScreen(cmd, HDUtils.GetBlitMaterial(source.rt.dimension), prevHistory, taaParams.taaHistoryPropertyBlock, 0);
HDUtils.DrawFullScreen(cmd, HDUtils.GetBlitMaterial(source.rt.dimension), nextHistory, taaParams.taaHistoryPropertyBlock, 0);
}
taaParams.taaPropertyBlock.SetInt(HDShaderIDs._StencilMask, (int)StencilUsage.ExcludeFromTAA);
taaParams.taaPropertyBlock.SetInt(HDShaderIDs._StencilRef, (int)StencilUsage.ExcludeFromTAA);
taaParams.taaPropertyBlock.SetTexture(HDShaderIDs._CameraMotionVectorsTexture, motionVecTexture);
taaParams.taaPropertyBlock.SetTexture(HDShaderIDs._InputTexture, source);
taaParams.taaPropertyBlock.SetTexture(HDShaderIDs._InputHistoryTexture, prevHistory);
if (prevMVLen != null && taaParams.motionVectorRejection)
{
taaParams.taaPropertyBlock.SetTexture(HDShaderIDs._InputVelocityMagnitudeHistory, prevMVLen);
}
taaParams.taaPropertyBlock.SetTexture(HDShaderIDs._DepthTexture, depthMipChain);
var taaHistorySize = taaParams.previousScreenSize;
taaParams.taaPropertyBlock.SetVector(HDShaderIDs._TaaPostParameters, taaParams.taaParameters);
taaParams.taaPropertyBlock.SetVector(HDShaderIDs._TaaHistorySize, taaHistorySize);
taaParams.taaPropertyBlock.SetVector(HDShaderIDs._TaaFilterWeights, taaParams.taaFilterWeights);
CoreUtils.SetRenderTarget(cmd, destination, depthBuffer);
cmd.SetRandomWriteTarget(1, nextHistory);
if (nextMVLen != null && taaParams.motionVectorRejection)
{
cmd.SetRandomWriteTarget(2, nextMVLen);
}
cmd.DrawProcedural(Matrix4x4.identity, taaParams.temporalAAMaterial, 0, MeshTopology.Triangles, 3, 1, taaParams.taaPropertyBlock);
cmd.DrawProcedural(Matrix4x4.identity, taaParams.temporalAAMaterial, 1, MeshTopology.Triangles, 3, 1, taaParams.taaPropertyBlock);
cmd.ClearRandomWriteTargets();
}
void GrabTemporalAntialiasingHistoryTextures(HDCamera camera, out RTHandle previous, out RTHandle next, bool postDoF = false)
{
RTHandle Allocator(string id, int frameIndex, RTHandleSystem rtHandleSystem)
{
return rtHandleSystem.Alloc(
Vector2.one, TextureXR.slices, DepthBits.None, dimension: TextureXR.dimension,
filterMode: FilterMode.Bilinear, colorFormat: m_ColorFormat,
enableRandomWrite: true, useDynamicScale: true, name: $"{id} TAA History"
);
}
int historyType = (int)(postDoF ?
HDCameraFrameHistoryType.TemporalAntialiasingPostDoF : HDCameraFrameHistoryType.TemporalAntialiasing);
next = camera.GetCurrentFrameRT(historyType)
?? camera.AllocHistoryFrameRT(historyType, Allocator, 2);
previous = camera.GetPreviousFrameRT(historyType);
}
void GrabVelocityMagnitudeHistoryTextures(HDCamera camera, out RTHandle previous, out RTHandle next)
{
RTHandle Allocator(string id, int frameIndex, RTHandleSystem rtHandleSystem)
{
return rtHandleSystem.Alloc(
Vector2.one, TextureXR.slices, DepthBits.None, dimension: TextureXR.dimension,
filterMode: FilterMode.Bilinear, colorFormat: GraphicsFormat.R16_SFloat,
enableRandomWrite: true, useDynamicScale: true, name: $"{id} Velocity magnitude"
);
}
next = camera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.TAAMotionVectorMagnitude)
?? camera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.TAAMotionVectorMagnitude, Allocator, 2);
previous = camera.GetPreviousFrameRT((int)HDCameraFrameHistoryType.TAAMotionVectorMagnitude);
}
void ReleasePostDoFTAAHistoryTextures(HDCamera camera)
{
var rt = camera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.TemporalAntialiasingPostDoF);
if (rt != null)
{
camera.ReleaseHistoryFrameRT((int)HDCameraFrameHistoryType.TemporalAntialiasingPostDoF);
}
}
#endregion
#region Depth Of Field
struct DepthOfFieldParameters
{
public ComputeShader dofKernelCS;
public int dofKernelKernel;
public ComputeShader dofCoCCS;
public int dofCoCKernel;
public ComputeShader dofCoCReprojectCS;
public int dofCoCReprojectKernel;
public ComputeShader dofDilateCS;
public int dofDilateKernel;
public ComputeShader dofMipCS;
public int dofMipColorKernel;
public int dofMipCoCKernel;
public ComputeShader dofMipSafeCS;
public int dofMipSafeKernel;
public ComputeShader dofPrefilterCS;
public int dofPrefilterKernel;
public ComputeShader dofTileMaxCS;
public int dofTileMaxKernel;
public ComputeShader dofGatherCS;
public int dofGatherNearKernel;
public int dofGatherFarKernel;
public ComputeShader dofCombineCS;
public int dofCombineKernel;
public ComputeShader dofPrecombineFarCS;
public int dofPrecombineFarKernel;
public ComputeShader dofClearIndirectArgsCS;
public int dofClearIndirectArgsKernel;
// PB DoF shaders
public ComputeShader dofCircleOfConfusionCS;
public int dofCircleOfConfusionKernel;
public ComputeShader pbDoFCoCMinMaxCS;
public int pbDoFMinMaxKernel;
public ComputeShader pbDoFGatherCS;
public int pbDoFGatherKernel;
public ComputeShader pbDoFDilateCS;
public int pbDoFDilateKernel;
public int minMaxCoCTileSize;
public BlueNoise.DitheredTextureSet ditheredTextureSet;
public HDCamera camera;
public bool nearLayerActive;
public bool farLayerActive;
public bool highQualityFiltering;
public bool useTiles;
public bool resetPostProcessingHistory;
public DepthOfFieldResolution resolution;
public DepthOfFieldMode focusMode;
public Vector2 physicalCameraCurvature;
public float physicalCameraAperture;
public float physicalCameraAnamorphism;
public float physicalCameraBarrelClipping;
public int physicalCameraBladeCount;
public int farSampleCount;
public int nearSampleCount;
public float farMaxBlur;
public float nearMaxBlur;
public float nearFocusStart;
public float nearFocusEnd;
public float farFocusStart;
public float farFocusEnd;
public float focusDistance;
public Vector2Int threadGroup8;
public bool useMipSafePath;
}
DepthOfFieldParameters PrepareDoFParameters(HDCamera camera)
{
DepthOfFieldParameters parameters = new DepthOfFieldParameters();
parameters.dofKernelCS = m_Resources.shaders.depthOfFieldKernelCS;
parameters.dofKernelKernel = parameters.dofKernelCS.FindKernel("KParametricBlurKernel");
parameters.dofCoCCS = m_Resources.shaders.depthOfFieldCoCCS;
parameters.dofCoCReprojectCS = m_Resources.shaders.depthOfFieldCoCReprojectCS;
parameters.dofCoCReprojectKernel = parameters.dofCoCReprojectCS.FindKernel("KMain");
parameters.dofDilateCS = m_Resources.shaders.depthOfFieldDilateCS;
parameters.dofDilateKernel = parameters.dofDilateCS.FindKernel("KMain");
parameters.dofMipCS = m_Resources.shaders.depthOfFieldMipCS;
if (!m_DepthOfField.physicallyBased)
{
parameters.dofMipColorKernel = parameters.dofMipCS.FindKernel(m_EnableAlpha ? "KMainColorAlpha" : "KMainColor");
}
else
{
parameters.dofMipColorKernel = parameters.dofMipCS.FindKernel(m_EnableAlpha ? "KMainColorCopyAlpha" : "KMainColorCopy");
}
parameters.dofMipCoCKernel = parameters.dofMipCS.FindKernel("KMainCoC");
parameters.dofMipSafeCS = m_Resources.shaders.depthOfFieldMipSafeCS;
parameters.dofPrefilterCS = m_Resources.shaders.depthOfFieldPrefilterCS;
parameters.dofTileMaxCS = m_Resources.shaders.depthOfFieldTileMaxCS;
parameters.dofTileMaxKernel = parameters.dofTileMaxCS.FindKernel("KMain");
parameters.dofGatherCS = m_Resources.shaders.depthOfFieldGatherCS;
parameters.dofGatherNearKernel = parameters.dofGatherCS.FindKernel("KMainNear");
parameters.dofGatherFarKernel = parameters.dofGatherCS.FindKernel("KMainFar");
parameters.dofCombineCS = m_Resources.shaders.depthOfFieldCombineCS;
parameters.dofCombineKernel = parameters.dofCombineCS.FindKernel("KMain");
parameters.dofPrecombineFarCS = m_Resources.shaders.depthOfFieldPreCombineFarCS;
parameters.dofPrecombineFarKernel = parameters.dofPrecombineFarCS.FindKernel("KMainPreCombineFar");
parameters.dofClearIndirectArgsCS = m_Resources.shaders.depthOfFieldClearIndirectArgsCS;
parameters.dofClearIndirectArgsKernel = parameters.dofClearIndirectArgsCS.FindKernel("KClear");
parameters.dofCircleOfConfusionCS = m_Resources.shaders.dofCircleOfConfusion;
parameters.pbDoFCoCMinMaxCS = m_Resources.shaders.dofCoCMinMaxCS;
parameters.pbDoFMinMaxKernel = parameters.pbDoFCoCMinMaxCS.FindKernel("KMainCoCMinMax");
parameters.pbDoFDilateCS = m_Resources.shaders.dofMinMaxDilateCS;
parameters.pbDoFDilateKernel = parameters.pbDoFDilateCS.FindKernel("KMain");
parameters.pbDoFGatherCS = m_Resources.shaders.dofGatherCS;
parameters.pbDoFGatherKernel = parameters.pbDoFGatherCS.FindKernel("KMain");
parameters.minMaxCoCTileSize = 8;
parameters.camera = camera;
parameters.resetPostProcessingHistory = camera.resetPostProcessingHistory;
parameters.nearLayerActive = m_DepthOfField.IsNearLayerActive();
parameters.farLayerActive = m_DepthOfField.IsFarLayerActive();
parameters.highQualityFiltering = m_DepthOfField.highQualityFiltering;
parameters.useTiles = !camera.xr.singlePassEnabled;
parameters.resolution = m_DepthOfField.resolution;
float scale = m_DepthOfField.physicallyBased ? 1f : 1f / (float)parameters.resolution; // Note: physical dof always runs at full resolution
float resolutionScale = (camera.actualHeight / 1080f) * (scale * 2f);
int farSamples = Mathf.CeilToInt(m_DepthOfField.farSampleCount * resolutionScale);
int nearSamples = Mathf.CeilToInt(m_DepthOfField.nearSampleCount * resolutionScale);
// We want at least 3 samples for both far and near
parameters.farSampleCount = Mathf.Max(3, farSamples);
parameters.nearSampleCount = Mathf.Max(3, nearSamples);
parameters.farMaxBlur = m_DepthOfField.farMaxBlur;
parameters.nearMaxBlur = m_DepthOfField.nearMaxBlur;
int targetWidth = Mathf.RoundToInt(camera.actualWidth * scale);
int targetHeight = Mathf.RoundToInt(camera.actualHeight * scale);
int threadGroup8X = (targetWidth + 7) / 8;
int threadGroup8Y = (targetHeight + 7) / 8;
parameters.threadGroup8 = new Vector2Int(threadGroup8X, threadGroup8Y);
parameters.physicalCameraCurvature = m_PhysicalCamera.curvature;
parameters.physicalCameraAnamorphism = m_PhysicalCamera.anamorphism;
parameters.physicalCameraAperture = m_PhysicalCamera.aperture;
parameters.physicalCameraBarrelClipping = m_PhysicalCamera.barrelClipping;
parameters.physicalCameraBladeCount = m_PhysicalCamera.bladeCount;
parameters.nearFocusStart = m_DepthOfField.nearFocusStart.value;
parameters.nearFocusEnd = m_DepthOfField.nearFocusEnd.value;
parameters.farFocusStart = m_DepthOfField.farFocusStart.value;
parameters.farFocusEnd = m_DepthOfField.farFocusEnd.value;
parameters.focusDistance = m_DepthOfField.focusDistance.value;
parameters.focusMode = m_DepthOfField.focusMode.value;
if (parameters.focusMode == DepthOfFieldMode.UsePhysicalCamera)
{
parameters.dofCoCKernel = parameters.dofCoCCS.FindKernel("KMainPhysical");
parameters.dofCircleOfConfusionKernel = parameters.dofCircleOfConfusionCS.FindKernel("KMainCoCPhysical");
}
else
{
parameters.dofCoCKernel = parameters.dofCoCCS.FindKernel("KMainManual");
parameters.dofCircleOfConfusionKernel = parameters.dofCircleOfConfusionCS.FindKernel("KMainCoCManual");
}
parameters.dofPrefilterCS.shaderKeywords = null;
parameters.dofPrefilterKernel = parameters.dofPrefilterCS.FindKernel("KMain");
parameters.dofMipSafeCS.shaderKeywords = null;
parameters.dofMipSafeKernel = parameters.dofMipSafeCS.FindKernel("KMain");
parameters.dofTileMaxCS.shaderKeywords = null;
parameters.dofGatherCS.shaderKeywords = null;
parameters.dofCombineCS.shaderKeywords = null;
parameters.dofPrecombineFarCS.shaderKeywords = null;
parameters.dofCombineCS.shaderKeywords = null;
parameters.pbDoFGatherCS.shaderKeywords = null;
parameters.dofCoCReprojectCS.shaderKeywords = null;
parameters.dofCoCCS.shaderKeywords = null;
parameters.dofCircleOfConfusionCS.shaderKeywords = null;
bool nearLayerActive = parameters.nearLayerActive;
bool farLayerActive = parameters.farLayerActive;
bool bothLayersActive = nearLayerActive && farLayerActive;
if (m_EnableAlpha)
{
parameters.dofPrefilterCS.EnableKeyword("ENABLE_ALPHA");
parameters.dofMipSafeCS.EnableKeyword("ENABLE_ALPHA");
parameters.dofGatherCS.EnableKeyword("ENABLE_ALPHA");
parameters.dofCombineCS.EnableKeyword("ENABLE_ALPHA");
parameters.dofPrecombineFarCS.EnableKeyword("ENABLE_ALPHA");
parameters.pbDoFGatherCS.EnableKeyword("ENABLE_ALPHA");
}
if (parameters.resolution == DepthOfFieldResolution.Full)
{
parameters.dofPrefilterCS.EnableKeyword("FULL_RES");
parameters.dofCombineCS.EnableKeyword("FULL_RES");
}
else if (parameters.highQualityFiltering)
{
parameters.dofCombineCS.EnableKeyword("HIGH_QUALITY");
}
else
{
parameters.dofCombineCS.EnableKeyword("LOW_QUALITY");
}
if (bothLayersActive || nearLayerActive)
{
parameters.dofPrefilterCS.EnableKeyword("NEAR");
parameters.dofTileMaxCS.EnableKeyword("NEAR");
parameters.dofCombineCS.EnableKeyword("NEAR");
}
if (bothLayersActive || !nearLayerActive)
{
parameters.dofPrefilterCS.EnableKeyword("FAR");
parameters.dofTileMaxCS.EnableKeyword("FAR");
parameters.dofCombineCS.EnableKeyword("FAR");
}
if (parameters.useTiles)
{
parameters.dofGatherCS.EnableKeyword("USE_TILES");
}
if (m_DepthOfField.physicallyBased)
{
parameters.dofCoCReprojectCS.EnableKeyword("ENABLE_MAX_BLENDING");
BlueNoise blueNoise = m_HDInstance.GetBlueNoiseManager();
parameters.ditheredTextureSet = blueNoise.DitheredTextureSet256SPP();
}
if (camera.msaaSamples != MSAASamples.None)
{
// When MSAA is enabled, DoF should use the min depth of the MSAA samples to avoid 1-pixel ringing around in-focus objects [case 1347291]
parameters.dofCoCCS.EnableKeyword("USE_MIN_DEPTH");
parameters.dofCircleOfConfusionCS.EnableKeyword("USE_MIN_DEPTH");
}
parameters.useMipSafePath = m_UseSafePath;
return parameters;
}
static void GetDoFResolutionScale(in DepthOfFieldParameters dofParameters, out float scale, out float resolutionScale)
{
scale = 1f / (float)dofParameters.resolution;
resolutionScale = (dofParameters.camera.actualHeight / 1080f) * (scale * 2f);
}
//
// Reference used:
// "A Lens and Aperture Camera Model for Synthetic Image Generation" [Potmesil81]
// "A Low Distortion Map Between Disk and Square" [Shirley97] [Chiu97]
// "High Quality Antialiasing" [Lorach07]
// "CryEngine 3 Graphics Gems" [Sousa13]
// "Next Generation Post Processing in Call of Duty: Advanced Warfare" [Jimenez14]
//
// Note: do not merge if/else clauses for each layer, pass schedule order is important to
// reduce sync points on the GPU
//
// TODO: can be further optimized
// TODO: debug panel entries (coc, tiles, etc)
//
static void DoDepthOfField(in DepthOfFieldParameters dofParameters, CommandBuffer cmd, RTHandle source, RTHandle destination, RTHandle depthBuffer,
RTHandle pingNearRGB, RTHandle pongNearRGB, RTHandle nearCoC, RTHandle nearAlpha, RTHandle dilatedNearCoC,
RTHandle pingFarRGB, RTHandle pongFarRGB, RTHandle farCoC, RTHandle fullresCoC, RTHandle[] mips, RTHandle dilationPingPong,
RTHandle prevCoCHistory, RTHandle nextCoCHistory, RTHandle motionVecTexture,
ComputeBuffer bokehNearKernel, ComputeBuffer bokehFarKernel, ComputeBuffer bokehIndirectCmd, ComputeBuffer nearBokehTileList, ComputeBuffer farBokehTileList,
bool taaEnabled, RTHandle depthMinMaxAvgMSAA)
{
bool nearLayerActive = dofParameters.nearLayerActive;
bool farLayerActive = dofParameters.farLayerActive;
Assert.IsTrue(nearLayerActive || farLayerActive);
bool bothLayersActive = nearLayerActive && farLayerActive;
bool useTiles = dofParameters.useTiles;
bool hqFiltering = dofParameters.highQualityFiltering;
const uint kIndirectNearOffset = 0u * sizeof(uint);
const uint kIndirectFarOffset = 3u * sizeof(uint);
// -----------------------------------------------------------------------------
// Data prep
// The number of samples & max blur sizes are scaled according to the resolution, with a
// base scale of 1.0 for 1080p output
int bladeCount = dofParameters.physicalCameraBladeCount;
float rotation = (dofParameters.physicalCameraAperture - HDPhysicalCamera.kMinAperture) / (HDPhysicalCamera.kMaxAperture - HDPhysicalCamera.kMinAperture);
rotation *= (360f / bladeCount) * Mathf.Deg2Rad; // TODO: Crude approximation, make it correct
float ngonFactor = 1f;
if (dofParameters.physicalCameraCurvature.y - dofParameters.physicalCameraCurvature.x > 0f)
ngonFactor = (dofParameters.physicalCameraAperture - dofParameters.physicalCameraCurvature.x) / (dofParameters.physicalCameraCurvature.y - dofParameters.physicalCameraCurvature.x);
ngonFactor = Mathf.Clamp01(ngonFactor);
ngonFactor = Mathf.Lerp(ngonFactor, 0f, Mathf.Abs(dofParameters.physicalCameraAnamorphism));
float anamorphism = dofParameters.physicalCameraAnamorphism / 4f;
float barrelClipping = dofParameters.physicalCameraBarrelClipping / 3f;
GetDoFResolutionScale(dofParameters, out float scale, out float resolutionScale);
var screenScale = new Vector2(scale, scale);
int targetWidth = Mathf.RoundToInt(dofParameters.camera.actualWidth * scale);
int targetHeight = Mathf.RoundToInt(dofParameters.camera.actualHeight * scale);
cmd.SetGlobalVector(HDShaderIDs._TargetScale, new Vector4((float)dofParameters.resolution, scale, 0f, 0f));
int farSamples = dofParameters.farSampleCount;
int nearSamples = dofParameters.nearSampleCount;
float farMaxBlur = dofParameters.farMaxBlur * resolutionScale;
float nearMaxBlur = dofParameters.nearMaxBlur * resolutionScale;
// If TAA is enabled we use the camera history system to grab CoC history textures, but
// because these don't use the same RTHandleScale as the global one, we need to use
// the RTHandleScale of the history RTHandles
var cocHistoryScale = taaEnabled ? dofParameters.camera.historyRTHandleProperties.rtHandleScale : RTHandles.rtHandleProperties.rtHandleScale;
ComputeShader cs;
int kernel;
// -----------------------------------------------------------------------------
// Render logic
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldKernel)))
{
// -----------------------------------------------------------------------------
// Pass: generate bokeh kernels
// Given that we allow full customization of near & far planes we'll need a separate
// kernel for each layer
cs = dofParameters.dofKernelCS;
kernel = dofParameters.dofKernelKernel;
// Near samples
if (nearLayerActive)
{
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params1, new Vector4(nearSamples, ngonFactor, bladeCount, rotation));
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params2, new Vector4(anamorphism, 0f, 0f, 0f));
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._BokehKernel, bokehNearKernel);
cmd.DispatchCompute(cs, kernel, Mathf.CeilToInt((nearSamples * nearSamples) / 64f), 1, 1);
}
// Far samples
if (farLayerActive)
{
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params1, new Vector4(farSamples, ngonFactor, bladeCount, rotation));
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params2, new Vector4(anamorphism, 0f, 0f, 0f));
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._BokehKernel, bokehFarKernel);
cmd.DispatchCompute(cs, kernel, Mathf.CeilToInt((farSamples * farSamples) / 64f), 1, 1);
}
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldCoC)))
{
// -----------------------------------------------------------------------------
// Pass: compute CoC in full-screen (needed for temporal re-projection & combine)
// CoC is initially stored in a RHalf texture in range [-1,1] as it makes RT
// management easier and temporal re-projection cheaper; later transformed into
// individual targets for near & far layers
cs = dofParameters.dofCoCCS;
kernel = dofParameters.dofCoCKernel;
if (dofParameters.focusMode == DepthOfFieldMode.UsePhysicalCamera)
{
// "A Lens and Aperture Camera Model for Synthetic Image Generation" [Potmesil81]
float F = dofParameters.camera.camera.focalLength / 1000f;
float A = dofParameters.camera.camera.focalLength / dofParameters.physicalCameraAperture;
float P = dofParameters.focusDistance;
float maxCoC = (A * F) / Mathf.Max((P - F), 1e-6f);
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(P, maxCoC, 0f, 0f));
}
else // DepthOfFieldMode.Manual
{
float nearEnd = dofParameters.nearFocusEnd;
float nearStart = Mathf.Min(dofParameters.nearFocusStart, nearEnd - 1e-5f);
float farStart = Mathf.Max(dofParameters.farFocusStart, nearEnd);
float farEnd = Mathf.Max(dofParameters.farFocusEnd, farStart + 1e-5f);
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(nearStart, nearEnd, farStart, farEnd));
}
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._CameraDepthTexture, depthBuffer);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputCoCTexture, fullresCoC);
if (dofParameters.camera.msaaSamples != MSAASamples.None)
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._DepthMinMaxAvg, depthMinMaxAvgMSAA);
cmd.DispatchCompute(cs, kernel, (dofParameters.camera.actualWidth + 7) / 8, (dofParameters.camera.actualHeight + 7) / 8, dofParameters.camera.viewCount);
// -----------------------------------------------------------------------------
// Pass: re-project CoC if TAA is enabled
if (taaEnabled)
{
ReprojectCoCHistory(dofParameters, cmd, dofParameters.camera, prevCoCHistory, nextCoCHistory, motionVecTexture, ref fullresCoC);
}
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldPrefilter)))
{
// -----------------------------------------------------------------------------
// Pass: downsample & prefilter CoC and layers
// We only need to pre-multiply the CoC for the far layer; if only near is being
// rendered we can use the downsampled color target as-is
// TODO: We may want to add an anti-flicker here
cs = dofParameters.dofPrefilterCS;
kernel = dofParameters.dofPrefilterKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, fullresCoC);
cmd.SetComputeVectorParam(cs, HDShaderIDs._CoCTargetScale, cocHistoryScale);
if (nearLayerActive)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputNearCoCTexture, nearCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputNearTexture, pingNearRGB);
}
if (farLayerActive)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputFarCoCTexture, farCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputFarTexture, pingFarRGB);
}
cmd.DispatchCompute(cs, kernel, dofParameters.threadGroup8.x, dofParameters.threadGroup8.y, dofParameters.camera.viewCount);
}
if (farLayerActive)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldPyramid)))
{
// -----------------------------------------------------------------------------
// Pass: mip generation
// We only do this for the far layer because the near layer can't really use
// very wide radii due to reconstruction artifacts
int tx = ((targetWidth >> 1) + 7) / 8;
int ty = ((targetHeight >> 1) + 7) / 8;
if (dofParameters.useMipSafePath)
{
// The other compute fails hard on Intel because of texture format issues
cs = dofParameters.dofMipSafeCS;
kernel = dofParameters.dofMipSafeKernel;
var mipScale = scale;
for (int i = 0; i < 4; i++)
{
mipScale *= 0.5f;
var size = new Vector2Int(Mathf.RoundToInt(dofParameters.camera.actualWidth * mipScale), Mathf.RoundToInt(dofParameters.camera.actualHeight * mipScale));
var mip = mips[i];
cmd.SetComputeVectorParam(cs, HDShaderIDs._TexelSize, new Vector4(size.x, size.y, 1f / size.x, 1f / size.y));
int gx = (size.x + 7) / 8;
int gy = (size.y + 7) / 8;
// Downsample
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, pingFarRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, mip);
cmd.DispatchCompute(cs, kernel, gx, gy, dofParameters.camera.viewCount);
// Copy to mip
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, mip);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, pingFarRGB, i + 1);
cmd.DispatchCompute(cs, kernel, gx, gy, dofParameters.camera.viewCount);
}
}
else
{
cs = dofParameters.dofMipCS;
kernel = dofParameters.dofMipColorKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, pingFarRGB, 0);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip1, pingFarRGB, 1);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip2, pingFarRGB, 2);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip3, pingFarRGB, 3);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip4, pingFarRGB, 4);
cmd.DispatchCompute(cs, kernel, tx, ty, dofParameters.camera.viewCount);
}
cs = dofParameters.dofMipCS;
kernel = dofParameters.dofMipCoCKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, farCoC, 0);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip1, farCoC, 1);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip2, farCoC, 2);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip3, farCoC, 3);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip4, farCoC, 4);
cmd.DispatchCompute(cs, kernel, tx, ty, dofParameters.camera.viewCount);
}
}
if (nearLayerActive)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldDilate)))
{
// -----------------------------------------------------------------------------
// Pass: dilate the near CoC
cs = dofParameters.dofDilateCS;
kernel = dofParameters.dofDilateKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(targetWidth - 1, targetHeight - 1, 0f, 0f));
int passCount = Mathf.CeilToInt((nearMaxBlur + 2f) / 4f);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, nearCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputCoCTexture, dilatedNearCoC);
cmd.DispatchCompute(cs, kernel, dofParameters.threadGroup8.x, dofParameters.threadGroup8.y, dofParameters.camera.viewCount);
if (passCount > 1)
{
// Ping-pong
var src = dilatedNearCoC;
var dst = dilationPingPong;
for (int i = 1; i < passCount; i++)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, src);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputCoCTexture, dst);
cmd.DispatchCompute(cs, kernel, dofParameters.threadGroup8.x, dofParameters.threadGroup8.y, dofParameters.camera.viewCount);
CoreUtils.Swap(ref src, ref dst);
}
dilatedNearCoC = src;
}
}
}
if (useTiles)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldTileMax)))
{
// -----------------------------------------------------------------------------
// Pass: tile-max classification
// Clear the indirect command buffer
cs = dofParameters.dofClearIndirectArgsCS;
kernel = dofParameters.dofClearIndirectArgsKernel;
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._IndirectBuffer, bokehIndirectCmd);
cmd.DispatchCompute(cs, kernel, 1, 1, 1);
// Build the tile list & indirect command buffer
cs = dofParameters.dofTileMaxCS;
kernel = dofParameters.dofTileMaxKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(targetWidth - 1, targetHeight - 1, 0f, 0f));
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._IndirectBuffer, bokehIndirectCmd);
if (nearLayerActive)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputNearCoCTexture, dilatedNearCoC);
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._NearTileList, nearBokehTileList);
}
if (farLayerActive)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputFarCoCTexture, farCoC);
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._FarTileList, farBokehTileList);
}
cmd.DispatchCompute(cs, kernel, dofParameters.threadGroup8.x, dofParameters.threadGroup8.y, 1);
}
}
if (farLayerActive)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldGatherFar)))
{
// -----------------------------------------------------------------------------
// Pass: bokeh blur the far layer
if (useTiles)
{
// Need to clear dest as we recycle render targets and tiles won't write to
// all pixels thus leaving previous-frame info
cmd.SetRenderTarget(pongFarRGB);
cmd.ClearRenderTarget(false, true, Color.clear);
}
cs = dofParameters.dofGatherCS;
kernel = dofParameters.dofGatherFarKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(farSamples, farSamples * farSamples, barrelClipping, farMaxBlur));
cmd.SetComputeVectorParam(cs, HDShaderIDs._TexelSize, new Vector4(targetWidth, targetHeight, 1f / targetWidth, 1f / targetHeight));
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, pingFarRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, farCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, pongFarRGB);
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._BokehKernel, bokehFarKernel);
if (useTiles)
{
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._TileList, farBokehTileList);
cmd.DispatchCompute(cs, kernel, bokehIndirectCmd, kIndirectFarOffset);
}
else
{
cmd.DispatchCompute(cs, kernel, dofParameters.threadGroup8.x, dofParameters.threadGroup8.y, dofParameters.camera.viewCount);
}
}
}
if (nearLayerActive)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldPreCombine)))
{
// -----------------------------------------------------------------------------
// Pass: if the far layer was active, use it as a source for the near blur to
// avoid out-of-focus artifacts (e.g. near blur in front of far blur)
if (farLayerActive)
{
cs = dofParameters.dofPrecombineFarCS;
kernel = dofParameters.dofPrecombineFarKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, pingNearRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputFarTexture, pongFarRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, farCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, pongNearRGB);
cmd.DispatchCompute(cs, kernel, dofParameters.threadGroup8.x, dofParameters.threadGroup8.y, dofParameters.camera.viewCount);
CoreUtils.Swap(ref pingNearRGB, ref pongNearRGB);
}
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldGatherNear)))
{
// -----------------------------------------------------------------------------
// Pass: bokeh blur the near layer
if (useTiles)
{
// Same as the far layer, clear to discard garbage data
if (!farLayerActive)
{
cmd.SetRenderTarget(pongNearRGB);
cmd.ClearRenderTarget(false, true, Color.clear);
}
cmd.SetRenderTarget(nearAlpha);
cmd.ClearRenderTarget(false, true, Color.clear);
}
cs = dofParameters.dofGatherCS;
kernel = dofParameters.dofGatherNearKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(nearSamples, nearSamples * nearSamples, barrelClipping, nearMaxBlur));
cmd.SetComputeVectorParam(cs, HDShaderIDs._TexelSize, new Vector4(targetWidth, targetHeight, 1f / targetWidth, 1f / targetHeight));
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, pingNearRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, nearCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputDilatedCoCTexture, dilatedNearCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, pongNearRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputAlphaTexture, nearAlpha);
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._BokehKernel, bokehNearKernel);
if (useTiles)
{
cmd.SetComputeBufferParam(cs, kernel, HDShaderIDs._TileList, nearBokehTileList);
cmd.DispatchCompute(cs, kernel, bokehIndirectCmd, kIndirectNearOffset);
}
else
{
cmd.DispatchCompute(cs, kernel, dofParameters.threadGroup8.x, dofParameters.threadGroup8.y, dofParameters.camera.viewCount);
}
}
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldCombine)))
{
// -----------------------------------------------------------------------------
// Pass: combine blurred layers with source color
cs = dofParameters.dofCombineCS;
kernel = dofParameters.dofCombineKernel;
if (nearLayerActive)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputNearTexture, pongNearRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputNearAlphaTexture, nearAlpha);
}
if (farLayerActive)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputFarTexture, pongFarRGB);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, fullresCoC);
}
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.DispatchCompute(cs, kernel, (dofParameters.camera.actualWidth + 7) / 8, (dofParameters.camera.actualHeight + 7) / 8, dofParameters.camera.viewCount);
}
}
static RTHandle CoCAllocatorMipsTrue(string id, int frameIndex, RTHandleSystem rtHandleSystem)
{
return rtHandleSystem.Alloc(
Vector2.one, TextureXR.slices, DepthBits.None, GraphicsFormat.R16_SFloat,
dimension: TextureXR.dimension, enableRandomWrite: true, useMipMap: true, useDynamicScale: true, name: $"{id} CoC History"
);
}
static RTHandle CoCAllocatorMipsFalse(string id, int frameIndex, RTHandleSystem rtHandleSystem)
{
return rtHandleSystem.Alloc(
Vector2.one, TextureXR.slices, DepthBits.None, GraphicsFormat.R16_SFloat,
dimension: TextureXR.dimension, enableRandomWrite: true, useMipMap: false, useDynamicScale: true, name: $"{id} CoC History"
);
}
static void GrabCoCHistory(HDCamera camera, out RTHandle previous, out RTHandle next, bool useMips = false)
{
// WARNING WORKAROUND
// For some reason, the Allocator as it was declared before would capture the useMips parameter but only when both render graph and XR are enabled.
// To work around this we have two hard coded allocators and use one or the other depending on the parameters.
// Also don't try to put the right allocator in a temporary variable as it will also generate allocations hence the horrendous copy paste bellow.
if (useMips)
{
next = camera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.DepthOfFieldCoC)
?? camera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.DepthOfFieldCoC, CoCAllocatorMipsTrue, 2);
}
else
{
next = camera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.DepthOfFieldCoC)
?? camera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.DepthOfFieldCoC, CoCAllocatorMipsFalse, 2);
}
if (useMips == true && next.rt.mipmapCount == 1)
{
camera.ReleaseHistoryFrameRT((int)HDCameraFrameHistoryType.DepthOfFieldCoC);
next = camera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.DepthOfFieldCoC, CoCAllocatorMipsTrue, 2);
}
previous = camera.GetPreviousFrameRT((int)HDCameraFrameHistoryType.DepthOfFieldCoC);
}
static void ReprojectCoCHistory(in DepthOfFieldParameters parameters, CommandBuffer cmd, HDCamera camera, RTHandle prevCoC, RTHandle nextCoC, RTHandle motionVecTexture, ref RTHandle fullresCoC)
{
var cocHistoryScale = new Vector2(camera.historyRTHandleProperties.rtHandleScale.z, camera.historyRTHandleProperties.rtHandleScale.w);
//Note: this reprojection creates some ghosting, we should replace it with something based on the new TAA
ComputeShader cs = parameters.dofCoCReprojectCS;
int kernel = parameters.dofCoCReprojectKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(parameters.resetPostProcessingHistory ? 0f : 0.91f, cocHistoryScale.x, cocHistoryScale.y, 0f));
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, fullresCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputHistoryCoCTexture, prevCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputCoCTexture, nextCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._CameraMotionVectorsTexture, motionVecTexture);
cmd.DispatchCompute(cs, kernel, (camera.actualWidth + 7) / 8, (camera.actualHeight + 7) / 8, camera.viewCount);
fullresCoC = nextCoC;
}
static void DoPhysicallyBasedDepthOfField(in DepthOfFieldParameters dofParameters, CommandBuffer cmd, RTHandle source, RTHandle destination, RTHandle fullresCoC, RTHandle prevCoCHistory, RTHandle nextCoCHistory, RTHandle motionVecTexture, RTHandle sourcePyramid, RTHandle depthBuffer, RTHandle minMaxCoCPing, RTHandle minMaxCoCPong, bool taaEnabled, RTHandle depthMinMaxAvgMSAA)
{
float scale = 1f / (float)dofParameters.resolution;
int targetWidth = Mathf.RoundToInt(dofParameters.camera.actualWidth * scale);
int targetHeight = Mathf.RoundToInt(dofParameters.camera.actualHeight * scale);
// Map the old "max radius" parameters to a bigger range, so we can work on more challenging scenes, [0, 16] --> [0, 64]
Vector2 cocLimit = new Vector2(
Mathf.Max(4 * dofParameters.farMaxBlur, 0.01f),
Mathf.Max(4 * dofParameters.nearMaxBlur, 0.01f));
float maxCoc = Mathf.Max(cocLimit.x, cocLimit.y);
ComputeShader cs;
int kernel;
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldCoC)))
{
cs = dofParameters.dofCircleOfConfusionCS;
kernel = dofParameters.dofCircleOfConfusionKernel;
if (dofParameters.focusMode == DepthOfFieldMode.UsePhysicalCamera)
{
// The sensor scale is used to convert the CoC size from mm to screen pixels
float sensorScale;
if (dofParameters.camera.camera.gateFit == Camera.GateFitMode.Horizontal)
sensorScale = (0.5f / dofParameters.camera.camera.sensorSize.x) * dofParameters.camera.camera.pixelWidth;
else
sensorScale = (0.5f / dofParameters.camera.camera.sensorSize.y) * dofParameters.camera.camera.pixelHeight;
// "A Lens and Aperture Camera Model for Synthetic Image Generation" [Potmesil81]
// Note: Focus distance is in meters, but focalLength and sensor size are in mm.
// We don't convert them to meters because the multiplication factors cancel-out
float F = dofParameters.camera.camera.focalLength / 1000f;
float A = dofParameters.camera.camera.focalLength / dofParameters.physicalCameraAperture;
float P = dofParameters.focusDistance;
float maxFarCoC = sensorScale * (A * F) / Mathf.Max((P - F), 1e-6f);
// Scale and Bias factors for directly computing CoC size from post-rasterization depth with a single mad
float cocBias = maxFarCoC * (1f - P / dofParameters.camera.camera.farClipPlane);
float cocScale = maxFarCoC * P * (dofParameters.camera.camera.farClipPlane - dofParameters.camera.camera.nearClipPlane) / (dofParameters.camera.camera.farClipPlane * dofParameters.camera.camera.nearClipPlane);
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(cocLimit.x, cocLimit.y, cocScale, cocBias));
}
else
{
float nearEnd = dofParameters.nearFocusEnd;
float nearStart = Mathf.Min(dofParameters.nearFocusStart, nearEnd - 1e-5f);
float farStart = Mathf.Max(dofParameters.farFocusStart, nearEnd);
float farEnd = Mathf.Max(dofParameters.farFocusEnd, farStart + 1e-5f);
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(farStart, nearEnd, 1.0f / (farEnd - farStart), 1.0f / (nearStart - nearEnd)));
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params2, new Vector4(cocLimit.y, cocLimit.x, 0, 0));
}
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._CameraDepthTexture, depthBuffer);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, fullresCoC);
if (dofParameters.camera.msaaSamples != MSAASamples.None)
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._DepthMinMaxAvg, depthMinMaxAvgMSAA);
cmd.DispatchCompute(cs, kernel, (dofParameters.camera.actualWidth + 7) / 8, (dofParameters.camera.actualHeight + 7) / 8, dofParameters.camera.viewCount);
if (taaEnabled)
{
ReprojectCoCHistory(dofParameters, cmd, dofParameters.camera, prevCoCHistory, nextCoCHistory, motionVecTexture, ref fullresCoC);
}
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldPyramid)))
{
// DoF color pyramid
if (sourcePyramid != null)
{
cs = dofParameters.dofMipCS;
kernel = dofParameters.dofMipColorKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source, 0);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, sourcePyramid, 0);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip1, sourcePyramid, 1);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip2, sourcePyramid, 2);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip3, sourcePyramid, 3);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip4, sourcePyramid, 4);
int tx = ((dofParameters.camera.actualWidth >> 1) + 7) / 8;
int ty = ((dofParameters.camera.actualHeight >> 1) + 7) / 8;
cmd.DispatchCompute(cs, kernel, tx, ty, dofParameters.camera.viewCount);
}
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldDilate)))
{
int tileSize = dofParameters.minMaxCoCTileSize;
int tx = ((dofParameters.camera.actualWidth / tileSize) + 7) / 8;
int ty = ((dofParameters.camera.actualHeight / tileSize) + 7) / 8;
// Min Max CoC tiles
{
cs = dofParameters.pbDoFCoCMinMaxCS;
kernel = dofParameters.pbDoFMinMaxKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, fullresCoC, 0);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, minMaxCoCPing, 0);
cmd.DispatchCompute(cs, kernel, tx, ty, dofParameters.camera.viewCount);
}
// Min Max CoC tile dilation
{
cs = dofParameters.pbDoFDilateCS;
kernel = dofParameters.pbDoFDilateKernel;
int iterations = (int)Mathf.Max(Mathf.Ceil(cocLimit.y / dofParameters.minMaxCoCTileSize), 1.0f);
for (int pass = 0; pass < iterations; ++pass)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, minMaxCoCPing, 0);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, minMaxCoCPong, 0);
cmd.DispatchCompute(cs, kernel, tx, ty, dofParameters.camera.viewCount);
CoreUtils.Swap(ref minMaxCoCPing, ref minMaxCoCPong);
}
}
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldCombine)))
{
cs = dofParameters.pbDoFGatherCS;
kernel = dofParameters.pbDoFGatherKernel;
float sampleCount = Mathf.Max(dofParameters.nearSampleCount, dofParameters.farSampleCount);
float anamorphism = dofParameters.physicalCameraAnamorphism / 4f;
float mipLevel = 1 + Mathf.Ceil(Mathf.Log(maxCoc, 2));
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(sampleCount, maxCoc, anamorphism, 0.0f));
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params2, new Vector4(mipLevel, 0, 0, (float)dofParameters.resolution));
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, sourcePyramid != null ? sourcePyramid : source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, fullresCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileList, minMaxCoCPing, 0);
BlueNoise.BindDitheredTextureSet(cmd, dofParameters.ditheredTextureSet);
cmd.DispatchCompute(cs, kernel, (dofParameters.camera.actualWidth + 7) / 8, (dofParameters.camera.actualHeight + 7) / 8, dofParameters.camera.viewCount);
}
}
#endregion
#region Motion Blur
struct MotionBlurParameters
{
public ComputeShader motionVecPrepCS;
public ComputeShader tileGenCS;
public ComputeShader tileNeighbourhoodCS;
public ComputeShader tileMergeCS;
public ComputeShader motionBlurCS;
public int motionVecPrepKernel;
public int tileGenKernel;
public int tileNeighbourhoodKernel;
public int tileMergeKernel;
public int motionBlurKernel;
public HDCamera camera;
public Vector4 tileTargetSize;
public Vector4 motionBlurParams0;
public Vector4 motionBlurParams1;
public Vector4 motionBlurParams2;
public Vector4 motionBlurParams3;
public bool motionblurSupportScattering;
}
MotionBlurParameters PrepareMotionBlurParameters(HDCamera camera)
{
MotionBlurParameters parameters = new MotionBlurParameters();
parameters.camera = camera;
int tileSize = 32;
if (m_MotionBlurSupportsScattering)
{
tileSize = 16;
}
int tileTexWidth = Mathf.CeilToInt(camera.actualWidth / tileSize);
int tileTexHeight = Mathf.CeilToInt(camera.actualHeight / tileSize);
parameters.tileTargetSize = new Vector4(tileTexWidth, tileTexHeight, 1.0f / tileTexWidth, 1.0f / tileTexHeight);
float screenMagnitude = (new Vector2(camera.actualWidth, camera.actualHeight).magnitude);
parameters.motionBlurParams0 = new Vector4(
screenMagnitude,
screenMagnitude * screenMagnitude,
m_MotionBlur.minimumVelocity.value,
m_MotionBlur.minimumVelocity.value * m_MotionBlur.minimumVelocity.value
);
parameters.motionBlurParams1 = new Vector4(
m_MotionBlur.intensity.value,
m_MotionBlur.maximumVelocity.value / screenMagnitude,
0.25f, // min/max velocity ratio for high quality.
m_MotionBlur.cameraRotationVelocityClamp.value
);
uint sampleCount = (uint)m_MotionBlur.sampleCount;
parameters.motionBlurParams2 = new Vector4(
m_MotionBlurSupportsScattering ? (sampleCount + (sampleCount & 1)) : sampleCount,
tileSize,
m_MotionBlur.depthComparisonExtent.value,
m_MotionBlur.cameraMotionBlur.value ? 0.0f : 1.0f
);
parameters.motionVecPrepCS = m_Resources.shaders.motionBlurMotionVecPrepCS;
parameters.motionVecPrepKernel = parameters.motionVecPrepCS.FindKernel("MotionVecPreppingCS");
parameters.motionVecPrepCS.shaderKeywords = null;
if (!m_MotionBlur.cameraMotionBlur.value)
{
parameters.motionVecPrepCS.EnableKeyword("CAMERA_DISABLE_CAMERA");
}
else
{
var clampMode = m_MotionBlur.specialCameraClampMode.value;
if (clampMode == CameraClampMode.None)
parameters.motionVecPrepCS.EnableKeyword("NO_SPECIAL_CLAMP");
else if (clampMode == CameraClampMode.Rotation)
parameters.motionVecPrepCS.EnableKeyword("CAMERA_ROT_CLAMP");
else if (clampMode == CameraClampMode.Translation)
parameters.motionVecPrepCS.EnableKeyword("CAMERA_TRANS_CLAMP");
else if (clampMode == CameraClampMode.SeparateTranslationAndRotation)
parameters.motionVecPrepCS.EnableKeyword("CAMERA_SEPARATE_CLAMP");
else if (clampMode == CameraClampMode.FullCameraMotionVector)
parameters.motionVecPrepCS.EnableKeyword("CAMERA_FULL_CLAMP");
}
parameters.motionBlurParams3 = new Vector4(
m_MotionBlur.cameraTranslationVelocityClamp.value,
m_MotionBlur.cameraVelocityClamp.value,
0, 0);
parameters.tileGenCS = m_Resources.shaders.motionBlurGenTileCS;
parameters.tileGenCS.shaderKeywords = null;
if (m_MotionBlurSupportsScattering)
{
parameters.tileGenCS.EnableKeyword("SCATTERING");
}
parameters.tileGenKernel = parameters.tileGenCS.FindKernel("TileGenPass");
parameters.tileNeighbourhoodCS = m_Resources.shaders.motionBlurNeighborhoodTileCS;
parameters.tileNeighbourhoodCS.shaderKeywords = null;
if (m_MotionBlurSupportsScattering)
{
parameters.tileNeighbourhoodCS.EnableKeyword("SCATTERING");
}
parameters.tileNeighbourhoodKernel = parameters.tileNeighbourhoodCS.FindKernel("TileNeighbourhood");
parameters.tileMergeCS = m_Resources.shaders.motionBlurMergeTileCS;
parameters.tileMergeKernel = parameters.tileMergeCS.FindKernel("TileMerge");
parameters.motionBlurCS = m_Resources.shaders.motionBlurCS;
parameters.motionBlurCS.shaderKeywords = null;
CoreUtils.SetKeyword(parameters.motionBlurCS, "ENABLE_ALPHA", m_EnableAlpha);
parameters.motionBlurKernel = parameters.motionBlurCS.FindKernel("MotionBlurCS");
parameters.motionblurSupportScattering = m_MotionBlurSupportsScattering;
return parameters;
}
static void DoMotionBlur(in MotionBlurParameters motionBlurParams, CommandBuffer cmd, RTHandle source, RTHandle destination, RTHandle depthTexture, RTHandle motionVectorTexture,
RTHandle preppedMotionVec, RTHandle minMaxTileVel,
RTHandle maxTileNeigbourhood, RTHandle tileToScatterMax,
RTHandle tileToScatterMin)
{
int tileSize = 32;
if (motionBlurParams.motionblurSupportScattering)
{
tileSize = 16;
}
// -----------------------------------------------------------------------------
// Prep motion vectors
// - Pack normalized motion vectors and linear depth in R11G11B10
ComputeShader cs;
int kernel;
int threadGroupX;
int threadGroupY;
int groupSizeX = 8;
int groupSizeY = 8;
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.MotionBlurMotionVecPrep)))
{
cs = motionBlurParams.motionVecPrepCS;
kernel = motionBlurParams.motionVecPrepKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._MotionVecAndDepth, preppedMotionVec);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._CameraDepthTexture, depthTexture);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams, motionBlurParams.motionBlurParams0);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams1, motionBlurParams.motionBlurParams1);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams2, motionBlurParams.motionBlurParams2);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams3, motionBlurParams.motionBlurParams3);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._CameraMotionVectorsTexture, motionVectorTexture);
cmd.SetComputeMatrixParam(cs, HDShaderIDs._PrevVPMatrixNoTranslation, motionBlurParams.camera.mainViewConstants.prevViewProjMatrixNoCameraTrans);
cmd.SetComputeMatrixParam(cs, HDShaderIDs._CurrVPMatrixNoTranslation, motionBlurParams.camera.mainViewConstants.viewProjectionNoCameraTrans);
threadGroupX = (motionBlurParams.camera.actualWidth + (groupSizeX - 1)) / groupSizeX;
threadGroupY = (motionBlurParams.camera.actualHeight + (groupSizeY - 1)) / groupSizeY;
cmd.DispatchCompute(cs, kernel, threadGroupX, threadGroupY, motionBlurParams.camera.viewCount);
}
// -----------------------------------------------------------------------------
// Generate MinMax motion vectors tiles
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.MotionBlurTileMinMax)))
{
// We store R11G11B10 with RG = Max vel and B = Min vel magnitude
cs = motionBlurParams.tileGenCS;
kernel = motionBlurParams.tileGenKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileMinMaxMotionVec, minMaxTileVel);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._MotionVecAndDepth, preppedMotionVec);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams, motionBlurParams.motionBlurParams0);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams1, motionBlurParams.motionBlurParams1);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams2, motionBlurParams.motionBlurParams2);
if (motionBlurParams.motionblurSupportScattering)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileToScatterMax, tileToScatterMax);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileToScatterMin, tileToScatterMin);
}
threadGroupX = (motionBlurParams.camera.actualWidth + (tileSize - 1)) / tileSize;
threadGroupY = (motionBlurParams.camera.actualHeight + (tileSize - 1)) / tileSize;
cmd.DispatchCompute(cs, kernel, threadGroupX, threadGroupY, motionBlurParams.camera.viewCount);
}
// -----------------------------------------------------------------------------
// Generate max tiles neigbhourhood
using (new ProfilingScope(cmd, motionBlurParams.motionblurSupportScattering ? ProfilingSampler.Get(HDProfileId.MotionBlurTileScattering) : ProfilingSampler.Get(HDProfileId.MotionBlurTileNeighbourhood)))
{
cs = motionBlurParams.tileNeighbourhoodCS;
kernel = motionBlurParams.tileNeighbourhoodKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._TileTargetSize, motionBlurParams.tileTargetSize);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileMinMaxMotionVec, minMaxTileVel);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileMaxNeighbourhood, maxTileNeigbourhood);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams, motionBlurParams.motionBlurParams0);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams1, motionBlurParams.motionBlurParams1);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams2, motionBlurParams.motionBlurParams2);
if (motionBlurParams.motionblurSupportScattering)
{
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileToScatterMax, tileToScatterMax);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileToScatterMin, tileToScatterMin);
}
groupSizeX = 8;
groupSizeY = 8;
threadGroupX = ((int)motionBlurParams.tileTargetSize.x + (groupSizeX - 1)) / groupSizeX;
threadGroupY = ((int)motionBlurParams.tileTargetSize.y + (groupSizeY - 1)) / groupSizeY;
cmd.DispatchCompute(cs, kernel, threadGroupX, threadGroupY, motionBlurParams.camera.viewCount);
}
// -----------------------------------------------------------------------------
// Merge min/max info spreaded above.
if (motionBlurParams.motionblurSupportScattering)
{
cs = motionBlurParams.tileMergeCS;
kernel = motionBlurParams.tileMergeKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._TileTargetSize, motionBlurParams.tileTargetSize);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileToScatterMax, tileToScatterMax);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileToScatterMin, tileToScatterMin);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileMaxNeighbourhood, maxTileNeigbourhood);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams, motionBlurParams.motionBlurParams0);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams1, motionBlurParams.motionBlurParams1);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams2, motionBlurParams.motionBlurParams2);
cmd.DispatchCompute(cs, kernel, threadGroupX, threadGroupY, motionBlurParams.camera.viewCount);
}
// -----------------------------------------------------------------------------
// Blur kernel
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.MotionBlurKernel)))
{
cs = motionBlurParams.motionBlurCS;
kernel = motionBlurParams.motionBlurKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._TileTargetSize, motionBlurParams.tileTargetSize);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._MotionVecAndDepth, preppedMotionVec);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._TileMaxNeighbourhood, maxTileNeigbourhood);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams, motionBlurParams.motionBlurParams0);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams1, motionBlurParams.motionBlurParams1);
cmd.SetComputeVectorParam(cs, HDShaderIDs._MotionBlurParams2, motionBlurParams.motionBlurParams2);
groupSizeX = 16;
groupSizeY = 16;
threadGroupX = (motionBlurParams.camera.actualWidth + (groupSizeX - 1)) / groupSizeX;
threadGroupY = (motionBlurParams.camera.actualHeight + (groupSizeY - 1)) / groupSizeY;
cmd.DispatchCompute(cs, kernel, threadGroupX, threadGroupY, motionBlurParams.camera.viewCount);
}
}
#endregion
#region Panini Projection
struct PaniniProjectionParameters
{
public ComputeShader paniniProjectionCS;
public int paniniProjectionKernel;
public Vector4 paniniParams;
public int width;
public int height;
public int viewCount;
}
PaniniProjectionParameters PreparePaniniProjectionParameters(HDCamera camera)
{
PaniniProjectionParameters parameters = new PaniniProjectionParameters();
parameters.width = camera.actualWidth;
parameters.height = camera.actualHeight;
parameters.viewCount = camera.viewCount;
parameters.paniniProjectionCS = m_Resources.shaders.paniniProjectionCS;
parameters.paniniProjectionCS.shaderKeywords = null;
float distance = m_PaniniProjection.distance.value;
var viewExtents = CalcViewExtents(camera);
var cropExtents = CalcCropExtents(camera, distance);
float scaleX = cropExtents.x / viewExtents.x;
float scaleY = cropExtents.y / viewExtents.y;
float scaleF = Mathf.Min(scaleX, scaleY);
float paniniD = distance;
float paniniS = Mathf.Lerp(1.0f, Mathf.Clamp01(scaleF), m_PaniniProjection.cropToFit.value);
if (1f - Mathf.Abs(paniniD) > float.Epsilon)
{
parameters.paniniProjectionCS.EnableKeyword("GENERIC");
}
else
{
parameters.paniniProjectionCS.EnableKeyword("UNITDISTANCE");
}
if (m_EnableAlpha)
parameters.paniniProjectionCS.EnableKeyword("ENABLE_ALPHA");
parameters.paniniParams = new Vector4(viewExtents.x, viewExtents.y, paniniD, paniniS);
parameters.paniniProjectionKernel = parameters.paniniProjectionCS.FindKernel("KMain");
return parameters;
}
// Back-ported & adapted from the work of the Stockholm demo team - thanks Lasse!
static void DoPaniniProjection(in PaniniProjectionParameters parameters, CommandBuffer cmd, RTHandle source, RTHandle destination)
{
var cs = parameters.paniniProjectionCS;
int kernel = parameters.paniniProjectionKernel;
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, parameters.paniniParams);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.DispatchCompute(cs, kernel, (parameters.width + 7) / 8, (parameters.height + 7) / 8, parameters.viewCount);
}
Vector2 CalcViewExtents(HDCamera camera)
{
float fovY = camera.camera.fieldOfView * Mathf.Deg2Rad;
float aspect = (float)camera.actualWidth / (float)camera.actualHeight;
float viewExtY = Mathf.Tan(0.5f * fovY);
float viewExtX = aspect * viewExtY;
return new Vector2(viewExtX, viewExtY);
}
Vector2 CalcCropExtents(HDCamera camera, float d)
{
// given
// S----------- E--X-------
// | ` ~. /,´
// |-- --- Q
// | ,/ `
// 1 | ,´/ `
// | ,´ / ´
// | ,´ / ´
// |,` / ,
// O /
// | / ,
// d | /
// | / ,
// |/ .
// P
// | ´
// | , ´
// +- ´
//
// have X
// want to find E
float viewDist = 1f + d;
var projPos = CalcViewExtents(camera);
var projHyp = Mathf.Sqrt(projPos.x * projPos.x + 1f);
float cylDistMinusD = 1f / projHyp;
float cylDist = cylDistMinusD + d;
var cylPos = projPos * cylDistMinusD;
return cylPos * (viewDist / cylDist);
}
#endregion
#region Bloom
struct BloomParameters
{
public ComputeShader bloomPrefilterCS;
public ComputeShader bloomBlurCS;
public ComputeShader bloomUpsampleCS;
public int bloomPrefilterKernel;
public int bloomBlurKernel;
public int bloomDownsampleKernel;
public int bloomUpsampleKernel;
public int viewCount;
public int bloomMipCount;
public float bloomScatterParam;
public Vector4 thresholdParams;
public Vector4[] bloomMipInfo;
}
BloomParameters PrepareBloomParameters(HDCamera camera)
{
BloomParameters parameters = new BloomParameters();
parameters.viewCount = camera.viewCount;
parameters.bloomMipCount = m_BloomMipCount;
parameters.bloomMipInfo = m_BloomMipsInfo;
parameters.bloomPrefilterCS = m_Resources.shaders.bloomPrefilterCS;
parameters.bloomPrefilterKernel = parameters.bloomPrefilterCS.FindKernel("KMain");
parameters.bloomPrefilterCS.shaderKeywords = null;
if (m_Bloom.highQualityPrefiltering)
parameters.bloomPrefilterCS.EnableKeyword("HIGH_QUALITY");
else
parameters.bloomPrefilterCS.EnableKeyword("LOW_QUALITY");
if (m_EnableAlpha)
parameters.bloomPrefilterCS.EnableKeyword("ENABLE_ALPHA");
parameters.bloomBlurCS = m_Resources.shaders.bloomBlurCS;
parameters.bloomBlurKernel = parameters.bloomBlurCS.FindKernel("KMain");
parameters.bloomDownsampleKernel = parameters.bloomBlurCS.FindKernel("KDownsample");
parameters.bloomUpsampleCS = m_Resources.shaders.bloomUpsampleCS;
parameters.bloomUpsampleCS.shaderKeywords = null;
var highQualityFiltering = m_Bloom.highQualityFiltering;
// We switch to bilinear upsampling as it goes less wide than bicubic and due to our border/RTHandle handling, going wide on small resolution
// where small mips have a strong influence, might result problematic.
if (camera.actualWidth < 800 || camera.actualHeight < 450) highQualityFiltering = false;
if (highQualityFiltering)
parameters.bloomUpsampleCS.EnableKeyword("HIGH_QUALITY");
else
parameters.bloomUpsampleCS.EnableKeyword("LOW_QUALITY");
parameters.bloomUpsampleKernel = parameters.bloomUpsampleCS.FindKernel("KMain");
float scatter = Mathf.Lerp(0.05f, 0.95f, m_Bloom.scatter.value);
parameters.bloomScatterParam = scatter;
parameters.thresholdParams = GetBloomThresholdParams();
return parameters;
}
void ComputeBloomMipSizesAndScales(HDCamera camera)
{
var resolution = m_Bloom.resolution;
float scaleW = 1f / ((int)resolution / 2f);
float scaleH = 1f / ((int)resolution / 2f);
// If the scene is less than 50% of 900p, then we operate on full res, since it's going to be cheap anyway and this might improve quality in challenging situations.
if (camera.actualWidth < 800 || camera.actualHeight < 450)
{
scaleW = 1.0f;
scaleH = 1.0f;
}
if (m_Bloom.anamorphic.value)
{
// Positive anamorphic ratio values distort vertically - negative is horizontal
float anamorphism = m_PhysicalCamera.anamorphism * 0.5f;
scaleW *= anamorphism < 0 ? 1f + anamorphism : 1f;
scaleH *= anamorphism > 0 ? 1f - anamorphism : 1f;
}
// Determine the iteration count
int maxSize = Mathf.Max(camera.actualWidth, camera.actualHeight);
int iterations = Mathf.FloorToInt(Mathf.Log(maxSize, 2f) - 2 - (resolution == BloomResolution.Half ? 0 : 1));
m_BloomMipCount = Mathf.Clamp(iterations, 1, k_MaxBloomMipCount);
for (int i = 0; i < m_BloomMipCount; i++)
{
float p = 1f / Mathf.Pow(2f, i + 1f);
float sw = scaleW * p;
float sh = scaleH * p;
int pw, ph;
if (DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled())
{
pw = Mathf.Max(1, Mathf.CeilToInt(sw * camera.actualWidth));
ph = Mathf.Max(1, Mathf.CeilToInt(sh * camera.actualHeight));
}
else
{
pw = Mathf.Max(1, Mathf.RoundToInt(sw * camera.actualWidth));
ph = Mathf.Max(1, Mathf.RoundToInt(sh * camera.actualHeight));
}
var scale = new Vector2(sw, sh);
var pixelSize = new Vector2Int(pw, ph);
m_BloomMipsInfo[i] = new Vector4(pw, ph, sw, sh);
}
}
Vector4 GetBloomThresholdParams()
{
const float k_Softness = 0.5f;
float lthresh = Mathf.GammaToLinearSpace(m_Bloom.threshold.value);
float knee = lthresh * k_Softness + 1e-5f;
return new Vector4(lthresh, lthresh - knee, knee * 2f, 0.25f / knee);
}
void PrepareUberBloomParameters(ref UberPostParameters parameters, HDCamera camera)
{
float intensity = Mathf.Pow(2f, m_Bloom.intensity.value) - 1f; // Makes intensity easier to control
var tint = m_Bloom.tint.value.linear;
var luma = ColorUtils.Luminance(tint);
tint = luma > 0f ? tint * (1f / luma) : Color.white;
var dirtTexture = m_Bloom.dirtTexture.value == null ? Texture2D.blackTexture : m_Bloom.dirtTexture.value;
int dirtEnabled = m_Bloom.dirtTexture.value != null && m_Bloom.dirtIntensity.value > 0f ? 1 : 0;
float dirtRatio = (float)dirtTexture.width / (float)dirtTexture.height;
float screenRatio = (float)camera.actualWidth / (float)camera.actualHeight;
var dirtTileOffset = new Vector4(1f, 1f, 0f, 0f);
float dirtIntensity = m_Bloom.dirtIntensity.value * intensity;
if (dirtRatio > screenRatio)
{
dirtTileOffset.x = screenRatio / dirtRatio;
dirtTileOffset.z = (1f - dirtTileOffset.x) * 0.5f;
}
else if (screenRatio > dirtRatio)
{
dirtTileOffset.y = dirtRatio / screenRatio;
dirtTileOffset.w = (1f - dirtTileOffset.y) * 0.5f;
}
parameters.bloomDirtTexture = dirtTexture;
parameters.bloomParams = new Vector4(intensity, dirtIntensity, 1f, dirtEnabled);
parameters.bloomTint = (Vector4)tint;
parameters.bloomDirtTileOffset = dirtTileOffset;
parameters.bloomThreshold = GetBloomThresholdParams();
parameters.bloomBicubicParams = new Vector4(m_BloomMipsInfo[0].x, m_BloomMipsInfo[0].y, 1.0f / m_BloomMipsInfo[0].x, 1.0f / m_BloomMipsInfo[0].y);
// We undo the scale here, because bloom uses these parameters for its bicubic filtering offset.
// The bicubic filtering function is SampleTexture2DBicubic, and it requires the underlying texture's
// unscaled pixel sizes to compute the offsets of the samples.
// For more info please see the implementation of SampleTexture2DBicubic
parameters.bloomBicubicParams.x /= RTHandles.rtHandleProperties.rtHandleScale.x;
parameters.bloomBicubicParams.y /= RTHandles.rtHandleProperties.rtHandleScale.y;
parameters.bloomBicubicParams.z *= RTHandles.rtHandleProperties.rtHandleScale.x;
parameters.bloomBicubicParams.w *= RTHandles.rtHandleProperties.rtHandleScale.y;
}
static void DoBloom(in BloomParameters bloomParameters, CommandBuffer cmd, RTHandle source, RTHandle[] bloomMipsDown, RTHandle[] bloomMipsUp)
{
// All the computes for this effect use the same group size so let's use a local
// function to simplify dispatches
// Make sure the thread group count is sufficient to draw the guard bands
void DispatchWithGuardBands(ComputeShader shader, int kernelId, in Vector2Int size, in int viewCount)
{
int w = size.x;
int h = size.y;
if (w < source.rt.width && w % 8 < k_RTGuardBandSize)
w += k_RTGuardBandSize;
if (h < source.rt.height && h % 8 < k_RTGuardBandSize)
h += k_RTGuardBandSize;
cmd.DispatchCompute(shader, kernelId, (w + 7) / 8, (h + 7) / 8, viewCount);
}
// Pre-filtering
ComputeShader cs;
int kernel;
{
var size = new Vector2Int((int)bloomParameters.bloomMipInfo[0].x, (int)bloomParameters.bloomMipInfo[0].y);
cs = bloomParameters.bloomPrefilterCS;
kernel = bloomParameters.bloomPrefilterKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, bloomMipsUp[0]); // Use m_BloomMipsUp as temp target
cmd.SetComputeVectorParam(cs, HDShaderIDs._TexelSize, new Vector4(size.x, size.y, 1f / size.x, 1f / size.y));
cmd.SetComputeVectorParam(cs, HDShaderIDs._BloomThreshold, bloomParameters.thresholdParams);
DispatchWithGuardBands(cs, kernel, size, bloomParameters.viewCount);
cs = bloomParameters.bloomBlurCS;
kernel = bloomParameters.bloomBlurKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, bloomMipsUp[0]);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, bloomMipsDown[0]);
cmd.SetComputeVectorParam(cs, HDShaderIDs._TexelSize, new Vector4(size.x, size.y, 1f / size.x, 1f / size.y));
DispatchWithGuardBands(cs, kernel, size, bloomParameters.viewCount);
}
// Blur pyramid
kernel = bloomParameters.bloomDownsampleKernel;
for (int i = 0; i < bloomParameters.bloomMipCount - 1; i++)
{
var src = bloomMipsDown[i];
var dst = bloomMipsDown[i + 1];
var size = new Vector2Int((int)bloomParameters.bloomMipInfo[i + 1].x, (int)bloomParameters.bloomMipInfo[i + 1].y);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, src);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, dst);
cmd.SetComputeVectorParam(cs, HDShaderIDs._TexelSize, new Vector4(size.x, size.y, 1f / size.x, 1f / size.y));
DispatchWithGuardBands(cs, kernel, size, bloomParameters.viewCount);
}
// Upsample & combine
cs = bloomParameters.bloomUpsampleCS;
kernel = bloomParameters.bloomUpsampleKernel;
for (int i = bloomParameters.bloomMipCount - 2; i >= 0; i--)
{
var low = (i == bloomParameters.bloomMipCount - 2) ? bloomMipsDown : bloomMipsUp;
var srcLow = low[i + 1];
var srcHigh = bloomMipsDown[i];
var dst = bloomMipsUp[i];
var highSize = new Vector2Int((int)bloomParameters.bloomMipInfo[i].x, (int)bloomParameters.bloomMipInfo[i].y);
var lowSize = new Vector2Int((int)bloomParameters.bloomMipInfo[i + 1].x, (int)bloomParameters.bloomMipInfo[i + 1].y);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputLowTexture, srcLow);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputHighTexture, srcHigh);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, dst);
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(bloomParameters.bloomScatterParam, 0f, 0f, 0f));
cmd.SetComputeVectorParam(cs, HDShaderIDs._BloomBicubicParams, new Vector4(lowSize.x, lowSize.y, 1f / lowSize.x, 1f / lowSize.y));
cmd.SetComputeVectorParam(cs, HDShaderIDs._TexelSize, new Vector4(highSize.x, highSize.y, 1f / highSize.x, 1f / highSize.y));
DispatchWithGuardBands(cs, kernel, highSize, bloomParameters.viewCount);
}
}
#endregion
#region Lens Distortion
void PrepareLensDistortionParameters(ref UberPostParameters parameters, UberPostFeatureFlags flags)
{
if ((flags & UberPostFeatureFlags.LensDistortion) != UberPostFeatureFlags.LensDistortion)
return;
parameters.uberPostCS.EnableKeyword("LENS_DISTORTION");
float amount = 1.6f * Mathf.Max(Mathf.Abs(m_LensDistortion.intensity.value * 100f), 1f);
float theta = Mathf.Deg2Rad * Mathf.Min(160f, amount);
float sigma = 2f * Mathf.Tan(theta * 0.5f);
var center = m_LensDistortion.center.value * 2f - Vector2.one;
parameters.lensDistortionParams1 = new Vector4(
center.x,
center.y,
Mathf.Max(m_LensDistortion.xMultiplier.value, 1e-4f),
Mathf.Max(m_LensDistortion.yMultiplier.value, 1e-4f)
);
parameters.lensDistortionParams2 = new Vector4(
m_LensDistortion.intensity.value >= 0f ? theta : 1f / theta,
sigma,
1f / m_LensDistortion.scale.value,
m_LensDistortion.intensity.value * 100f
);
}
#endregion
#region Chromatic Aberration
void PrepareChromaticAberrationParameters(ref UberPostParameters parameters, UberPostFeatureFlags flags)
{
if ((flags & UberPostFeatureFlags.ChromaticAberration) != UberPostFeatureFlags.ChromaticAberration)
return;
parameters.uberPostCS.EnableKeyword("CHROMATIC_ABERRATION");
var spectralLut = m_ChromaticAberration.spectralLut.value;
// If no spectral lut is set, use a pre-generated one
if (spectralLut == null)
{
if (m_InternalSpectralLut == null)
{
m_InternalSpectralLut = new Texture2D(3, 1, TextureFormat.RGB24, false)
{
name = "Chromatic Aberration Spectral LUT",
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp,
anisoLevel = 0,
hideFlags = HideFlags.DontSave
};
m_InternalSpectralLut.SetPixels(new[]
{
new Color(1f, 0f, 0f),
new Color(0f, 1f, 0f),
new Color(0f, 0f, 1f)
});
m_InternalSpectralLut.Apply();
}
spectralLut = m_InternalSpectralLut;
}
parameters.spectralLut = spectralLut;
parameters.chromaticAberrationParameters = new Vector4(m_ChromaticAberration.intensity.value * 0.05f, m_ChromaticAberration.maxSamples, 0f, 0f);
}
#endregion
#region Vignette
void PrepareVignetteParameters(ref UberPostParameters parameters, UberPostFeatureFlags flags)
{
if ((flags & UberPostFeatureFlags.Vignette) != UberPostFeatureFlags.Vignette)
return;
parameters.uberPostCS.EnableKeyword("VIGNETTE");
if (m_Vignette.mode.value == VignetteMode.Procedural)
{
float roundness = (1f - m_Vignette.roundness.value) * 6f + m_Vignette.roundness.value;
parameters.vignetteParams1 = new Vector4(m_Vignette.center.value.x, m_Vignette.center.value.y, 0f, 0f);
parameters.vignetteParams2 = new Vector4(m_Vignette.intensity.value * 3f, m_Vignette.smoothness.value * 5f, roundness, m_Vignette.rounded.value ? 1f : 0f);
parameters.vignetteColor = m_Vignette.color.value;
parameters.vignetteMask = Texture2D.blackTexture;
}
else // Masked
{
var color = m_Vignette.color.value;
color.a = Mathf.Clamp01(m_Vignette.opacity.value);
parameters.vignetteParams1 = new Vector4(0f, 0f, 1f, 0f);
parameters.vignetteColor = color;
parameters.vignetteMask = m_Vignette.mask.value;
}
}
#endregion
#region Color Grading
struct ColorGradingParameters
{
public ComputeShader builderCS;
public int builderKernel;
public int lutSize;
public Vector4 colorFilter;
public Vector3 lmsColorBalance;
public Vector4 hueSatCon;
public Vector4 channelMixerR;
public Vector4 channelMixerG;
public Vector4 channelMixerB;
public Vector4 shadows;
public Vector4 midtones;
public Vector4 highlights;
public Vector4 shadowsHighlightsLimits;
public Vector4 lift;
public Vector4 gamma;
public Vector4 gain;
public Vector4 splitShadows;
public Vector4 splitHighlights;
public ColorCurves curves;
public HableCurve hableCurve;
public Vector4 miscParams;
public Texture externalLuT;
public float lutContribution;
public TonemappingMode tonemappingMode;
}
ColorGradingParameters PrepareColorGradingParameters()
{
var parameters = new ColorGradingParameters();
parameters.tonemappingMode = m_TonemappingFS ? m_Tonemapping.mode.value : TonemappingMode.None;
parameters.builderCS = m_Resources.shaders.lutBuilder3DCS;
parameters.builderKernel = parameters.builderCS.FindKernel("KBuild");
// Setup lut builder compute & grab the kernel we need
parameters.builderCS.shaderKeywords = null;
if (m_Tonemapping.IsActive() && m_TonemappingFS)
{
switch (parameters.tonemappingMode)
{
case TonemappingMode.Neutral: parameters.builderCS.EnableKeyword("TONEMAPPING_NEUTRAL"); break;
case TonemappingMode.ACES: parameters.builderCS.EnableKeyword("TONEMAPPING_ACES"); break;
case TonemappingMode.Custom: parameters.builderCS.EnableKeyword("TONEMAPPING_CUSTOM"); break;
case TonemappingMode.External: parameters.builderCS.EnableKeyword("TONEMAPPING_EXTERNAL"); break;
}
}
else
{
parameters.builderCS.EnableKeyword("TONEMAPPING_NONE");
}
parameters.lutSize = m_LutSize;
//parameters.colorFilter;
parameters.lmsColorBalance = GetColorBalanceCoeffs(m_WhiteBalance.temperature.value, m_WhiteBalance.tint.value);
parameters.hueSatCon = new Vector4(m_ColorAdjustments.hueShift.value / 360f, m_ColorAdjustments.saturation.value / 100f + 1f, m_ColorAdjustments.contrast.value / 100f + 1f, 0f);
parameters.channelMixerR = new Vector4(m_ChannelMixer.redOutRedIn.value / 100f, m_ChannelMixer.redOutGreenIn.value / 100f, m_ChannelMixer.redOutBlueIn.value / 100f, 0f);
parameters.channelMixerG = new Vector4(m_ChannelMixer.greenOutRedIn.value / 100f, m_ChannelMixer.greenOutGreenIn.value / 100f, m_ChannelMixer.greenOutBlueIn.value / 100f, 0f);
parameters.channelMixerB = new Vector4(m_ChannelMixer.blueOutRedIn.value / 100f, m_ChannelMixer.blueOutGreenIn.value / 100f, m_ChannelMixer.blueOutBlueIn.value / 100f, 0f);
ComputeShadowsMidtonesHighlights(out parameters.shadows, out parameters.midtones, out parameters.highlights, out parameters.shadowsHighlightsLimits);
ComputeLiftGammaGain(out parameters.lift, out parameters.gamma, out parameters.gain);
ComputeSplitToning(out parameters.splitShadows, out parameters.splitHighlights);
// Be careful, if m_Curves is modified between preparing the render pass and executing it, result will be wrong.
// However this should be fine for now as all updates should happen outisde rendering.
parameters.curves = m_Curves;
if (parameters.tonemappingMode == TonemappingMode.Custom)
{
parameters.hableCurve = m_HableCurve;
parameters.hableCurve.Init(
m_Tonemapping.toeStrength.value,
m_Tonemapping.toeLength.value,
m_Tonemapping.shoulderStrength.value,
m_Tonemapping.shoulderLength.value,
m_Tonemapping.shoulderAngle.value,
m_Tonemapping.gamma.value
);
}
else if (parameters.tonemappingMode == TonemappingMode.External)
{
parameters.externalLuT = m_Tonemapping.lutTexture.value;
parameters.lutContribution = m_Tonemapping.lutContribution.value;
}
parameters.colorFilter = m_ColorAdjustments.colorFilter.value.linear;
parameters.miscParams = new Vector4(m_ColorGradingFS ? 1f : 0f, 0f, 0f, 0f);
return parameters;
}
// TODO: User lut support
static void DoColorGrading(in ColorGradingParameters parameters,
RTHandle internalLogLuT,
CommandBuffer cmd)
{
var builderCS = parameters.builderCS;
var builderKernel = parameters.builderKernel;
// Fill-in constant buffers & textures
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._OutputTexture, internalLogLuT);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Size, new Vector4(parameters.lutSize, 1f / (parameters.lutSize - 1f), 0f, 0f));
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ColorBalance, parameters.lmsColorBalance);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ColorFilter, parameters.colorFilter);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ChannelMixerRed, parameters.channelMixerR);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ChannelMixerGreen, parameters.channelMixerG);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ChannelMixerBlue, parameters.channelMixerB);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._HueSatCon, parameters.hueSatCon);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Lift, parameters.lift);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Gamma, parameters.gamma);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Gain, parameters.gain);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Shadows, parameters.shadows);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Midtones, parameters.midtones);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Highlights, parameters.highlights);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ShaHiLimits, parameters.shadowsHighlightsLimits);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._SplitShadows, parameters.splitShadows);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._SplitHighlights, parameters.splitHighlights);
// YRGB
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveMaster, parameters.curves.master.value.GetTexture());
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveRed, parameters.curves.red.value.GetTexture());
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveGreen, parameters.curves.green.value.GetTexture());
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveBlue, parameters.curves.blue.value.GetTexture());
// Secondary curves
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveHueVsHue, parameters.curves.hueVsHue.value.GetTexture());
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveHueVsSat, parameters.curves.hueVsSat.value.GetTexture());
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveLumVsSat, parameters.curves.lumVsSat.value.GetTexture());
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._CurveSatVsSat, parameters.curves.satVsSat.value.GetTexture());
// Artist-driven tonemap curve
if (parameters.tonemappingMode == TonemappingMode.Custom)
{
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._CustomToneCurve, parameters.hableCurve.uniforms.curve);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ToeSegmentA, parameters.hableCurve.uniforms.toeSegmentA);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ToeSegmentB, parameters.hableCurve.uniforms.toeSegmentB);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._MidSegmentA, parameters.hableCurve.uniforms.midSegmentA);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._MidSegmentB, parameters.hableCurve.uniforms.midSegmentB);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ShoSegmentA, parameters.hableCurve.uniforms.shoSegmentA);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._ShoSegmentB, parameters.hableCurve.uniforms.shoSegmentB);
}
else if (parameters.tonemappingMode == TonemappingMode.External)
{
cmd.SetComputeTextureParam(builderCS, builderKernel, HDShaderIDs._LogLut3D, parameters.externalLuT);
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._LogLut3D_Params, new Vector4(1f / parameters.lutSize, parameters.lutSize - 1f, parameters.lutContribution, 0f));
}
// Misc parameters
cmd.SetComputeVectorParam(builderCS, HDShaderIDs._Params, parameters.miscParams);
// Generate the lut
// See the note about Metal & Intel in LutBuilder3D.compute
// GetKernelThreadGroupSizes is currently broken on some binary versions.
//builderCS.GetKernelThreadGroupSizes(builderKernel, out uint threadX, out uint threadY, out uint threadZ);
uint threadX = 4;
uint threadY = 4;
uint threadZ = 4;
cmd.DispatchCompute(builderCS, builderKernel,
(int)((parameters.lutSize + threadX - 1u) / threadX),
(int)((parameters.lutSize + threadY - 1u) / threadY),
(int)((parameters.lutSize + threadZ - 1u) / threadZ)
);
}
// Returns color balance coefficients in the LMS space
public static Vector3 GetColorBalanceCoeffs(float temperature, float tint)
{
// Range ~[-1.5;1.5] works best
float t1 = temperature / 65f;
float t2 = tint / 65f;
// Get the CIE xy chromaticity of the reference white point.
// Note: 0.31271 = x value on the D65 white point
float x = 0.31271f - t1 * (t1 < 0f ? 0.1f : 0.05f);
float y = ColorUtils.StandardIlluminantY(x) + t2 * 0.05f;
// Calculate the coefficients in the LMS space.
var w1 = new Vector3(0.949237f, 1.03542f, 1.08728f); // D65 white point
var w2 = ColorUtils.CIExyToLMS(x, y);
return new Vector3(w1.x / w2.x, w1.y / w2.y, w1.z / w2.z);
}
void ComputeShadowsMidtonesHighlights(out Vector4 shadows, out Vector4 midtones, out Vector4 highlights, out Vector4 limits)
{
float weight;
shadows = m_ShadowsMidtonesHighlights.shadows.value;
shadows.x = Mathf.GammaToLinearSpace(shadows.x);
shadows.y = Mathf.GammaToLinearSpace(shadows.y);
shadows.z = Mathf.GammaToLinearSpace(shadows.z);
weight = shadows.w * (Mathf.Sign(shadows.w) < 0f ? 1f : 4f);
shadows.x = Mathf.Max(shadows.x + weight, 0f);
shadows.y = Mathf.Max(shadows.y + weight, 0f);
shadows.z = Mathf.Max(shadows.z + weight, 0f);
shadows.w = 0f;
midtones = m_ShadowsMidtonesHighlights.midtones.value;
midtones.x = Mathf.GammaToLinearSpace(midtones.x);
midtones.y = Mathf.GammaToLinearSpace(midtones.y);
midtones.z = Mathf.GammaToLinearSpace(midtones.z);
weight = midtones.w * (Mathf.Sign(midtones.w) < 0f ? 1f : 4f);
midtones.x = Mathf.Max(midtones.x + weight, 0f);
midtones.y = Mathf.Max(midtones.y + weight, 0f);
midtones.z = Mathf.Max(midtones.z + weight, 0f);
midtones.w = 0f;
highlights = m_ShadowsMidtonesHighlights.highlights.value;
highlights.x = Mathf.GammaToLinearSpace(highlights.x);
highlights.y = Mathf.GammaToLinearSpace(highlights.y);
highlights.z = Mathf.GammaToLinearSpace(highlights.z);
weight = highlights.w * (Mathf.Sign(highlights.w) < 0f ? 1f : 4f);
highlights.x = Mathf.Max(highlights.x + weight, 0f);
highlights.y = Mathf.Max(highlights.y + weight, 0f);
highlights.z = Mathf.Max(highlights.z + weight, 0f);
highlights.w = 0f;
limits = new Vector4(
m_ShadowsMidtonesHighlights.shadowsStart.value,
m_ShadowsMidtonesHighlights.shadowsEnd.value,
m_ShadowsMidtonesHighlights.highlightsStart.value,
m_ShadowsMidtonesHighlights.highlightsEnd.value
);
}
void ComputeLiftGammaGain(out Vector4 lift, out Vector4 gamma, out Vector4 gain)
{
lift = m_LiftGammaGain.lift.value;
lift.x = Mathf.GammaToLinearSpace(lift.x) * 0.15f;
lift.y = Mathf.GammaToLinearSpace(lift.y) * 0.15f;
lift.z = Mathf.GammaToLinearSpace(lift.z) * 0.15f;
float lumLift = ColorUtils.Luminance(lift);
lift.x = lift.x - lumLift + lift.w;
lift.y = lift.y - lumLift + lift.w;
lift.z = lift.z - lumLift + lift.w;
lift.w = 0f;
gamma = m_LiftGammaGain.gamma.value;
gamma.x = Mathf.GammaToLinearSpace(gamma.x) * 0.8f;
gamma.y = Mathf.GammaToLinearSpace(gamma.y) * 0.8f;
gamma.z = Mathf.GammaToLinearSpace(gamma.z) * 0.8f;
float lumGamma = ColorUtils.Luminance(gamma);
gamma.w += 1f;
gamma.x = 1f / Mathf.Max(gamma.x - lumGamma + gamma.w, 1e-03f);
gamma.y = 1f / Mathf.Max(gamma.y - lumGamma + gamma.w, 1e-03f);
gamma.z = 1f / Mathf.Max(gamma.z - lumGamma + gamma.w, 1e-03f);
gamma.w = 0f;
gain = m_LiftGammaGain.gain.value;
gain.x = Mathf.GammaToLinearSpace(gain.x) * 0.8f;
gain.y = Mathf.GammaToLinearSpace(gain.y) * 0.8f;
gain.z = Mathf.GammaToLinearSpace(gain.z) * 0.8f;
float lumGain = ColorUtils.Luminance(gain);
gain.w += 1f;
gain.x = gain.x - lumGain + gain.w;
gain.y = gain.y - lumGain + gain.w;
gain.z = gain.z - lumGain + gain.w;
gain.w = 0f;
}
void ComputeSplitToning(out Vector4 shadows, out Vector4 highlights)
{
// As counter-intuitive as it is, to make split-toning work the same way it does in
// Adobe products we have to do all the maths in sRGB... So do not convert these to
// linear before sending them to the shader, this isn't a bug!
shadows = m_SplitToning.shadows.value;
highlights = m_SplitToning.highlights.value;
// Balance is stored in `shadows.w`
shadows.w = m_SplitToning.balance.value / 100f;
highlights.w = 0f;
}
#endregion
#region FXAA
struct FXAAParameters
{
public ComputeShader fxaaCS;
public int fxaaKernel;
public int width;
public int height;
public int viewCount;
}
FXAAParameters PrepareFXAAParameters(HDCamera camera)
{
FXAAParameters parameters = new FXAAParameters();
parameters.fxaaCS = m_Resources.shaders.FXAACS;
parameters.fxaaCS.shaderKeywords = null;
CoreUtils.SetKeyword(parameters.fxaaCS, "ENABLE_ALPHA", m_EnableAlpha);
parameters.fxaaKernel = parameters.fxaaCS.FindKernel("FXAA");
parameters.width = camera.actualWidth;
parameters.height = camera.actualHeight;
parameters.viewCount = camera.viewCount;
return parameters;
}
void DoFXAA(in FXAAParameters parameters, CommandBuffer cmd, RTHandle source, RTHandle destination)
{
var cs = parameters.fxaaCS;
int kernel = parameters.fxaaKernel;
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.DispatchCompute(cs, kernel, (parameters.width + 7) / 8, (parameters.height + 7) / 8, parameters.viewCount);
}
#endregion
#region SMAA
struct SMAAParameters
{
public Material smaaMaterial;
public Texture smaaAreaTex;
public Texture smaaSearchTex;
public Vector4 smaaRTMetrics;
}
SMAAParameters PrepareSMAAParameters(HDCamera camera)
{
SMAAParameters parameters = new SMAAParameters();
parameters.smaaMaterial = m_SMAAMaterial;
parameters.smaaAreaTex = m_Resources.textures.SMAAAreaTex;
parameters.smaaSearchTex = m_Resources.textures.SMAASearchTex;
parameters.smaaMaterial.shaderKeywords = null;
switch (camera.SMAAQuality)
{
case HDAdditionalCameraData.SMAAQualityLevel.Low:
parameters.smaaMaterial.EnableKeyword("SMAA_PRESET_LOW");
break;
case HDAdditionalCameraData.SMAAQualityLevel.Medium:
parameters.smaaMaterial.EnableKeyword("SMAA_PRESET_MEDIUM");
break;
case HDAdditionalCameraData.SMAAQualityLevel.High:
parameters.smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH");
break;
default:
parameters.smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH");
break;
}
parameters.smaaRTMetrics = new Vector4(1.0f / camera.actualWidth, 1.0f / camera.actualHeight, camera.actualWidth, camera.actualHeight);
return parameters;
}
static void DoSMAA(in SMAAParameters parameters, CommandBuffer cmd, RTHandle source, RTHandle smaaEdgeTex, RTHandle smaaBlendTex, RTHandle destination, RTHandle depthBuffer)
{
parameters.smaaMaterial.SetVector(HDShaderIDs._SMAARTMetrics, parameters.smaaRTMetrics);
parameters.smaaMaterial.SetTexture(HDShaderIDs._SMAAAreaTex, parameters.smaaAreaTex);
parameters.smaaMaterial.SetTexture(HDShaderIDs._SMAASearchTex, parameters.smaaSearchTex);
parameters.smaaMaterial.SetInt(HDShaderIDs._StencilRef, (int)StencilUsage.SMAA);
parameters.smaaMaterial.SetInt(HDShaderIDs._StencilMask, (int)StencilUsage.SMAA);
// -----------------------------------------------------------------------------
// Clear
CoreUtils.SetRenderTarget(cmd, smaaEdgeTex, ClearFlag.Color);
CoreUtils.SetRenderTarget(cmd, smaaBlendTex, ClearFlag.Color);
// -----------------------------------------------------------------------------
// EdgeDetection stage
cmd.SetGlobalTexture(HDShaderIDs._InputTexture, source);
HDUtils.DrawFullScreen(cmd, parameters.smaaMaterial, smaaEdgeTex, depthBuffer, null, (int)SMAAStage.EdgeDetection);
// -----------------------------------------------------------------------------
// BlendWeights stage
cmd.SetGlobalTexture(HDShaderIDs._InputTexture, smaaEdgeTex);
HDUtils.DrawFullScreen(cmd, parameters.smaaMaterial, smaaBlendTex, depthBuffer, null, (int)SMAAStage.BlendWeights);
// -----------------------------------------------------------------------------
// NeighborhoodBlending stage
cmd.SetGlobalTexture(HDShaderIDs._InputTexture, source);
parameters.smaaMaterial.SetTexture(HDShaderIDs._SMAABlendTex, smaaBlendTex);
HDUtils.DrawFullScreen(cmd, parameters.smaaMaterial, destination, null, (int)SMAAStage.NeighborhoodBlending);
}
#endregion
#region CAS
struct CASParameters
{
public ComputeShader casCS;
public int initKernel;
public int mainKernel;
public int viewCount;
public int inputWidth;
public int inputHeight;
public int outputWidth;
public int outputHeight;
}
CASParameters PrepareContrastAdaptiveSharpeningParameters(HDCamera camera)
{
CASParameters parameters = new CASParameters();
parameters.casCS = m_Resources.shaders.contrastAdaptiveSharpenCS;
parameters.initKernel = parameters.casCS.FindKernel("KInitialize");
parameters.mainKernel = parameters.casCS.FindKernel("KMain");
parameters.viewCount = camera.viewCount;
parameters.inputWidth = camera.actualWidth;
parameters.inputHeight = camera.actualHeight;
parameters.outputWidth = Mathf.RoundToInt(camera.finalViewport.width);
parameters.outputHeight = Mathf.RoundToInt(camera.finalViewport.height);
return parameters;
}
static void DoContrastAdaptiveSharpening(in CASParameters parameters, CommandBuffer cmd, RTHandle source, RTHandle destination, ComputeBuffer casParametersBuffer)
{
var cs = parameters.casCS;
int kInit = parameters.initKernel;
int kMain = parameters.mainKernel;
if (kInit >= 0 && kMain >= 0)
{
cmd.SetComputeFloatParam(cs, HDShaderIDs._Sharpness, 1);
cmd.SetComputeTextureParam(cs, kMain, HDShaderIDs._InputTexture, source);
cmd.SetComputeVectorParam(cs, HDShaderIDs._InputTextureDimensions, new Vector4(parameters.inputWidth, parameters.inputHeight));
cmd.SetComputeTextureParam(cs, kMain, HDShaderIDs._OutputTexture, destination);
cmd.SetComputeVectorParam(cs, HDShaderIDs._OutputTextureDimensions, new Vector4(parameters.outputWidth, parameters.outputHeight));
cmd.SetComputeBufferParam(cs, kInit, "CasParameters", casParametersBuffer);
cmd.SetComputeBufferParam(cs, kMain, "CasParameters", casParametersBuffer);
cmd.DispatchCompute(cs, kInit, 1, 1, 1);
int dispatchX = HDUtils.DivRoundUp(parameters.outputWidth, 16);
int dispatchY = HDUtils.DivRoundUp(parameters.outputHeight, 16);
cmd.DispatchCompute(cs, kMain, dispatchX, dispatchY, parameters.viewCount);
}
}
#endregion
#region Final Pass
struct FinalPassParameters
{
public bool postProcessEnabled;
public Material finalPassMaterial;
public HDCamera hdCamera;
public BlueNoise blueNoise;
public bool flipY;
public System.Random random;
public bool useFXAA;
public bool enableAlpha;
public bool keepAlpha;
public bool filmGrainEnabled;
public Texture filmGrainTexture;
public float filmGrainIntensity;
public float filmGrainResponse;
public bool ditheringEnabled;
}
FinalPassParameters PrepareFinalPass(HDCamera hdCamera, BlueNoise blueNoise, bool flipY)
{
FinalPassParameters parameters = new FinalPassParameters();
// General
parameters.postProcessEnabled = m_PostProcessEnabled;
parameters.finalPassMaterial = m_FinalPassMaterial;
parameters.hdCamera = hdCamera;
parameters.blueNoise = blueNoise;
parameters.flipY = flipY;
parameters.random = m_Random;
parameters.enableAlpha = m_EnableAlpha;
parameters.keepAlpha = m_KeepAlpha;
var dynResHandler = DynamicResolutionHandler.instance;
bool dynamicResIsOn = hdCamera.isMainGameView && dynResHandler.DynamicResolutionEnabled();
parameters.useFXAA = hdCamera.antialiasing == AntialiasingMode.FastApproximateAntialiasing && !dynamicResIsOn && m_AntialiasingFS;
// Film Grain
parameters.filmGrainEnabled = m_FilmGrain.IsActive() && m_FilmGrainFS;
if (m_FilmGrain.type.value != FilmGrainLookup.Custom)
parameters.filmGrainTexture = m_Resources.textures.filmGrainTex[(int)m_FilmGrain.type.value];
else
parameters.filmGrainTexture = m_FilmGrain.texture.value;
parameters.filmGrainIntensity = m_FilmGrain.intensity.value;
parameters.filmGrainResponse = m_FilmGrain.response.value;
// Dithering
parameters.ditheringEnabled = hdCamera.dithering && m_DitheringFS;
return parameters;
}
static void DoFinalPass(in FinalPassParameters parameters,
RTHandle source,
RTHandle afterPostProcessTexture,
RenderTargetIdentifier destination,
RTHandle alphaTexture,
CommandBuffer cmd)
{
// Final pass has to be done in a pixel shader as it will be the one writing straight
// to the backbuffer eventually
Material finalPassMaterial = parameters.finalPassMaterial;
HDCamera hdCamera = parameters.hdCamera;
bool flipY = parameters.flipY;
finalPassMaterial.shaderKeywords = null;
finalPassMaterial.SetTexture(HDShaderIDs._InputTexture, source);
var dynResHandler = DynamicResolutionHandler.instance;
bool dynamicResIsOn = hdCamera.isMainGameView && dynResHandler.DynamicResolutionEnabled();
if (dynamicResIsOn)
{
switch (dynResHandler.filter)
{
case DynamicResUpscaleFilter.Bilinear:
finalPassMaterial.EnableKeyword("BILINEAR");
break;
case DynamicResUpscaleFilter.CatmullRom:
finalPassMaterial.EnableKeyword("CATMULL_ROM_4");
break;
case DynamicResUpscaleFilter.Lanczos:
finalPassMaterial.EnableKeyword("LANCZOS");
break;
case DynamicResUpscaleFilter.ContrastAdaptiveSharpen:
finalPassMaterial.EnableKeyword("CONTRASTADAPTIVESHARPEN");
break;
}
}
if (parameters.postProcessEnabled)
{
if (parameters.useFXAA)
finalPassMaterial.EnableKeyword("FXAA");
if (parameters.filmGrainEnabled)
{
if (parameters.filmGrainTexture != null) // Fail safe if the resources asset breaks :/
{
#if HDRP_DEBUG_STATIC_POSTFX
float offsetX = 0;
float offsetY = 0;
#else
float offsetX = (float)(parameters.random.NextDouble());
float offsetY = (float)(parameters.random.NextDouble());
#endif
finalPassMaterial.EnableKeyword("GRAIN");
finalPassMaterial.SetTexture(HDShaderIDs._GrainTexture, parameters.filmGrainTexture);
finalPassMaterial.SetVector(HDShaderIDs._GrainParams, new Vector2(parameters.filmGrainIntensity * 4f, parameters.filmGrainResponse));
float uvScaleX = parameters.hdCamera.actualWidth / (float)parameters.filmGrainTexture.width;
float uvScaleY = parameters.hdCamera.actualHeight / (float)parameters.filmGrainTexture.height;
float scaledOffsetX = offsetX * uvScaleX;
float scaledOffsetY = offsetY * uvScaleY;
finalPassMaterial.SetVector(HDShaderIDs._GrainTextureParams, new Vector4(uvScaleX, uvScaleY, offsetX, offsetY));
}
}
if (parameters.ditheringEnabled)
{
var blueNoiseTexture = parameters.blueNoise.textureArray16L;
#if HDRP_DEBUG_STATIC_POSTFX
int textureId = 0;
#else
int textureId = (int)hdCamera.GetCameraFrameCount() % blueNoiseTexture.depth;
#endif
finalPassMaterial.EnableKeyword("DITHER");
finalPassMaterial.SetTexture(HDShaderIDs._BlueNoiseTexture, blueNoiseTexture);
finalPassMaterial.SetVector(HDShaderIDs._DitherParams, new Vector3(parameters.hdCamera.actualWidth / blueNoiseTexture.width,
parameters.hdCamera.actualHeight / blueNoiseTexture.height, textureId));
}
}
finalPassMaterial.SetTexture(HDShaderIDs._AlphaTexture, alphaTexture);
finalPassMaterial.SetFloat(HDShaderIDs._KeepAlpha, parameters.keepAlpha ? 1.0f : 0.0f);
if (parameters.enableAlpha)
finalPassMaterial.EnableKeyword("ENABLE_ALPHA");
else
finalPassMaterial.DisableKeyword("ENABLE_ALPHA");
finalPassMaterial.SetVector(HDShaderIDs._UVTransform,
flipY
? new Vector4(1.0f, -1.0f, 0.0f, 1.0f)
: new Vector4(1.0f, 1.0f, 0.0f, 0.0f)
);
finalPassMaterial.SetVector(HDShaderIDs._ViewPortSize,
new Vector4(hdCamera.finalViewport.width, hdCamera.finalViewport.height, 1.0f / hdCamera.finalViewport.width, 1.0f / hdCamera.finalViewport.height));
// Blit to backbuffer
Rect backBufferRect = hdCamera.finalViewport;
// When post process is not the final pass, we render at (0,0) so that subsequent rendering does not have to bother about viewports.
// Final viewport is handled in the final blit in this case
if (!HDUtils.PostProcessIsFinalPass(hdCamera))
{
backBufferRect.x = backBufferRect.y = 0;
}
if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.AfterPostprocess))
{
finalPassMaterial.EnableKeyword("APPLY_AFTER_POST");
finalPassMaterial.SetTexture(HDShaderIDs._AfterPostProcessTexture, afterPostProcessTexture);
}
else
{
finalPassMaterial.SetTexture(HDShaderIDs._AfterPostProcessTexture, TextureXR.GetBlackTexture());
}
HDUtils.DrawFullScreen(cmd, backBufferRect, finalPassMaterial, destination);
}
#endregion
}
}