Skip to content

Commit

Permalink
Added static tile version.
Browse files Browse the repository at this point in the history
  • Loading branch information
kaychang-unity committed Apr 12, 2024
1 parent 418ad53 commit 69f1e7c
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ enum Pass
BokehLargeKernel,
BokehVeryLargeKernel,
BokehUnified,
BokehKernel1,
BokehKernel2,
BokehKernel3,
BokehKernel4,
PostFilter,
Combine,
DebugOverlay
Expand Down Expand Up @@ -178,7 +182,8 @@ RenderTexture CheckHistory(int eye, int id, PostProcessRenderContext context, Re

public override void Render(PostProcessRenderContext context)
{
bool useUnified = (Time.time % 2f) < 1f; // (kc)
bool useUnified = true;// (Time.time % 2f) < 1f; // (kc)
bool useStaticTiles = false;

// The coc is stored in alpha so we need a 4 channels target. Note that using ARGB32
// will result in a very weak near-blur.
Expand All @@ -194,6 +199,11 @@ public override void Render(PostProcessRenderContext context)
int maxCoCMipLevel;
var maxCoC = CalculateMaxCoCRadius(context.screenHeight, out maxCoCMipLevel);

// pad full-resolution screen so that the number of mips required by maxCoCMipLevel does not cause the downsampling chain to skip row or colums of pixels.
int tileSize = 1 << maxCoCMipLevel;
int paddedWidth = ((context.width + tileSize - 1) >> maxCoCMipLevel) << maxCoCMipLevel;
int paddedHeight = ((context.height + tileSize - 1) >> maxCoCMipLevel) << maxCoCMipLevel;

Vector4 cocKernelLimitsA;
Vector4 cocKernelLimitsB;
CalculateCoCKernelLimits(context.screenHeight, out cocKernelLimitsA, out cocKernelLimitsB);
Expand All @@ -206,7 +216,13 @@ public override void Render(PostProcessRenderContext context)
sheet.properties.SetFloat(ShaderIDs.LensCoeff, coeff);
sheet.properties.SetVector(ShaderIDs.CoCKernelLimitsA, cocKernelLimitsA);
sheet.properties.SetVector(ShaderIDs.CoCKernelLimitsB, cocKernelLimitsB);
sheet.properties.SetVector(ShaderIDs.MaxCoCTexUvScale, new Vector4(paddedWidth / (float)context.width, paddedHeight / (float)context.height, context.width / (float)paddedWidth, context.height / (float)paddedHeight));
sheet.properties.SetFloat(ShaderIDs.MaxCoC, maxCoC);
sheet.properties.SetVector(ShaderIDs.CoCScreen, new Vector4(context.width, context.height, 1f / context.width, 1f / context.height));
sheet.properties.SetFloat(ShaderIDs.CoCTileXCount, paddedWidth >> maxCoCMipLevel);
sheet.properties.SetFloat(ShaderIDs.CoCTileYCount, paddedHeight >> maxCoCMipLevel);
sheet.properties.SetFloat(ShaderIDs.CoCTilePixelWidth, 1 << maxCoCMipLevel);
sheet.properties.SetFloat(ShaderIDs.CoCTilePixelHeight, 1 << maxCoCMipLevel);
sheet.properties.SetFloat(ShaderIDs.RcpMaxCoC, 1f / maxCoC);
sheet.properties.SetFloat(ShaderIDs.RcpAspect, 1f / aspect);

Expand Down Expand Up @@ -236,20 +252,20 @@ public override void Render(PostProcessRenderContext context)
cmd.SetGlobalTexture(ShaderIDs.CoCTex, historyWrite);
}

if (useUnified)
if (useUnified || useStaticTiles)
{
// Downsampling CoC
context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.MaxCoCMips[1], 0, cocFormat, RenderTextureReadWrite.Linear, FilterMode.Point, context.width >> 1, context.height >> 1);
context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.MaxCoCMips[1], 0, cocFormat, RenderTextureReadWrite.Linear, FilterMode.Point, paddedWidth >> 1, paddedHeight >> 1);
cmd.BlitFullscreenTriangle(ShaderIDs.CoCTex, ShaderIDs.MaxCoCMips[1], sheet, (int)Pass.downsampleInitialMaxCoC);

