Skip to content

Hdrp Improved Depth Of Field #965

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2215,6 +2215,105 @@ RTHandle Allocator(string id, int frameIndex, RTHandleSystem rtHandleSystem)

#endregion

#region Depth Of Field (Physically based)
void DoPhysicallyBasedDepthOfField(CommandBuffer cmd, HDCamera camera, RTHandle source, RTHandle destination, bool taaEnabled)
{
float scale = 1f / (float)m_DepthOfField.resolution;
int targetWidth = Mathf.RoundToInt(camera.actualWidth * scale);
int targetHeight = Mathf.RoundToInt(camera.actualHeight * scale);

var fullresCoC = m_Pool.Get(Vector2.one, k_CoCFormat, true);

// Map the old "max radius" parameters to a bigger range, so we can work on more challenging scenes
float maxRadius = Mathf.Max(m_DepthOfField.farMaxBlur, m_DepthOfField.nearMaxBlur);
float cocLimit = Mathf.Clamp(2 * maxRadius, 1, 32);

ComputeShader cs;
int kernel;

using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldCoC)))
{
cs = m_Resources.shaders.dofCircleOfConfusion;
cs.shaderKeywords = null;

if(m_DepthOfField.focusMode == DepthOfFieldMode.UsePhysicalCamera)
{
kernel = cs.FindKernel("KMainCoCPhysical");

// The sensor scale is used to convert the CoC size from mm to screen pixels
float sensorScale;
if( camera.camera.gateFit == Camera.GateFitMode.Horizontal )
sensorScale = (0.5f / camera.camera.sensorSize.x) * camera.camera.pixelWidth;
else
sensorScale = (0.5f / camera.camera.sensorSize.y) * 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 = camera.camera.focalLength / 1000f;
float A = camera.camera.focalLength / m_PhysicalCamera.aperture;
float P = m_DepthOfField.focusDistance.value;
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 / camera.camera.farClipPlane);
float cocScale = maxFarCoC * P * (camera.camera.farClipPlane - camera.camera.nearClipPlane) / (camera.camera.farClipPlane * camera.camera.nearClipPlane);
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(cocLimit, 0.0f, cocScale, cocBias));
}
else
{
kernel = cs.FindKernel("KMainCoCManual");

float nearEnd = m_DepthOfField.nearFocusEnd.value;
float nearStart = Mathf.Min(m_DepthOfField.nearFocusStart.value, nearEnd - 1e-5f);
float farStart = Mathf.Max(m_DepthOfField.farFocusStart.value, nearEnd);
float farEnd = Mathf.Max(m_DepthOfField.farFocusEnd.value, farStart + 1e-5f);
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(nearStart, nearEnd, farStart, farEnd));
}

cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, fullresCoC);
cmd.DispatchCompute(cs, kernel, (camera.actualWidth + 7) / 8, (camera.actualHeight + 7) / 8, camera.viewCount);
}

using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldPyramid)))
{
// To have an adaptive gather radius, we need estimates for the the min and max CoC that intersect a pixel.
cs = m_Resources.shaders.DoFCoCPyramidCS;
cs.shaderKeywords = null;

kernel = cs.FindKernel("KMainCoCPyramid");

cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, fullresCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip1, fullresCoC, 1);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip2, fullresCoC, 2);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip3, fullresCoC, 3);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip4, fullresCoC, 4);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip5, fullresCoC, 5);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputMip6, fullresCoC, 6);
cmd.DispatchCompute(cs, kernel, (camera.actualWidth + 31) / 32, (camera.actualHeight + 31) / 32, camera.viewCount);
}

using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.DepthOfFieldCombine)))
{
cs = m_Resources.shaders.dofGatherCS;
cs.shaderKeywords = null;
if (m_EnableAlpha)
cs.EnableKeyword("ENABLE_ALPHA");

kernel = cs.FindKernel("KMain");
float sampleCount = Mathf.Max(m_DepthOfField.nearSampleCount, m_DepthOfField.farSampleCount);
float mipLevel = Mathf.Ceil(Mathf.Log(cocLimit, 2));
cmd.SetComputeVectorParam(cs, HDShaderIDs._Params, new Vector4(sampleCount, cocLimit, mipLevel, 0.0f));
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputTexture, source);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._InputCoCTexture, fullresCoC);
cmd.SetComputeTextureParam(cs, kernel, HDShaderIDs._OutputTexture, destination);
cmd.DispatchCompute(cs, kernel, (camera.actualWidth + 7) / 8, (camera.actualHeight + 7) / 8, camera.viewCount);
}

m_Pool.Recycle(fullresCoC);
}
#endregion

#region Motion Blur

struct MotionBlurParameters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/PostProcessDefines.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/DepthOfFieldCommon.hlsl"

#pragma only_renderers d3d11 playstation xboxone vulkan metal switch

#pragma kernel KMainCoCPhysical
#pragma kernel KMainCoCManual

CBUFFER_START(cb0)
float4 _Params;
CBUFFER_END

// outpute texture
RW_TEXTURE2D_X(float, _OutputTexture);

#define GROUP_RES 8u
#define GROUP_SIZE (GROUP_RES * GROUP_RES)

