GPU Gems 3, Chapter 27. Motion Blur as a Post-Processing Effect by 신동호

원문: http://http.developer.nvidia.com/GPUGems3/gpugems3_ch27.html

본 문서는 GPU Gems 3 권, 27장에 나오는 깊이 버퍼를 이용한 포스트 프로세싱 모션 블러에 대한 논문을 번역하고 있습니다. 깊이 버퍼만 있다면 이처럼 여러가지 재미있는 것들을 할 수 있군요:D 최근 논문들은 이처럼 깊이 버퍼를 활용한 내용이 많은데 이는 directx10 에 추가된 기능이기도 하다는 군요. directx9 에서는 rendertexture 로 만들어 줘야 할 것 같습니다.



Chapter 27. Motion Blur as a Post-Processing Effect

27.1 들어가는 글

비디오 게임에서 스피드 감이 있는 시뮬레이션을 보여줄 수 있는 가장 좋은 방법중 하나가 바로 모션 블러를 사용하는 것입니다. 모션 블러는 특히 레이싱 게임등에서 더욱 중요한 효과입니다. 왜냐하면 현실감과 속도감을 증가시켜 주기 때문입니다. 모션 블러는 또한 특히 30 프레임 혹은 그 이하의 프레임인 게임에서 화면을 부드럽게 보일 수 있도록 도와줍니다. 하지만 기존 엔진에 모션 블러를 추가하는 것이 쉽지는 않습니다. 왜냐하면 대부분의 모션 블러 테크닉은 분리되어 있는 모든 패스에서 픽셀 당 속도 버퍼를 생성하여 랜더링 해야 하기 때문입니다. 또한, 멀티 패스와 같은 접근 방식은 제한점이 있습니다: 대부분의 응용프로그램들은 전체 파이프라인을 통과하고 있는 하나 혹은 여전히 관리되고 있는 응용프로그램의 타겟 프레임율을 분석하는 것이 어렵습니다.

픽셀 당 속도 맵을 생성하는 다른 방법으로는, 멀티 랜더 타겟과 출력중인 랜더 타겟중 하나의 속도 정보를 포함하는 방법이 있습니다. 이와 같은 접근의 주요 단점으로는, 속도와 두 번째 랜더 타겟의 결과를 계산하기 위해 모든 장면의 쉐이더 코드를 수정해 주어야 한다는 점입니다. 또 다른 단점으로, 멀티 랜더 타겟으로 랜더링하는 것은 어떤 플랫폼에서는 성능을 감소시키게 될 것이라는 점입니다. 추가적으로, 어떤 플랫폼은 랜더링 메모리에 제한이 있으며 1280x720 혹은 그 이상의 프레임 버퍼에서 멀티 랜더 타겟을 사용하기 위해서는 타일링 메커니즘을 사용해야 한다는 점입니다.

이번 장에서 우리는 장면의 속도 맵을 생성하기 위해서 픽셀 쉐이더를 사용한 깊이 버퍼를 사용하는 기술에 대해 알아볼 것입니다. 픽셀 쉐이더 프로그램은 현재 프레임의 뷰 프로젝션 행렬과 깊이 값-깊이 버퍼에 저장된-을 이용하여 각 픽셀의 월드 공간 좌표를 계산합니다. 첫 번째로, 우리는 픽셀의 월드 공간의 위치를 결정합니다. 이 값은 이전 프레임의 뷰 프로젝션 행렬을 사용하여 변환할 수 있습니다. 그 다음 과정은, 픽셀 당 속도값은 현재 프레임과 이전 프레임의 뷰포트 상의 위치의 차로 계산할 수 있습니다. 모션 블러 효과는 이후에 이 속도 벡터를 사용하여 만들어 낼 수 있습니다.

이 방법의 이점은, 포스트 프로세싱 과정에서 처리할 수 있다는 점입니다. 이 장점은은 깊이 버퍼를 텍스쳐 형태로 사용할 수 있는 하드웨어의 경우 기존 엔진에 쉽게 통합될 수 있다는 것을 의미합니다.

그림 27-1 과 그림 27-2 는 모션블러를 사용하고 사용하지 않는 경우 장면이 어떻게 다른게 보이는지 보여주고 있습니다. 27-1 은 모션에 대하여 강하게 잔상을 주었습니다.



















[그림 27-1 ] 모션 블러가 들어간 장면



















[그림 27-2 ] 모션 블러 없는 장면

27-2. 깊이 버퍼로부터 객체 위치 계산하기

객체가 랜더링 되었을 때, 깊이 값이 깊이 버퍼에 쓰여집니다. 깊이 버퍼에 저장된 값은 보간된 z 값으로 삼각형의 세 점으로 보간된 w 값으로 나누어진 값입니다. 이 값은 또한 월드 뷰 프로젝션 행렬에 의해 변형된 값입니다. 텍스쳐의 깊이 버퍼를 사용하여, 우리는 객체의 월드 공간 좌표를 추론할 수 있습니다. 이 좌표는 현재 뷰 프로젝션 행렬의 역행렬로 픽셀의 뷰 포지션을 변형하고 w 를 곱한 값입니다. 뷰포트 공간에 있는 픽셀의 위치를 뷰포트 위치라고 정의하겠습니다. 뷰포트 공간의 x, y 는 -1 에서 1 의 범위에 있는 값으로 원점은 (0, 0) 로 화면의 중심입니다. 픽셀의 깊이 버퍼에 저장된 깊이 값은 z 컴포넌트가 되고 w 컴포넌트는 1 이 됩니다.

우리는 H 픽셀의 뷰포트 공간 좌표를 보여줄 수 있습니다. M 은 월드 뷰 프로젝션 행렬이며, W 는 픽셀의 월드 공간 좌표입니다.

H = (x/w, y/w, z/w, 1)
H x M inverse = wX/wW, wY/wW, wZ/wW, wW = D
W = D/D.w

27-1 에 있는 HLSL/Cg 코드는 위의 공식을 사용하고 있으며, 주어진 픽셀-랜더링된 객체의 좌표-로부터 월드 공간의 좌표를 계산하는 전체 화면 포스트 프로세싱 픽셀 쉐이더입니다. 이 쉐이더 코드는 깊이 버퍼를 사용하고 현재 뷰 프로젝션 행렬의 역행렬을 사용합니다.

예제 27-1. 랜더링된 깊이 버퍼로부터 픽셀당 객체의 월드 공간 좌표를 계산하는 쉐이더 코드

// 현재 픽셀의 깊이 버퍼 값을 가져옵니다.
float zOverW = tex2D(depthTexture, texCoord);
// H 는 -1 에서 1 범위에 있는 픽셀의 뷰포트 좌표 입니다.
float4 H = float4(texCoord.x * 2 - 1, (1 - texCoord.y) * 2 - 1, zOverW, 1);
// 뷰 프로젝션 행렬의 역행렬을 사용하여 변환
float4 D = mul(H, g_ViewProjectionInverseMatrix);
// 월드 좌표를 구하기 위해 w 로 나누어 줍니다.
float4 worldPos = D / D.w

첫 번째로, 우리는 월드 공간의 좌표를 결정하였습니다. 이전 프레임의 뷰 프로젝션 행렬을 사용하여 이 값을 변환할 수 있으며, 스크린 좌표의 차이로 픽셀의 속도를 계산할 수 있습니다. 27-2 는 이 과정을 보여주고 있습니다.

예제 27-2. 픽셀당 속도 벡터를 계산-이미지의 블러 방향을 결정-하는 쉐이더 코드

// 현재 뷰포트 위치
float4 currentPos = H;
// 월드 좌표를 사용합니다. 이전 뷰 프로젝션 행렬에 의해 변형됩니다.
float4 previousPos = mul(worldPos, g_previousViewProjectionMatrix);
// w 로 나누어 비동차(nonhomogeneous) 좌표 [-1, 1] 로 변환
previousPos /= previousPos.w;
// 프레임의 위치와 픽셀에 계산된 마지막 프레임의 속도를 사용합니다. 
float2 velocity = (currentPos - previousPos) / 2.f;

깊이 버퍼를 얻는 방식은 플렛폼과 사용하는 그래픽 API 종속적입니다. 어떻게 깊이 버퍼에 접근할 수 있는가에 대한 자세한 내용은 Cilham 2006 을 참조하세요. 만약 타겟 하드웨어가 깊이 버퍼 텍스쳐를 지원하지 않는다면, 멀티 랜더 타겟을 사용하여 깊이 텍스쳐를 생성하여 분리된 랜더 타겟에 깊이를 출력하거나 색깔의 알파 체널에 깊이 값을 출력해야 할 것입니다.

27.3 모션 블러 실행하기

첫 번째로, 우리는 픽셀의 속도를 가지고 있습니다. 따라서 색깔 버퍼의 속도를 추출하여 모션블러를 할 값들을 누적계산 할 수 있습니다. 27-3 예제는 이 과정을 보여주고 있습니다.

예제 27.3. 다중 시간에 대한 색깔 버퍼로 부터 추출된 현재 픽셀의 속도 백터를 사용하여 모션 블러 효과를 구현한 쉐이더 코드