for (int i = 2; i <= maxCoCMipLevel; ++i)
{
context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.MaxCoCMips[i], 0, cocFormat, RenderTextureReadWrite.Linear, FilterMode.Point, context.width >> i, context.height >> i);
context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.MaxCoCMips[i], 0, cocFormat, RenderTextureReadWrite.Linear, FilterMode.Point, paddedWidth >> i, paddedHeight >> i);
cmd.BlitFullscreenTriangle(ShaderIDs.MaxCoCMips[i - 1], ShaderIDs.MaxCoCMips[i], sheet, (int)Pass.downsampleMaxCoC);
}

// Extend CoC
context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.MaxCoCTex, 0, cocFormat, RenderTextureReadWrite.Linear, FilterMode.Point, context.width >> maxCoCMipLevel, context.height >> maxCoCMipLevel);
context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.MaxCoCTex, 0, cocFormat, RenderTextureReadWrite.Linear, FilterMode.Point, paddedWidth >> maxCoCMipLevel, paddedHeight >> maxCoCMipLevel);
cmd.BlitFullscreenTriangle(ShaderIDs.MaxCoCMips[maxCoCMipLevel], ShaderIDs.MaxCoCTex, sheet, (int)Pass.extendMaxCoC);
}

Expand All @@ -259,7 +275,32 @@ public override void Render(PostProcessRenderContext context)

// Bokeh simulation pass
context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.DepthOfFieldTemp, 0, colorFormat, RenderTextureReadWrite.Default, FilterMode.Bilinear, context.width / 2, context.height / 2);
cmd.BlitFullscreenTriangle(ShaderIDs.DepthOfFieldTex, ShaderIDs.DepthOfFieldTemp, sheet, useUnified ? (int)Pass.BokehUnified : (int)Pass.BokehSmallKernel + (int)settings.kernelSize.value);
if (useUnified)
{
/*
int tileXCount = paddedWidth >> maxCoCMipLevel;
int tileYCount = paddedHeight >> maxCoCMipLevel;
int tileCount = tileXCount * tileYCount;
cmd.SetGlobalFloat(ShaderIDs.CoCRingCount, 2.0f);
cmd.BlitProcedural(ShaderIDs.DepthOfFieldTex, ShaderIDs.DepthOfFieldTemp, sheet, (int)Pass.BokehUnified, 6, tileCount);
*/
cmd.BlitFullscreenTriangle(ShaderIDs.DepthOfFieldTex, ShaderIDs.DepthOfFieldTemp, sheet, (int)Pass.BokehUnified);
}
else if (useStaticTiles)
{
int tileXCount = paddedWidth >> maxCoCMipLevel;
int tileYCount = paddedHeight >> maxCoCMipLevel;
int tileCount = tileXCount * tileYCount;
for (int i = 0; i < 4; ++i)
{
cmd.SetGlobalFloat(ShaderIDs.CoCRingCount, i + 1);
cmd.BlitProcedural(ShaderIDs.DepthOfFieldTex, ShaderIDs.DepthOfFieldTemp, sheet, (int)Pass.BokehKernel1 + i, 6, tileCount);
}
}
else
{
cmd.BlitFullscreenTriangle(ShaderIDs.DepthOfFieldTex, ShaderIDs.DepthOfFieldTemp, sheet, (int)Pass.BokehSmallKernel + (int)settings.kernelSize.value);
}