[numthreads(GROUP_RES, GROUP_RES, 1)]
void KMainCoCPhysical(uint3 dispatchThreadId : SV_DispatchThreadID)
{
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
PositionInputs posInputs = GetPositionInput(float2(dispatchThreadId.xy), _ScreenSize.zw, uint2(GROUP_RES, GROUP_RES));

float depth = LoadCameraDepth(posInputs.positionSS);

// Note: the linearization of the depth is encoded directly in the MAD parameters
float CoC = _Params.w - _Params.z * depth;

const float maxCoC = _Params.x;
if (CoC > 0)
{
// CoC clamping for the far field
CoC = min(CoC, maxCoC);
}
else
{
// CoC clamping for the near field
CoC = max(CoC, -maxCoC);
}

_OutputTexture[COORD_TEXTURE2D_X(posInputs.positionSS)] = CoC;
}

// Manual CoC using near & far planes
[numthreads(GROUP_RES, GROUP_RES, 1)]
void KMainCoCManual(uint3 dispatchThreadId : SV_DispatchThreadID)
{
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);

//TODO

_OutputTexture[COORD_TEXTURE2D_X(dispatchThreadId.xy)] = 0.0f;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"

#pragma only_renderers d3d11 playstation xboxone vulkan metal switch

#pragma kernel KMainCoCPyramid

RW_TEXTURE2D_X(float, _InputTexture);

RW_TEXTURE2D_X(float, _OutputMip1);
RW_TEXTURE2D_X(float, _OutputMip2);
RW_TEXTURE2D_X(float, _OutputMip3);
RW_TEXTURE2D_X(float, _OutputMip4);
RW_TEXTURE2D_X(float, _OutputMip5);
RW_TEXTURE2D_X(float, _OutputMip6);

#define GROUP_SIZE 32

groupshared float gs_cache[GROUP_SIZE * GROUP_SIZE];

void StorePixel(uint index, float color)
{
gs_cache[index] = color;
}

float LoadPixel(uint index)
{
return gs_cache[index];
}

float MaxOp(float a, float b)
{
return abs(a) > abs(b) ? a : b;
}

// Generate six mips in one pass
[numthreads(GROUP_SIZE, GROUP_SIZE, 1)]
void KMainCoCPyramid(uint3 dispatchThreadId : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex)
{
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);

uint2 ul = dispatchThreadId.xy << 1u;

// First mip
float color = _InputTexture[COORD_TEXTURE2D_X(ul)];
color = MaxOp(color, _InputTexture[COORD_TEXTURE2D_X(ul + uint2(1u, 0u))]);
color = MaxOp(color, _InputTexture[COORD_TEXTURE2D_X(ul + uint2(0u, 1u))]);
color = MaxOp(color, _InputTexture[COORD_TEXTURE2D_X(ul + uint2(1u, 1u))]);

// TODO: also compute and pack the min value
StorePixel(groupIndex, color);

_OutputMip1[COORD_TEXTURE2D_X(dispatchThreadId.xy)] = color;

GroupMemoryBarrierWithGroupSync();

// Second mip - checks that X and Y are even
if ((groupIndex & 0x21) == 0)
{
color = MaxOp(color, LoadPixel(groupIndex + 1u));
color = MaxOp(color, LoadPixel(groupIndex + GROUP_SIZE));
color = MaxOp(color, LoadPixel(groupIndex + GROUP_SIZE + 1u));

StorePixel(groupIndex, color);

_OutputMip2[COORD_TEXTURE2D_X(dispatchThreadId.xy / 2u)] = color;
}

GroupMemoryBarrierWithGroupSync();

// Third mip - checks that X and Y are multiples of four
if ((groupIndex & 0x63) == 0)
{
color = MaxOp(color, LoadPixel(groupIndex + 2u));
color = MaxOp(color, LoadPixel(groupIndex + 2u * GROUP_SIZE));
color = MaxOp(color, LoadPixel(groupIndex + 2u * GROUP_SIZE + 2u));
StorePixel(groupIndex, color);

_OutputMip3[COORD_TEXTURE2D_X(dispatchThreadId.xy / 4u)] = color;
}

GroupMemoryBarrierWithGroupSync();

// Fourth mip - checks that X and Y are multiples of 8
if ((groupIndex & 0xE7) == 0)
{
color = MaxOp(color, LoadPixel(groupIndex + 4u));
color = MaxOp(color, LoadPixel(groupIndex + 4u * GROUP_SIZE));
color = MaxOp(color, LoadPixel(groupIndex + 4u * GROUP_SIZE + 4u));
StorePixel(groupIndex, color);

_OutputMip4[COORD_TEXTURE2D_X(dispatchThreadId.xy / 8u)] = color;
}

GroupMemoryBarrierWithGroupSync();

// Fifth mip - checks that X and Y are multiples of 16
if ((groupIndex & 0x1EF) == 0)
{
color = MaxOp(color, LoadPixel(groupIndex + 8u));
color = MaxOp(color, LoadPixel(groupIndex + 8u * GROUP_SIZE));
color = MaxOp(color, LoadPixel(groupIndex + 8u * GROUP_SIZE + 8u));

_OutputMip5[COORD_TEXTURE2D_X(dispatchThreadId.xy / 16u)] = color;
}

// Last mip - only one thread
if (groupIndex == 0)
{
color = MaxOp(color, LoadPixel(groupIndex + 16u));
color = MaxOp(color, LoadPixel(groupIndex + 16u * GROUP_SIZE));
color = MaxOp(color, LoadPixel(groupIndex + 16u * GROUP_SIZE + 16u));

_OutputMip6[COORD_TEXTURE2D_X(dispatchThreadId.xy / 32u)] = color;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading