[gpu gems3] chapter 28. practical post process depth of field
TRANSCRIPT
GP
U G
em
s3
Chapter 28. Practical Post-Process Depth of Field
http://ohyecloudy.com
http://cafe.naver.com/shader.cafe
2009.07.06
Overview
Post-Process Stage에서 DoF를 구현한다.
Depth Texture를 사용
depth 값을 사용해 CoC를 구한다.
Blur 경계를 부드럽게 표현
focused 오브젝트와 unfocused 오브젝트 경계
lens
Imaging Plane
nu nv
d
c
0v
n
n
v
vv
d
c 0
n
n
f
f
v
vv
d
c
v
vv00
p
p
v
vvdc
0
pv
v
fv
fdc 0
0
1
초점거리 변수focal length
Initial Stochastic Approach
The Scatter-as-Gather Approach
The Blur Approach
Evolution of the Algorithm
모든 픽셀의 CoC 계산. 계산된 CoC를 원본 이미지 샘플링에 사용. 원형 푸아송 분포(Poisson distribution)
주변 픽셀과 현재 계산중인 픽셀의 CoC가 같다는 가정. 이 가정이 아니면 주변 픽셀의 CoC를 따져서 샘플링 해야 한다.
Search CoC
original texture
Initial Stochastic Approach 주변 픽셀과 현재 계산중인 픽셀의 CoC가 같다는 가정
Sharp shilhouettes, Ring artifact, 텍스쳐 품질 저하.
The Scatter-as-Gather Approach
The Blur Approach
Evolution of the Algorithm
original texture
주변 픽셀과 현재 계산중인 픽셀의 CoC가 같다는 가정. 각자 자신의 CoC를 가진다. 주변 픽셀을 검색할 때 가장 큰 CoC로 검색 주변 픽셀의 CoC가 저마다 다르기 때문.
현재 계산중인 픽셀이 CoC 범위에 들지 않으므로 제외
Initial Stochastic Approach 주변 픽셀과 현재 계산중인 픽셀의 CoC가 같다는 가정
Sharp shilhouettes, Ring artifact, 텍스쳐 품질 저하.
The Scatter-as-Gather Approach 각 픽셀마다 자싞의 CoC를 가진다.
첫번째 시도보다는 blur된 텍스쳐 품질은 좋아짐
The Blur Approach
Evolution of the Algorithm
focused와 unfocused 경계선이 부드럽지 않다. 최대 blur와 focused 오브젝트의 경계선은 약 50% blur
gradient가 필요.
102
1DDDB
01 2 DDD B
0000 ),max(2)2,max( DDDDDDD BB
diameter
Initial Stochastic Approach 주변 픽셀과 현재 계산중인 픽셀의 CoC가 같다는 가정
Sharp shilhouettes, Ring artifact, 텍스쳐 품질 저하.
The Scatter-as-Gather Approach 각 픽셀마다 자싞의 CoC를 가진다.
첫번째 시도보다는 blur된 텍스쳐 품질은 좋아짐
The Blur Approach CoC 텍스쳐 ¼ 다운 샘플링 & Blur
gradient로 경계선처리
셋 중 가장 만족스러운 결과물
Evolution of the Algorithm
구현 고려 사항
Depth Information R32F Texture
Variable-Width Blur
Circle of Confusion Radius
First-Person Weapon Considerations
piecewise linear curve로 approximate 서로 다른 blur radius 3개
오리지널 unblurred sample
RGB A
2개의 큰 blur radius 다운 샘플링된 CoC
가장 작은 blur radius
5번의 texture lookup
5X5에서 평균 17 픽셀
가장 작은 blur
실제 blur된 컬러의 approximation
연속된 blur 컬러 표현
구현 고려 사항
Depth Information R32F Texture
Variable-Width Blur blur radius 3개 + unblurred sample
가장 작은 blur radius로 실제 컬러 귺사, 연속된 blur 컬러 표현
Circle of Confusion Radius
First-Person Weapon Considerations
Depth Texture에 기록된 Depth 값으로 구한다.
더 자유로운 표현을 위해
world-near-end distance
world-far-start distance
pv
v
fv
fdc 0
0
1
구현 고려 사항
Depth Information R32F Texture
Variable-Width Blur blur radius 3개 + unblurred sample
가장 작은 blur radius로 실제 컬러 귺사, 연속된 blur 컬러 표현
Circle of Confusion Radius 앞에서 구한 공식을 그대로 사용하지 않는다.
자유로운 표현을 위해 변수 추가
First-Person Weapon Considerations
FPS(first-person shooter) 게임에서 플레이어 무기 표현은 매우 중요
특별 처리
view model로 따로 near, far 값을 갖게 한다.
world와 view model의 구분
view model의 경우 depth를 마이너스 값으로 기록
구현 고려 사항
Depth Information R32F Texture
Variable-Width Blur blur radius 3개 + unblurred sample
가장 작은 blur radius로 실제 컬러 귺사, 연속된 blur 컬러 표현
Circle of Confusion Radius 앞에서 구한 공식을 그대로 사용하지 않는다.
자유로운 표현을 위해 변수 추가
First-Person Weapon Considerations 특별처리, Depth는 음수값으로 기록
The Complete Algorithm
Downsamples the Scene and Initializes the Near CoC. soft egde를 표현하기 위한 near CoC 생성 렌더 타겟은 씬의 ¼ 사이즈
Pixel Shader That Calculates the Actual Near CoC Blurs the Near CoC and Downsampled Color Image Once Merges the Far CoC with the Near CoC and Applies It to the Screen
struct PixelInput { float4 position : POSITION; float2 tcColor0 : TEXCOORD0; float2 tcColor1 : TEXCOORD1; float2 tcDepth0 : TEXCOORD2; float2 tcDepth1 : TEXCOORD3; float2 tcDepth2 : TEXCOORD4; float2 tcDepth3 : TEXCOORD5; }; PixelInput DofDownVS( float4 pos : POSITION, float2 tc : TEXCOORD0 ) { PixelInput pixel; pixel.position = mul( pos, worldViewProj ); pixel.tcColor0 = tc + float2( -1.0, -1.0 ) * invRenderTargetSize; pixel.tcColor1 = tc + float2( +1.0, -1.0 ) * invRenderTargetSize; pixel.tcDepth0 = tc + float2( -1.5, -1.5 ) * invRenderTargetSize; pixel.tcDepth1 = tc + float2( -0.5, -1.5 ) * invRenderTargetSize; pixel.tcDepth2 = tc + float2( +0.5, -1.5 ) * invRenderTargetSize; pixel.tcDepth3 = tc + float2( +1.5, -1.5 ) * invRenderTargetSize; return pixel; }
const float2 dofRowDelta;// float2( 0, 0.25 / renderTargetHeight ) half4 DofDownPS( const PixelInput pixel ) : COLOR { rowOfs[0] = 0; rowOfs[1] = dofRowDelta.xy; rowOfs[2] = dofRowDelta.xy * 2; rowOfs[3] = dofRowDelta.xy * 3; // Use bilinear filtering to average 4 color samples for free. half3 color = 0; color += tex2D( colorSampler, pixel.tcColor0.xy + rowOfs[0] ).rgb; color += tex2D( colorSampler, pixel.tcColor1.xy + rowOfs[0] ).rgb; color += tex2D( colorSampler, pixel.tcColor0.xy + rowOfs[2] ).rgb; color += tex2D( colorSampler, pixel.tcColor1.xy + rowOfs[2] ).rgb; color /= 4; // Process 4 samples at a time to use vector hardware efficiently. // The CoC will be 1 if the depth is negative, so use "min" to pick // between "sceneCoc" and "viewCoc". depth[0] = tex2D( depthSampler, pixel.tcDepth0.xy + rowOfs[0] ).r; depth[1] = tex2D( depthSampler, pixel.tcDepth1.xy + rowOfs[0] ).r; depth[2] = tex2D( depthSampler, pixel.tcDepth2.xy + rowOfs[0] ).r; depth[3] = tex2D( depthSampler, pixel.tcDepth3.xy + rowOfs[0] ).r; half4 viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); half4 sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); half4 curCoc = min( viewCoc, sceneCoc ); half4 coc = curCoc; // rowOfs[1], rowOfs[2], rowOfs[3] half maxCoc = max( max( coc[0], coc[1] ), max( coc[2], coc[3] ) ); return half4( color, maxCoc ); }
The Complete Algorithm
Downsamples the Scene and Initializes the Near CoC. soft egde를 표현하기 위한 near CoC 생성 렌더 타겟은 씬의 ¼ 사이즈
Pixel Shader That Calculates the Actual Near CoC 경계선을 부드럽게 할 수 있도록 CoC 계산
Blurs the Near CoC and Downsampled Color Image Once Merges the Far CoC with the Near CoC and Applies It to the Screen
// These are set by the game engine. sampler shrunkSampler; // Output of DofDownsample() sampler blurredSampler; // Blurred version of the shrunk sampler // This is the pixel shader function that calculates the actual // value used for the near circle of confusion. // "texCoords" are 0 at the bottom left pixel and 1 at the top right. float4 DofNearCoc( const float2 texCoords ) { half4 shrunk = tex2D( shrunkSampler, texCoords ); half4 blurred = tex2D( blurredSampler, texCoords ); float3 color = shrunk.rgb; float coc = 2 * max( blurred.a, shrunk.a ) - shrunk.a; return float4( color, coc ); }
0000 ),max(2)2,max( DDDDDDD BB
Gaussian blur
shrunkSampler blurredSampler
DofDownTexture
blurred DofDownTexture
The Complete Algorithm
Downsamples the Scene and Initializes the Near CoC. soft egde를 표현하기 위한 near CoC 생성
렌더 타겟은 씬의 ¼ 사이즈
Pixel Shader That Calculates the Actual Near CoC 경계선을 부드럽게 할 수 있도록 CoC 계산
Blurs the Near CoC and Downsampled Color Image Once
3x3 blur를 사용해 실제로 경계선을 부드럽게 한다.
Merges the Far CoC with the Near CoC and Applies It to the Screen
// This vertex and pixel shader applies a 3 x 3 blur to the image in // colorMapSampler, which is the same size as the render target. // The sample weights are 1/16 in the corners, 2/16 on the edges, // and 4/16 in the center. sampler colorSampler;// Output of DofNearCoc() float2 invRenderTargetSize; struct PixelInput { float4 position : POSITION; float4 texCoords : TEXCOORD0; }; PixelInput SmallBlurVS( float4 position, float2 texCoords ) { PixelInput pixel; const float4 halfPixel = { -0.5, 0.5, -0.5, 0.5 }; pixel.position = Transform_ObjectToClip( position ); pixel.texCoords = texCoords.xxyy + halfPixel * invRenderTargetSize; return pixel; } float4 SmallBlurPS( const PixelInput pixel ) { float4 color; color = 0; color += tex2D( colorSampler, pixel.texCoords.xz ); color += tex2D( colorSampler, pixel.texCoords.yz ); color += tex2D( colorSampler, pixel.texCoords.xw ); color += tex2D( colorSampler, pixel.texCoords.yw ); return color / 4; }
The Complete Algorithm
Downsamples the Scene and Initializes the Near CoC. soft egde를 표현하기 위한 near CoC 생성 렌더 타겟은 씬의 ¼ 사이즈
Pixel Shader That Calculates the Actual Near CoC 경계선을 부드럽게 할 수 있도록 CoC 계산
Blurs the Near CoC and Downsampled Color Image Once 3x3 blur를 사용해 실제로 경계선을 부드럽게 한다.
Merges the Far CoC with the Near CoC and Applies It to the Screen varialbe-width blur 적용 모든 color sample의 alpha를 1로 가정 읽지 않은 center 샘플을 color가 0이고 alpha가 0인 픽셀로 처리
R G B A
오리지널 텍스쳐 컬러 max CoC
Color Texture
Depth Texture
DofDownTexture
Gaussian blur
blurred DofDownTexture
largeBlurSampler
DofNearCoc Texture
SmallBlurTexture
0000 ),max(2)2,max( DDDDDDD BB
3x3 blur
smallBlurSampler
sampler colorSampler; // Original source image sampler smallBlurSampler; // Output of SmallBlurPS() sampler largeBlurSampler; // Blurred output of DofDownsample() half4 ApplyDepthOfField( const float2 texCoords ) { half coc; half3 small = GetSmallBlurSample( texCoords ); half4 med = tex2D( smallBlurSampler, texCoords ); half3 large = tex2D( largeBlurSampler, texCoords ).rgb; half nearCoc = med.a; half depth = tex2D( depthSampler, texCoords ).r; if ( depth > 1.0e6 ) { coc = nearCoc; // We don't want to blur the sky. } else { // dofEqFar.x and dofEqFar.y specify the linear ramp to convert // to depth for the distant out-of-focus region. half farCoc = saturate( dofEqFar.x * depth + dofEqFar.y ); // dofEqFar.z is the ratio of the far to the near blur radius. coc = max( nearCoc, farCoc * dofEqFar.z ); } return InterpolateDof( small, med.rgb, large, coc ); }
half3 GetSmallBlurSample( float2 tc ) { const half weight = 4.0 / 17; half3 sum = 0; // Unblurred sample done by alpha blending sum += weight * tex2Doffset( colorSampler, tc, +0.5, -1.5 ).rgb; sum += weight * tex2Doffset( colorSampler, tc, -1.5, -0.5 ).rgb; sum += weight * tex2Doffset( colorSampler, tc, -0.5, +1.5 ).rgb; sum += weight * tex2Doffset( colorSampler, tc, +1.5, +0.5 ).rgb; return sum; }
float4 tex2Doffset( sampler s, float2 tc, float2 offset ) { return tex2D( s, tc + offset * invRenderTargetSize ); }
half4 InterpolateDof( half3 small, half3 med, half3 large, half coc ) { // Efficiently calculate the cross-blend weights for each sample. // Let the unblurred sample to small blur fade happen over distance // d0, the small to medium blur over distance d1, and the medium to // large blur over distance d2, where d0 + d1 + d2 = 1. // dofLerpScale = float4( -1 / d0, -1 / d1, -1 / d2, 1 / d2 ); // dofLerpBias = float4( 1, (1 – d2) / d1, 1 / d2, (d2 – 1) / d2 ); half4 weights = saturate( coc * dofLerpScale + dofLerpBias ); weights.yz = min( weights.yz, 1 - weights.xy ); // Unblurred sample with weight "weights.x" done by alpha blending half3 color = weights.y * small + weights.z * med + weights.w * large; half alpha = dot( weights.yzw, half3( 16.0 / 17, 1.0, 1.0 ) ); return half4( color, alpha ); }
0d1d 2d
Unblurred Small blur Medium blur Large blur
Conclusion
• 픽셀당 8.625 샘플링이 필요.
– large Gaussian blur의 샘플링 카운트를 계산하지 않았을때
• 오리지널 이미지에 있는 픽셀이 1.3125 번 쓰인다.
– ¼로 다운 샘플링해서 사용
• 렌더타겟 6장
– Gaussian blur가 2패스일때를 가정.