// Postfilter pass
cmd.BlitFullscreenTriangle(ShaderIDs.DepthOfFieldTemp, ShaderIDs.DepthOfFieldTex, sheet, (int)Pass.PostFilter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,34 @@ public static void BlitFullscreenTriangle(this CommandBuffer cmd, RenderTargetId
#endif
}

/// <summary>
/// Blits procedural geometry using a given material.
/// </summary>
/// <param name="cmd">The command buffer to use</param>
/// <param name="source">The source render target</param>
/// <param name="destination">The destination render target</param>
/// <param name="propertySheet">The property sheet to use</param>
/// <param name="pass">The pass from the material to use</param>
/// <param name="instanceCount">The number of instances to render</param>
/// <param name="clear">Should the destination target be cleared?</param>
/// <param name="viewport">An optional viewport to consider for the blit</param>
/// <param name="preserveDepth">Should the depth buffer be preserved?</param>
public static void BlitProcedural(this CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, PropertySheet propertySheet, int pass, int vertexCount, int instanceCount, bool clear = false, Rect? viewport = null, bool preserveDepth = false)
{
cmd.SetGlobalTexture(ShaderIDs.MainTex, source);
var loadAction = viewport == null ? LoadAction.DontCare : LoadAction.Load;
cmd.SetRenderTargetWithLoadStoreAction(destination, loadAction, StoreAction.Store, preserveDepth ? LoadAction.Load : loadAction, StoreAction.Store);

if (viewport != null)
cmd.SetViewport(viewport.Value);

if (clear)
cmd.ClearRenderTarget(true, true, Color.clear);

// TODO: detect which platforms support quads
cmd.DrawProcedural(Matrix4x4.identity, propertySheet.material, pass, MeshTopology.Triangles, vertexCount, instanceCount, propertySheet.properties);
}

/// <summary>
/// Blits a fullscreen triangle from a double-wide source.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ static class ShaderIDs
internal static readonly int LensCoeff = Shader.PropertyToID("_LensCoeff");
internal static readonly int CoCKernelLimitsA = Shader.PropertyToID("_CoCKernelLimitsA");
internal static readonly int CoCKernelLimitsB = Shader.PropertyToID("_CoCKernelLimitsB");
internal static readonly int MaxCoCTexUvScale = Shader.PropertyToID("_MaxCoCTexUvScale");
internal static readonly int CoCRingCount = Shader.PropertyToID("_CoCRingCount");
internal static readonly int CoCScreen = Shader.PropertyToID("_CoCScreen");
internal static readonly int CoCTileXCount = Shader.PropertyToID("_CoCTileXCount");
internal static readonly int CoCTileYCount = Shader.PropertyToID("_CoCTileYCount");
internal static readonly int CoCTilePixelWidth = Shader.PropertyToID("_CoCTilePixelWidth");
internal static readonly int CoCTilePixelHeight = Shader.PropertyToID("_CoCTilePixelHeight");
internal static readonly int MaxCoC = Shader.PropertyToID("_MaxCoC");
internal static readonly int RcpMaxCoC = Shader.PropertyToID("_RcpMaxCoC");
internal static readonly int RcpAspect = Shader.PropertyToID("_RcpAspect");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ float _Distance;
float _LensCoeff; // f^2 / (N * (S1 - f) * film_width * 2)
half4 _CoCKernelLimitsA;
half4 _CoCKernelLimitsB;
float4 _MaxCoCTexUvScale; // (kc)rename + move more variables to half
float _CoCRingCount;
float4 _CoCScreen;
float _CoCTileXCount;
float _CoCTileYCount;
float _CoCTilePixelWidth;
float _CoCTilePixelHeight;
float _MaxCoC;
float _RcpMaxCoC;
float _RcpAspect;
Expand Down Expand Up @@ -150,9 +157,27 @@ half4 FragPrefilter(VaryingsDefault i) : SV_Target
return half4(avg, coc);
}

