//**************************************************************************/ // Copyright (c) 2010 Autodesk, Inc. // All rights reserved. // // These coded instructions, statements, and computer programs contain // unpublished proprietary information written by Autodesk, Inc., and are // protected by Federal copyright law. They may not be disclosed to third // parties or copied or duplicated in any form, in whole or in part, without // the prior written consent of Autodesk, Inc. //**************************************************************************/ // DESCRIPTION: Spread the influence of DOF to neighboring pixels. // AUTHOR: Kells Elmquist // CREATED: November 2010 //**************************************************************************/ #include "Common.cgh" ///////////////////////////////////////////////////////////////////////////// // // Spread DOF circles of confusion to affected pixels // // The source image texure to be blurred uniform texture source; uniform sampler2D sourceSamp = sampler_state { Texture = ; }; // the coc/depth texture is a float2 texture that has no-abs coc in .x & linear depth in .y uniform texture cocDepth; uniform sampler2D cocDepthSamp = sampler_state { Texture = ; }; uniform int searchRadius1 = 1; // in pixels, radius 1 = 3x3 samples; side=2*radius+1 uniform int sampleSpacing1 = 1; // stride between pixel samples in search, 1x1 default uniform int searchRadius2 = 3; // in pixels, radius 3 = 7x7 samples uniform int sampleSpacing2 = 2; // stride between pixel samples in motion spreading, 1x1 default uniform float acceptThreshold = 0.05f;// similarity threshold to accept neighbor coc uniform float cocAtInfinity = 0.1f; // aka alpha, its in uv coords uniform float spreadScale = 1.0f; // spacing of 3x3, each sample represents an area of 9 pixels uniform float maxCocScale = 2.0f; // hack factor for maxCoc, correct value is too small // filter kernel, return value of kernel of radius r at a distance x from the filter center // both x & r must be positive, r must be > 0 float filter( float r, float x ) { //return 1.0f; // test for sharp if( r < 0.00001f ) return 1; // linear (triangle) kernel return saturate( 1.0f - x/r ); // gaussian kernel //return 0.79788f * exp( -x*x/(0.375 * r*r) ) / r; } // DOF Pixel shader. multi pass variable scale search & reverse filter, // NOTE: This expects the common screen quad vertex shader output in common.cgh. float4 spreadDOFBlur( VS_TO_PS_ScreenQuad In ) : COLOR0 { // Compute the offset between samples in uv space, spacing always 1 for first pass float2 offset = (float)sampleSpacing1 * gTexelSize; int count = 2 * searchRadius1 + 1; // search count x count samples float maxCoc = maxCocScale * max(offset.x, offset.y) * ( max((float)searchRadius1, (float)searchRadius2) + 0.5f); // center pixel float4 c0 = tex2D(cocDepthSamp, In.UV); // fixup the coc for bgnd pixels, buffer is cleared to 10e6 if( c0.y > 999990 ) c0.x = cocAtInfinity; float coc0 = min( abs( c0.x ), maxCoc ); //float coc0 = abs( c0.x ); float wSum = filter( coc0, 0.0f ); // color sums: infront, center, both work as rgba and have local sum of weights in frontW and centerW respectively float4 centerSum = wSum * tex2D(sourceSamp, In.UV); float4 infrontSum = (float4)0; float centerW = 1.0; float frontW = 0.0; // get start uv, clr & weight sums float2 UV = In.UV - offset * (float)searchRadius1; float u0 = UV.x; // collect each qualified sample point, then weight & average them // for each row for (int i = 0; i < count; i++) { // for each pixel in the row for (int j = 0; j < count; j++) { if( i==searchRadius1 && j==searchRadius1 ) // no center continue; float4 cn = tex2D(cocDepthSamp, UV); // fixup the coc for bgnd pixels, buffer is cleared to 10e6 if( cn.y > 999990 ) cn.x = cocAtInfinity; float coc = min( abs( cn.x ), maxCoc ); //float coc = abs( cn.x ); // d is the distance from center to sample in uv space float d = length( UV - In.UV ); // The z component is 0.0 by default. If it is 1.0, the pixel // will need to be excluded from post effects. bool includePostEffects = (cn.z < 0.5f); // qualify samples by z, note z is stored in y field of float2 // & see if coc of sample overlaps center pixel, coc >= d, if( coc >= d && includePostEffects ) { // get the scene sample at UV float4 sn = tex2D(sourceSamp, UV); float w = filter( coc, d ); wSum += w; // & classify sample by coc ... //if( abs(coc - coc0) <= acceptThreshold ) // coc band method float dInPix = d / gTexelSize.x; // texelSz in 1/width, so d in pixels is d * width. if( abs(coc - coc0)/dInPix <= acceptThreshold ) // coc max gradient method { // within near coc threshold band centerSum += w * sn; centerW += w; } else if( coc < coc0 ) { // sample is in front of c0 infrontSum += w * sn; frontW += w; } } // Increment the texture coordinates by the offset in x, next pixel in row UV.x += offset.x; } // for each j // Increment the texture coordinates by the offset in y, next row UV.x = u0; UV.y += offset.y; } // for each i // optional second, scaled search area // second pass, use the given sample spacing offset = (float)sampleSpacing2 * gTexelSize; count = 2 * searchRadius2 + 1; // search count x count samples UV = In.UV - offset * searchRadius2; u0 = UV.x; // collect each qualified sample point for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { if( i==searchRadius2 && j==searchRadius2 ) continue; float4 cn = tex2D(cocDepthSamp, UV); if( cn.y > 999990.0f ) cn.x = cocAtInfinity; float coc = min( abs( cn.x ), maxCoc ); //float coc = abs( cn.x ); float d = length( UV - In.UV ); // The z component is 0.0 by default. If it is 1.0, the pixel // will need to be excluded from post effects. bool includePostEffects = (cn.z < 0.5f); if( coc >= d && includePostEffects ) { float4 sn = tex2D(sourceSamp, UV); float w = spreadScale * filter( coc, d ); wSum += w; // & classify sample by coc ... //if( abs(coc - coc0) <= acceptThreshold ) // coc band method float dInPix = d / gTexelSize.x; // texelSz in 1/width, so d in pixels is d * width. if( abs(coc - coc0)/dInPix <= acceptThreshold ) // coc max gradient method { centerSum += w * sn; centerW += w; } else if( coc < coc0 ) { infrontSum += w * sn; frontW += w; } } UV.x += offset.x; } UV.x = u0; UV.y += offset.y; } // for each i // normalize front & center sums separately // always true, initialized : if( centerW != 0.0f ) centerSum /= centerW; if( frontW != 0.0f ) infrontSum /= frontW; // blend the front over the center. scale by 2 float frontBlend = saturate( frontW / wSum ); return lerp( centerSum, infrontSum, frontBlend ); } // shader for either pass of the 2 pass h/v spreading technique. technique spreadDOFPass { pass p0 { VertexShader = compile glslv VS_ScreenQuad(); PixelShader = compile glslf spreadDOFBlur(); } }