You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
18 KiB
387 lines
18 KiB
using System; |
|
using UnityEngine; |
|
|
|
namespace UnityStandardAssets.ImageEffects |
|
{ |
|
[ExecuteInEditMode] |
|
[RequireComponent (typeof(Camera))] |
|
[AddComponentMenu ("Image Effects/Camera/Depth of Field (Lens Blur, Scatter, DX11)") ] |
|
public class DepthOfField : PostEffectsBase { |
|
|
|
public bool visualizeFocus = false; |
|
public float focalLength = 10.0f; |
|
public float focalSize = 0.05f; |
|
public float aperture = 0.5f; |
|
public Transform focalTransform = null; |
|
public float maxBlurSize = 2.0f; |
|
public bool highResolution = false; |
|
|
|
public enum BlurType { |
|
DiscBlur = 0, |
|
DX11 = 1, |
|
} |
|
|
|
public enum BlurSampleCount { |
|
Low = 0, |
|
Medium = 1, |
|
High = 2, |
|
} |
|
|
|
public BlurType blurType = BlurType.DiscBlur; |
|
public BlurSampleCount blurSampleCount = BlurSampleCount.High; |
|
|
|
public bool nearBlur = false; |
|
public float foregroundOverlap = 1.0f; |
|
|
|
public Shader dofHdrShader; |
|
private Material dofHdrMaterial = null; |
|
|
|
public Shader dx11BokehShader; |
|
private Material dx11bokehMaterial; |
|
|
|
public float dx11BokehThreshold = 0.5f; |
|
public float dx11SpawnHeuristic = 0.0875f; |
|
public Texture2D dx11BokehTexture = null; |
|
public float dx11BokehScale = 1.2f; |
|
public float dx11BokehIntensity = 2.5f; |
|
|
|
private float focalDistance01 = 10.0f; |
|
private ComputeBuffer cbDrawArgs; |
|
private ComputeBuffer cbPoints; |
|
private float internalBlurWidth = 1.0f; |
|
|
|
private Camera cachedCamera; |
|
|
|
public override bool CheckResources () { |
|
CheckSupport (true); // only requires depth, not HDR |
|
|
|
dofHdrMaterial = CheckShaderAndCreateMaterial (dofHdrShader, dofHdrMaterial); |
|
if (supportDX11 && blurType == BlurType.DX11) { |
|
dx11bokehMaterial = CheckShaderAndCreateMaterial(dx11BokehShader, dx11bokehMaterial); |
|
CreateComputeResources (); |
|
} |
|
|
|
if (!isSupported) |
|
ReportAutoDisable (); |
|
|
|
return isSupported; |
|
} |
|
|
|
void OnEnable () { |
|
cachedCamera = GetComponent<Camera>(); |
|
cachedCamera.depthTextureMode |= DepthTextureMode.Depth; |
|
} |
|
|
|
void OnDisable () { |
|
ReleaseComputeResources (); |
|
|
|
if (dofHdrMaterial) DestroyImmediate(dofHdrMaterial); |
|
dofHdrMaterial = null; |
|
if (dx11bokehMaterial) DestroyImmediate(dx11bokehMaterial); |
|
dx11bokehMaterial = null; |
|
} |
|
|
|
void ReleaseComputeResources () { |
|
if (cbDrawArgs != null) cbDrawArgs.Release(); |
|
cbDrawArgs = null; |
|
if (cbPoints != null) cbPoints.Release(); |
|
cbPoints = null; |
|
} |
|
|
|
void CreateComputeResources () { |
|
if (cbDrawArgs == null) |
|
{ |
|
cbDrawArgs = new ComputeBuffer (1, 16, ComputeBufferType.IndirectArguments); |
|
var args= new int[4]; |
|
args[0] = 0; args[1] = 1; args[2] = 0; args[3] = 0; |
|
cbDrawArgs.SetData (args); |
|
} |
|
if (cbPoints == null) |
|
{ |
|
cbPoints = new ComputeBuffer (90000, 12+16, ComputeBufferType.Append); |
|
} |
|
} |
|
|
|
float FocalDistance01 ( float worldDist) { |
|
return cachedCamera.WorldToViewportPoint((worldDist-cachedCamera.nearClipPlane) * cachedCamera.transform.forward + cachedCamera.transform.position).z / (cachedCamera.farClipPlane-cachedCamera.nearClipPlane); |
|
} |
|
|
|
private void WriteCoc ( RenderTexture fromTo, bool fgDilate) { |
|
dofHdrMaterial.SetTexture("_FgOverlap", null); |
|
|
|
if (nearBlur && fgDilate) { |
|
|
|
int rtW = fromTo.width/2; |
|
int rtH = fromTo.height/2; |
|
|
|
// capture fg coc |
|
RenderTexture temp2 = RenderTexture.GetTemporary (rtW, rtH, 0, fromTo.format); |
|
Graphics.Blit (fromTo, temp2, dofHdrMaterial, 4); |
|
|
|
// special blur |
|
float fgAdjustment = internalBlurWidth * foregroundOverlap; |
|
|
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, fgAdjustment , 0.0f, fgAdjustment)); |
|
RenderTexture temp1 = RenderTexture.GetTemporary (rtW, rtH, 0, fromTo.format); |
|
Graphics.Blit (temp2, temp1, dofHdrMaterial, 2); |
|
RenderTexture.ReleaseTemporary(temp2); |
|
|
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (fgAdjustment, 0.0f, 0.0f, fgAdjustment)); |
|
temp2 = RenderTexture.GetTemporary (rtW, rtH, 0, fromTo.format); |
|
Graphics.Blit (temp1, temp2, dofHdrMaterial, 2); |
|
RenderTexture.ReleaseTemporary(temp1); |
|
|
|
// "merge up" with background COC |
|
dofHdrMaterial.SetTexture("_FgOverlap", temp2); |
|
fromTo.MarkRestoreExpected(); // only touching alpha channel, RT restore expected |
|
Graphics.Blit (fromTo, fromTo, dofHdrMaterial, 13); |
|
RenderTexture.ReleaseTemporary(temp2); |
|
} |
|
else { |
|
// capture full coc in alpha channel (fromTo is not read, but bound to detect screen flip) |
|
fromTo.MarkRestoreExpected(); // only touching alpha channel, RT restore expected |
|
Graphics.Blit (fromTo, fromTo, dofHdrMaterial, 0); |
|
} |
|
} |
|
|
|
void OnRenderImage (RenderTexture source, RenderTexture destination) { |
|
if (!CheckResources ()) { |
|
Graphics.Blit (source, destination); |
|
return; |
|
} |
|
|
|
// clamp & prepare values so they make sense |
|
|
|
if (aperture < 0.0f) aperture = 0.0f; |
|
if (maxBlurSize < 0.1f) maxBlurSize = 0.1f; |
|
focalSize = Mathf.Clamp(focalSize, 0.0f, 2.0f); |
|
internalBlurWidth = Mathf.Max(maxBlurSize, 0.0f); |
|
|
|
// focal & coc calculations |
|
|
|
focalDistance01 = (focalTransform) ? (cachedCamera.WorldToViewportPoint (focalTransform.position)).z / (cachedCamera.farClipPlane) : FocalDistance01 (focalLength); |
|
dofHdrMaterial.SetVector("_CurveParams", new Vector4(1.0f, focalSize, (1.0f / (1.0f - aperture) - 1.0f), focalDistance01)); |
|
|
|
// possible render texture helpers |
|
|
|
RenderTexture rtLow = null; |
|
RenderTexture rtLow2 = null; |
|
RenderTexture rtSuperLow1 = null; |
|
RenderTexture rtSuperLow2 = null; |
|
float fgBlurDist = internalBlurWidth * foregroundOverlap; |
|
|
|
if (visualizeFocus) |
|
{ |
|
|
|
// |
|
// 2. |
|
// visualize coc |
|
// |
|
// |
|
|
|
WriteCoc (source, true); |
|
Graphics.Blit (source, destination, dofHdrMaterial, 16); |
|
} |
|
else if ((blurType == BlurType.DX11) && dx11bokehMaterial) |
|
{ |
|
|
|
// |
|
// 1. |
|
// optimized dx11 bokeh scatter |
|
// |
|
// |
|
|
|
|
|
if (highResolution) { |
|
|
|
internalBlurWidth = internalBlurWidth < 0.1f ? 0.1f : internalBlurWidth; |
|
fgBlurDist = internalBlurWidth * foregroundOverlap; |
|
|
|
rtLow = RenderTexture.GetTemporary (source.width, source.height, 0, source.format); |
|
|
|
var dest2= RenderTexture.GetTemporary (source.width, source.height, 0, source.format); |
|
|
|
// capture COC |
|
WriteCoc (source, false); |
|
|
|
// blur a bit so we can do a frequency check |
|
rtSuperLow1 = RenderTexture.GetTemporary(source.width>>1, source.height>>1, 0, source.format); |
|
rtSuperLow2 = RenderTexture.GetTemporary(source.width>>1, source.height>>1, 0, source.format); |
|
|
|
Graphics.Blit(source, rtSuperLow1, dofHdrMaterial, 15); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, 1.5f , 0.0f, 1.5f)); |
|
Graphics.Blit (rtSuperLow1, rtSuperLow2, dofHdrMaterial, 19); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (1.5f, 0.0f, 0.0f, 1.5f)); |
|
Graphics.Blit (rtSuperLow2, rtSuperLow1, dofHdrMaterial, 19); |
|
|
|
// capture fg coc |
|
if (nearBlur) |
|
Graphics.Blit (source, rtSuperLow2, dofHdrMaterial, 4); |
|
|
|
dx11bokehMaterial.SetTexture ("_BlurredColor", rtSuperLow1); |
|
dx11bokehMaterial.SetFloat ("_SpawnHeuristic", dx11SpawnHeuristic); |
|
dx11bokehMaterial.SetVector ("_BokehParams", new Vector4(dx11BokehScale, dx11BokehIntensity, Mathf.Clamp(dx11BokehThreshold, 0.005f, 4.0f), internalBlurWidth)); |
|
dx11bokehMaterial.SetTexture ("_FgCocMask", nearBlur ? rtSuperLow2 : null); |
|
|
|
// collect bokeh candidates and replace with a darker pixel |
|
Graphics.SetRandomWriteTarget (1, cbPoints); |
|
Graphics.Blit (source, rtLow, dx11bokehMaterial, 0); |
|
Graphics.ClearRandomWriteTargets (); |
|
|
|
// fg coc blur happens here (after collect!) |
|
if (nearBlur) { |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, fgBlurDist , 0.0f, fgBlurDist)); |
|
Graphics.Blit (rtSuperLow2, rtSuperLow1, dofHdrMaterial, 2); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (fgBlurDist, 0.0f, 0.0f, fgBlurDist)); |
|
Graphics.Blit (rtSuperLow1, rtSuperLow2, dofHdrMaterial, 2); |
|
|
|
// merge fg coc with bg coc |
|
Graphics.Blit (rtSuperLow2, rtLow, dofHdrMaterial, 3); |
|
} |
|
|
|
// NEW: LAY OUT ALPHA on destination target so we get nicer outlines for the high rez version |
|
Graphics.Blit (rtLow, dest2, dofHdrMaterial, 20); |
|
|
|
// box blur (easier to merge with bokeh buffer) |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (internalBlurWidth, 0.0f , 0.0f, internalBlurWidth)); |
|
Graphics.Blit (rtLow, source, dofHdrMaterial, 5); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.0f, internalBlurWidth)); |
|
Graphics.Blit (source, dest2, dofHdrMaterial, 21); |
|
|
|
// apply bokeh candidates |
|
Graphics.SetRenderTarget (dest2); |
|
ComputeBuffer.CopyCount (cbPoints, cbDrawArgs, 0); |
|
dx11bokehMaterial.SetBuffer ("pointBuffer", cbPoints); |
|
dx11bokehMaterial.SetTexture ("_MainTex", dx11BokehTexture); |
|
dx11bokehMaterial.SetVector ("_Screen", new Vector3(1.0f/(1.0f*source.width), 1.0f/(1.0f*source.height), internalBlurWidth)); |
|
dx11bokehMaterial.SetPass (2); |
|
|
|
Graphics.DrawProceduralIndirect (MeshTopology.Points, cbDrawArgs, 0); |
|
|
|
Graphics.Blit (dest2, destination); // hackaround for DX11 high resolution flipfun (OPTIMIZEME) |
|
|
|
RenderTexture.ReleaseTemporary(dest2); |
|
RenderTexture.ReleaseTemporary(rtSuperLow1); |
|
RenderTexture.ReleaseTemporary(rtSuperLow2); |
|
} |
|
else { |
|
rtLow = RenderTexture.GetTemporary (source.width>>1, source.height>>1, 0, source.format); |
|
rtLow2 = RenderTexture.GetTemporary (source.width>>1, source.height>>1, 0, source.format); |
|
|
|
fgBlurDist = internalBlurWidth * foregroundOverlap; |
|
|
|
// capture COC & color in low resolution |
|
WriteCoc (source, false); |
|
source.filterMode = FilterMode.Bilinear; |
|
Graphics.Blit (source, rtLow, dofHdrMaterial, 6); |
|
|
|
// blur a bit so we can do a frequency check |
|
rtSuperLow1 = RenderTexture.GetTemporary(rtLow.width>>1, rtLow.height>>1, 0, rtLow.format); |
|
rtSuperLow2 = RenderTexture.GetTemporary(rtLow.width>>1, rtLow.height>>1, 0, rtLow.format); |
|
|
|
Graphics.Blit(rtLow, rtSuperLow1, dofHdrMaterial, 15); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, 1.5f , 0.0f, 1.5f)); |
|
Graphics.Blit (rtSuperLow1, rtSuperLow2, dofHdrMaterial, 19); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (1.5f, 0.0f, 0.0f, 1.5f)); |
|
Graphics.Blit (rtSuperLow2, rtSuperLow1, dofHdrMaterial, 19); |
|
|
|
RenderTexture rtLow3 = null; |
|
|
|
if (nearBlur) { |
|
// capture fg coc |
|
rtLow3 = RenderTexture.GetTemporary (source.width>>1, source.height>>1, 0, source.format); |
|
Graphics.Blit (source, rtLow3, dofHdrMaterial, 4); |
|
} |
|
|
|
dx11bokehMaterial.SetTexture ("_BlurredColor", rtSuperLow1); |
|
dx11bokehMaterial.SetFloat ("_SpawnHeuristic", dx11SpawnHeuristic); |
|
dx11bokehMaterial.SetVector ("_BokehParams", new Vector4(dx11BokehScale, dx11BokehIntensity, Mathf.Clamp(dx11BokehThreshold, 0.005f, 4.0f), internalBlurWidth)); |
|
dx11bokehMaterial.SetTexture ("_FgCocMask", rtLow3); |
|
|
|
// collect bokeh candidates and replace with a darker pixel |
|
Graphics.SetRandomWriteTarget (1, cbPoints); |
|
Graphics.Blit (rtLow, rtLow2, dx11bokehMaterial, 0); |
|
Graphics.ClearRandomWriteTargets (); |
|
|
|
RenderTexture.ReleaseTemporary(rtSuperLow1); |
|
RenderTexture.ReleaseTemporary(rtSuperLow2); |
|
|
|
// fg coc blur happens here (after collect!) |
|
if (nearBlur) { |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, fgBlurDist , 0.0f, fgBlurDist)); |
|
Graphics.Blit (rtLow3, rtLow, dofHdrMaterial, 2); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (fgBlurDist, 0.0f, 0.0f, fgBlurDist)); |
|
Graphics.Blit (rtLow, rtLow3, dofHdrMaterial, 2); |
|
|
|
// merge fg coc with bg coc |
|
Graphics.Blit (rtLow3, rtLow2, dofHdrMaterial, 3); |
|
} |
|
|
|
// box blur (easier to merge with bokeh buffer) |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (internalBlurWidth, 0.0f , 0.0f, internalBlurWidth)); |
|
Graphics.Blit (rtLow2, rtLow, dofHdrMaterial, 5); |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.0f, internalBlurWidth)); |
|
Graphics.Blit (rtLow, rtLow2, dofHdrMaterial, 5); |
|
|
|
// apply bokeh candidates |
|
Graphics.SetRenderTarget (rtLow2); |
|
ComputeBuffer.CopyCount (cbPoints, cbDrawArgs, 0); |
|
dx11bokehMaterial.SetBuffer ("pointBuffer", cbPoints); |
|
dx11bokehMaterial.SetTexture ("_MainTex", dx11BokehTexture); |
|
dx11bokehMaterial.SetVector ("_Screen", new Vector3(1.0f/(1.0f*rtLow2.width), 1.0f/(1.0f*rtLow2.height), internalBlurWidth)); |
|
dx11bokehMaterial.SetPass (1); |
|
Graphics.DrawProceduralIndirect (MeshTopology.Points, cbDrawArgs, 0); |
|
|
|
// upsample & combine |
|
dofHdrMaterial.SetTexture ("_LowRez", rtLow2); |
|
dofHdrMaterial.SetTexture ("_FgOverlap", rtLow3); |
|
dofHdrMaterial.SetVector ("_Offsets", ((1.0f*source.width)/(1.0f*rtLow2.width)) * internalBlurWidth * Vector4.one); |
|
Graphics.Blit (source, destination, dofHdrMaterial, 9); |
|
|
|
if (rtLow3) RenderTexture.ReleaseTemporary(rtLow3); |
|
} |
|
} |
|
else |
|
{ |
|
|
|
// |
|
// 2. |
|
// poisson disc style blur in low resolution |
|
// |
|
// |
|
|
|
source.filterMode = FilterMode.Bilinear; |
|
|
|
if (highResolution) internalBlurWidth *= 2.0f; |
|
|
|
WriteCoc (source, true); |
|
|
|
rtLow = RenderTexture.GetTemporary (source.width >> 1, source.height >> 1, 0, source.format); |
|
rtLow2 = RenderTexture.GetTemporary (source.width >> 1, source.height >> 1, 0, source.format); |
|
|
|
int blurPass = (blurSampleCount == BlurSampleCount.High || blurSampleCount == BlurSampleCount.Medium) ? 17 : 11; |
|
|
|
if (highResolution) { |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.025f, internalBlurWidth)); |
|
Graphics.Blit (source, destination, dofHdrMaterial, blurPass); |
|
} |
|
else { |
|
dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.1f, internalBlurWidth)); |
|
|
|
// blur |
|
Graphics.Blit (source, rtLow, dofHdrMaterial, 6); |
|
Graphics.Blit (rtLow, rtLow2, dofHdrMaterial, blurPass); |
|
|
|
// cheaper blur in high resolution, upsample and combine |
|
dofHdrMaterial.SetTexture("_LowRez", rtLow2); |
|
dofHdrMaterial.SetTexture("_FgOverlap", null); |
|
dofHdrMaterial.SetVector ("_Offsets", Vector4.one * ((1.0f*source.width)/(1.0f*rtLow2.width)) * internalBlurWidth); |
|
Graphics.Blit (source, destination, dofHdrMaterial, blurSampleCount == BlurSampleCount.High ? 18 : 12); |
|
} |
|
} |
|
|
|
if (rtLow) RenderTexture.ReleaseTemporary(rtLow); |
|
if (rtLow2) RenderTexture.ReleaseTemporary(rtLow2); |
|
} |
|
} |
|
}
|
|
|