VaryingsDefault VertDownsampleCoC(AttributesDefault v)
{
VaryingsDefault o;
o.vertex = float4(v.vertex.xy, 0.0, 1.0);
o.texcoord = TransformTriangleVertexToUV(v.vertex.xy);
#if defined(INITIAL_COC)
o.texcoord *= _MaxCoCTexUvScale.xy;
#endif

#if UNITY_UV_STARTS_AT_TOP
o.texcoord = o.texcoord * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif

o.texcoordStereo = TransformStereoScreenSpaceTex(o.texcoord, 1.0);

return o;
}

half4 FragDownsampleCoC(VaryingsDefault i) : SV_Target
{
// TODO gather version
// TODO implement gather version

float3 duv = _MainTex_TexelSize.xyx * float3(0.5, 0.5, -0.5);
float2 uv0 = UnityStereoTransformScreenSpaceTex(i.texcoord - duv.xy);
Expand All @@ -168,6 +193,7 @@ half4 FragDownsampleCoC(VaryingsDefault i) : SV_Target
cocs.w = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv3).r;

#if defined(INITIAL_COC)
// Storing the absolute normalized CoC is enough.
cocs = cocs * 2.0 - 1.0;
#endif
cocs = abs(cocs);
Expand Down Expand Up @@ -209,22 +235,24 @@ half4 FragExtendCoC(VaryingsDefault i) : SV_Target
// Bokeh filter with disk-shaped kernels
half4 FragBlur(VaryingsDefault i) : SV_Target
{
half4 samp0 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoordStereo);
// normalized value in range [0, 1]
half maxCoC = SAMPLE_TEXTURE2D(_MaxCoCTex, sampler_MaxCoCTex, i.texcoordStereo).r;
const half margin = _MainTex_TexelSize.y * 2;

int sampleCount = kSampleCount;
half4 samp0 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoordStereo);

half4 bgAcc = 0.0; // Background: far field bokeh
half4 fgAcc = 0.0; // Foreground: near field bokeh

const half margin = _MainTex_TexelSize.y * 2;
#if defined(KERNEL_UNIFIED)
int sampleCount = kDiskAllKernelSizes[KERNEL_UNIFIED];
#else
int sampleCount = kSampleCount;
#endif

UNITY_LOOP
for (int si = 0; si < sampleCount; si++)
{
#if defined(KERNEL_UNIFIED)
float2 disp = kDiskAllKernels[si] * _MaxCoC * (12.0 / 8.0);
float2 disp = kDiskAllKernels[si] * (_MaxCoC * (12.0 / 8.0));
#else
float2 disp = kDiskKernel[si] * _MaxCoC;
#endif
Expand Down Expand Up @@ -270,11 +298,11 @@ half4 FragBlur(VaryingsDefault i) : SV_Target
}

// Bokeh filter with disk-shaped kernels
half4 FragBlurUnified(VaryingsDefault i) : SV_Target
half4 FragBlurDynamic(VaryingsDefault i) : SV_Target
{
half4 samp0 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoordStereo);
// normalized value in range [0, 1]
half maxCoC = SAMPLE_TEXTURE2D(_MaxCoCTex, sampler_MaxCoCTex, i.texcoordStereo).r;
half maxCoC = SAMPLE_TEXTURE2D(_MaxCoCTex, sampler_MaxCoCTex, i.texcoordStereo * _MaxCoCTexUvScale.zw).r;

int sampleCount;