// 현재 픽셀의 초기 색깔을 구합니다.
float4 color = tex2D(sceneSampler, texCoord);
texCoord += velocity;
for (int i = 1; i < g_numSamples; ++i, texCoord += velocity)
{
    // 속도 벡터로부터 색깔 버퍼를 추출합니다.
    float4 currentColor = tex2D(sceneSampler, texCoord);
    // 색깔을 누적 합산하여 계산합니다.
    color += currentColor;
}
// 모든 추출 값의 평균을 계산하여 최종 블러 색깔을 구합니다.
float4 finalColor = color / numSamples;

그림 27-3 은 이 기술을 최종 적용한 화면입니다. 관찰자와 가까운 지형이 먼 지형보다 더 블러가 된 것을 확인해 보시기 바랍니다.



















[그림 27-3] 전체 화면 모션 블러 효과가 적용된 지형

27.4 동적 객체 제어하기

이 기술은 정적인 객체에게는 완벽하게 동작합니다. 왜냐하면, 카메라로부터 움직인 양을 가지고 계산하기 때문입니다. 하지만, 만약 보다 정확하게 장면에 있는 동적인 객체의 속도를 기록할 필요가 있다면, 분리된 속도 텍스쳐를 생성할 수 있습니다.

그리드 동적 객체들의 속도 텍스쳐를 생성하는 것은, 현재 프레임의 뷰 프로젝션 행렬과 마지막 프레임의 뷰 프로젝션 행렬에 의해 변환되며 포스트 프로세싱 패스에서 같은 방법으로 뷰포트 좌표의 차이를 계산하게 됩니다. 이 속도는 픽셀 쉐이더의 변환 좌표와 속도를 통해 픽셀 당 계산될 것입니다. 이 기술은 DirectX 9 SDK 의 모션 블러 예제에 설명되어 있습니다.(Microsoft 2006)

27.5 객체 마스킹 끄기

응용프로그램에 종속적으로, 모션 블러 효과를 받지 말하야 하는 장면 구성을 위해 객체의 마스크를 끄기를 원하는 상황이 있을 수 있습니다. 예를 들어, 레이싱 게임의 경우, 여러분은 아마도 레이싱 자동차가 블러 없이 또렷하고 디테일하게 보여지기를 원할 것입니다. 이런 상태를 유지하기 위해서 분리된 텍스쳐 혹은 색깔 버퍼의 알파 채널에 마스크를 랜더링 하고 이 마스크를 사용하여 어떤 픽셀에 블러를 줄 것인지 결정하는데 사용할 수 있습니다.

27.6 추가 작업

장면의 깊이 버퍼를 기반으로 장면에 있는 객체의 월드 공간 좌표를 계산하는 이 기술은, 매우 유용합니다. 우리는 이 기술을 사용하여 다른 그래픽 효과를 구현할 수 있습니다: Cilham 2006 에 설명되어 있는 방식으로 DoF(Depth of Field) 는 이 기술을 활용할 수 있는 좋은 효과 입니다. 또한 깊이 버퍼를 사용하여 포스트 프로세싱 단계에서 안개 효과를 구현할 수도 있습니다.

27.7 결론

이번 장에서 우리는 깊이 버퍼에 저장되어 있는 깊이 값을 사용하여 객체의 월드 공간 좌표를 역계산 하는 방법에 대해 이야기하고 어떻게 이 정보를 사용하여 게임 엔진에 모션 블러를 구현할 수 있는지 기초적인 방법에 대해 살펴 보았습니다. 포스트 프로세싱 단계에서 모션블러를 구현하는 것은 전통적인 멀티 패스를 사용한 구현보다 성능적으로 훨씬 좋으며 기존 랜더링 엔진에 통합하기 쉽습니다.

27.8 참조 문서

Gilham, David. 2006. "Real-Time Depth-of-Field Implemented with a Post-Processing Only Technique." In Shader X5, edited by Wolfgang Engel, pp. 163–175. Charles River Media.

Microsoft Corporation. 2006. "DirectX 9.0 Programmer's Reference."

트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://aronze.egloos.com/tb/1378393 [도움말]

핑백

  • CrazyXIII : MotionBlur 2009-09-16 01:36:12 #

    ... 보았음.)Reference는 굉장히 많은데,noerror님이 KASA에서 발표한 모션블러 발표자료GPUGems3권의 MotionBlur As PostProcess(이건 번역글)ShaderX7권에서 소개된 내용.Google에서 "MotionBlur Shader"라고 검색하면, GameDev.net에서 토론된 내용들을 볼 수 있습니다.일 ... more

덧글

덧글 입력 영역