Expand All @@ -298,7 +326,7 @@ half4 FragBlurUnified(VaryingsDefault i) : SV_Target
UNITY_LOOP
for (int si = 0; si < sampleCount; si++)
{
float2 disp = kDiskAllKernels[si] * _MaxCoC * (12.0 / 8.0);
float2 disp = kDiskAllKernels[si] * (_MaxCoC * (12.0 / 8.0));

float dist = length(disp);

Expand Down Expand Up @@ -338,21 +366,83 @@ half4 FragBlurUnified(VaryingsDefault i) : SV_Target
half alpha = saturate(fgAcc.a);
half3 rgb = lerp(bgAcc.rgb, fgAcc.rgb, alpha);

/*
if (i.texcoord.x < 0.05)
rgb.r += 0.5; // (kc)
return half4(rgb, alpha);
}

if (sampleCount == 8)
rgb.r += 0.5;
if (sampleCount == 22)
rgb.g += 0.5;
if (sampleCount == 43)
rgb.b += 0.5;
if (sampleCount == 71)
rgb.rg += 0.5;
*/
struct Attributes
{
uint vertexID : SV_VertexID;
uint instanceID : SV_InstanceID;
};

return half4(rgb, alpha);
uint2 UnpackTileID(uint tileID)
{
return uint2(tileID & 0xFFFF, (tileID >> 16) & 0xFFFF);
}

// 0 - 0,1
// 1 - 0,0
// 2 - 1,0
// 3 - 1,1
float4 GetQuadVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
{
uint topBit = vertexID >> 1;
uint botBit = (vertexID & 1);
float x = topBit;
float y = 1 - (topBit + botBit) & 1; // produces 1 for indices 0,3 and 0 for 1,2
float4 pos = float4(x, y, z, 1.0);
return pos;
}

VaryingsDefault VertexTiling(Attributes input)
{
uint2 tileCoord = uint2(input.instanceID % (uint)_CoCTileXCount, input.instanceID / (uint)_CoCTileXCount); // (kc) stereo mode?
// normalized value in range [0, 1]
half maxCoC = LOAD_TEXTURE2D(_MaxCoCTex, _MaxCoCTex_TexelSize, tileCoord).x;

bool shouldDiscard;

UNITY_BRANCH if (maxCoC < _CoCKernelLimitsA[0])
shouldDiscard = _CoCRingCount != 0;
// margin adjustment later in the shader code artifically expand bokeh by 4px in fullscreen units (1 extra ring), we cannot have small bokeh as a result!
else UNITY_BRANCH if (maxCoC < _CoCKernelLimitsA[1])
shouldDiscard = _CoCRingCount != 1+1;
else UNITY_BRANCH if (maxCoC < _CoCKernelLimitsA[2])
shouldDiscard = _CoCRingCount != 2+1;
else UNITY_BRANCH if (maxCoC < _CoCKernelLimitsA[3])
shouldDiscard = _CoCRingCount != 3+1;
else
shouldDiscard = _CoCRingCount != 4;

VaryingsDefault output;

[branch] if (shouldDiscard)
{
output.vertex = float4(-2, -2, -2, 1);
output.texcoord = 0.0.xx;
output.texcoordStereo = 0.0.xx;
#if STEREO_INSTANCING_ENABLED
output.stereoTargetEyeIndex = 0;
#endif
return output;
}

// This handles both "real quad" and "2 triangles" cases: remaps {0, 1, 2, 3, 4, 5} into {0, 1, 2, 3, 0, 2}.
uint quadIndex = (input.vertexID & 0x03) + (input.vertexID >> 2) * (input.vertexID & 0x01);
float2 pp = GetQuadVertexPosition(quadIndex).xy;
uint2 pixelCoord = tileCoord * uint2(_CoCTilePixelWidth, _CoCTilePixelHeight);
pixelCoord += uint2(pp.xy * uint2(_CoCTilePixelWidth, _CoCTilePixelHeight));
pixelCoord.y = _CoCScreen.y - pixelCoord.y;
float2 clipCoord = (pixelCoord * _CoCScreen.zw) * 2.0 - 1.0;

output.vertex = float4(clipCoord, 0, 1);
output.texcoord = clipCoord * 0.5 + 0.5;
#if UNITY_UV_STARTS_AT_TOP
output.texcoord = output.texcoord * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif
output.texcoordStereo = TransformStereoScreenSpaceTex(output.texcoord, 1.0);

return output;
}

// Postfilter blur
Expand Down
Loading

0 comments on commit 69f1e7c

Please sign in to comment.