센로그

9. Output-Merging 본문

게임/게임 그래픽 프로그래밍

9. Output-Merging

seeyoun 2023. 5. 28. 01:30

◆ Output-Merger Stage

최종적으로 스크린에 한번에 보여질 color들을 결정하는 스테이지.

Pixel Shader의 output으로써, pixel들이 넘어왔을 것임.

이제 실제 스크린에 보여줄 칼라를 결정해야 한다.

 

이전에는 오브젝트 각각의 칼라를 결정해 준 것이고,

이번에는 오브젝트들의 칼라를 모아서 최종적으로 스크린에 한번에 보여질 칼라를 결정하는 것.

이 픽셀을 찍을건지 말건지, 그냥 보일건지 변형시킬건지.. 이런거 결정.

 

Output-merger stage에서는 다음 두가지 처리를 수행함

  • Depth-Stencil Test
  • Color Blending

 

또, 다음 요소들을 고려해서 최종 칼라 값을 결정함.

1) a combination of pipeline state,
2) the pixel data generated by the pixel shaders,
3) the contents of the render targets,
4) and the contents of the depth/stencil buffers.

최종으로 렌더링 할건데, 너 픽셀 셰이더에서 나온 컬러 쓸거야? 아니면 걍 대충 정해놓은 거 쓸래? 이런 것들.

 

어떤건지 대충 살펴보자.

 


Pipeline State

Direct3D에서는 PSO 구조체

하드웨어 세팅에 관한 이야기.

 

CPU에서 GPU로 데이터를 넘길 때, 같이 넘기는 옵션하드웨어 세팅이라고 함

GPU가 input data를 어떻게 interpret하고 process할지에 대한 옵션! (어떤 데이터니까 어떻게 처리해라~하는 옵션)

이 옵션에는 primitive type 이외에도, rasterizer state, blend state, depth-stencil state와 같은 일반적인 설정이 포함됨.

 

Direct3D 12에서는, Pipeline State 해당하는 친구들을 PSO(Pipeline State Object)에 넣어서 전달해줌.

초기화 시간에 PSO가 여러개 생성되기도 하는데, object에 따라 다른 세팅으로 처리할 수도 있기 때문임.

렌더링 할 때 다른 PSO로 전환해서 렌더링 할 수 있다는 뜻.

 

D3D12_GRAPHICS_PIPELINE_STATE_DESC 구조체에서 모든 pipeline state들을 제시함.

 


Render Target

Drawing Command를 받는, 도화지의 개념. buffer.

Render Target이란, Drawing Command를 받아들이는 친구(resource or object)를 의미함.

우리가 어떤 도화지 위에 그림을 그릴거야. 이걸 컴퓨터 모니터 스크린에 박아넣는다 할 때, 이 도화지를 박아넣는거야.

이 도화지가 '렌더 타겟'인 것

 

렌더 타겟을 사용한다는 건, temporary intermediate buffer로써 이 렌더타겟을 사용한다는 것.

최종적으로 그릴 buffer에 바로 그리는 게 아니라, 임시 메모리 버퍼에 담아두고 바꾸는 것.

 

게임 1프레임에서 2프레임 넘어갈 때 새로 그려야하는데, 그리는 과정이 우리 눈에 들어올 수도 있음!

따라서, 현재 보여주는 도화지(front buffer) 이외에 별도의 도화지(back buffer)를 하나 더 써서, 두 개를 바꾸는 것

 

이외에도, 또다른 별도의 렌더 타겟을 만들어서 버퍼 두개를 합쳐서 그린다거나 할 수도 있다.

씬을 그릴때 RGB값을 많이 그리는데, 내가 갑자기 depth값을 얻고싶어! 그러면 렌더 타겟1에다간 RGB를 리턴하고, 렌더타겟2에는 depth리턴해서, 1을 활용해 빛 계산, 2를 활용해 깊이 계산할 때 쓸 수 있다.

정보를 추가로 다양하게 담아놓고 쓸 수있다 뜻~

 

■ Frame buffer

RAM의 한 부분으로, 화면에 그려질 화면 전체에 대한 color data를 담는 메모리 공간.

스크린에 보여질 영역을 잠시 보관하고 있는 버퍼

 

 

■ Back buffer

현재 스크린에 보여주고 있는 frame buffer을 front buffer(primary buffer)라고 부르고,

그 다음에 보여주려고 준비해놓은 frame buffer을 back buffer(secondary buffer)이라고 부름.

 


Depth-Stencil Testing

Depth test + Stencil test를 의미함.

depth-stencil buffer depth 데이터stencil 데이터를 동시에 수용할 수 있는 구조체.

텍스처 리소스를 사용해서 정의하는 버퍼이다.

 

  • depth 데이터: '어떤 픽셀이 카메라에 가장 가깝게 있느냐'를 결정할 때 사용
    카메라로부터의 거리를 사용해, 더 앞에있는 의 픽셀을 렌더링한다.

  • stencil 데이터: '이 픽셀을 업데이트할까, 말까?'를 결정할 때 사용함.
    front buffer을 갈아치울거야! 근데, 이 부분만 업데이트할거야. 라는 느낌. 
    그림에서 1로 된 부분만 렌더링함.

 

OM에서 이 두 데이터를 사용해 어느 픽셀을 그릴  것인지 결정함.

모든 프레임을 그대로 쓰는 게 아니라, 마스크(Mask)를 사용해서 업데이트 되는 영역만 그릴 수 있음.

 

Direct3D 12에서는 D3D12_DEPTH_STENCIL_VIEW_DESC라는 구조체를 사용해 이를 표현함.

PSO안에 포함해서 넘겨줌.

Format은, 텍스처의 형태를 의미. RGBA 쓸거야? FLOAT야? UINT야? 이런 것.

ViewDimension은, 텍스처를 어떤 Dimension으로 다룰 것인지에 관한 설정.

 


Depth Test

어떤 픽셀이 더 앞에 있는지 결정해서 그리는 걸 depth buffering 또는 z-buffering이라고 부름.
depth buffer 초기값MaxDepth

 

이전 스테이지에서, RGB값 이외에, 보간된 z값(depth 값)들도 OM에 전달된다.

depth 값들이 OM 에 도달하게 되면, 아래와 같이 Viewport 내부로 Clamp(고정)된다.

즉, depth값Viewport의 최소 depth보다는 크게, 최대 depth보다는 작게 설정한다는 것이다.

 

clamping 다음에, 현재 존재하는 depth 버퍼와 이번에 들어온 depth value를 비교한다.

< 방법 >

우선 depth test는 per-sample operation이다. 64*64 해상도 쓰면 하나씩 다 체크한다는 것.

어떤 픽셀이 보이고, 어떤 픽셀이 가려져 있는지 체크한다.

 

이를 위해 이번에 내가 projection하는 픽셀의 depth값과, 현재 depth buffer에 저장되어있는 depth값을 비교한다.

픽셀의 depth값 < 현재 depth buffe의 depth값인 경우, 이번 pixel을 앞에다 렌더링해준다.

그리고 이번 pixel의 color, depth값으로 color buffer, depth buffer을 각각 업데이트 해준다.

반대의 경우, draw하지 않고 그대로 냅둔다.

※ 엄청 비슷하게 있으면, 연산에 따라 앞뒤가 계속 바뀔 수 있다.. unity에서 겹쳐있으면 앞뒤로 진동하는 현상.

 

예시) 

빨간 삼각형 depth를 0.8, 파란 삼각형 depth를 0.5라 하자.

z-buffer은 MaxDepth인 1로 초기화되어있음.

 

1) 빨간 삼각형을 먼저 넣는 경우

파란 삼각형 넣었을 때, 빨간 삼각형보다 depth 값이 작으므로 depth 및 color 업데이트해줌.

 

2) 파란 삼각형을 먼저 넣는 경우

빨간 삼각형 넣었을 때, 파란 삼각형보다 depth 값이 크므로 그대로 유지함.

단, 파란 삼각형이랑 겹치지 않는 부분은, 원래 초기화된 값인 1보다 빨간 삼각형의 depth 0.8이 더 작으므로 업데이트해줌.

 

결국 두 경우가 똑같이 렌더링되는 걸 확인할 수 있다~

 

 


Stencil Test

업데이트 되는 영역(Mask)만 설정해 그려줌으로써, 자원을 아끼면서 다양한 효과를 낼 수 있음.

앞서 언급했듯이, Mask를 사용해, 1로 되어있는 부분만 업데이트함!

 

Stencil Test를 사용해, 자원을 많이 아끼면서 다양한 효과를 낼 수 있음.

예를들면 어떤 효과를 낼 수 있냐!

 

1) Transition effect

ppt 전환 효과 느낌~

 

 

2) Discarding UI area

UI가 가리는 부분은 업데이트 안하도록 할 수도 있음. 옛날에 많이 씀

요새는 투명 UI도 많은데, 투명한 경우에는 이런 방법 못 씀ㅇㅇ

 


Color Blending

object에서 투명한 부분을 고려해 색 결정해주는 것.

 

OM에서는 depth-stencil test 이외에 Color Blending이라는 것도 한다고 했다.

 

유리로 된 사과 뒤에 오렌지가 있으면, 오렌지의 색깔이 투명 사과에 비춰서 보여야 할 것임.

Color Blending에서 이런 부분을 처리해줌.

 

즉, translucent한 어떤 pixel들은 유리나 수면을 통해 조금은 보여야하는 경우가 있음. (translucent. 빛이 완전히 투과 X)

=> 블렌딩을 통해, 몇 가지 픽셀 값들을 결합해서 최종 칼라값을 결정해줌.

 

기억할 것은, output value가 render target에 들어가기 전에, blending 연산이 픽셀마다 모두 처리된다는 것이다.

 

 

Color Blending 처리시 사용되는 요소들에 대해 알아보자.

■ alpha value 

Blending 처리시 fragment의 alpha 을 사용함. alpha 값은 투명도를 나타냄.

alpha 값 범위가 [0, 255]일 때, [0: 완전 투명(transparent), 255: 완전 불투명(opaque)]

실제로 투명도 표현 시에는, 정규화된 [0, 1]범위를 더 많이 씀.

 

■ The blending equation

c_srcpixel shader의 output이고, c_dstback buffer의 color라고 하자. (src = source, dst = destination)

만약 블렌딩을 안할거야! 그러면 그냥 c_dst = c_src 로 갖다넣으면 됨.

 

근데 블렌딩 할거면, c_src와 c_dst를 블렌드해서 c로 합쳐줘야 함.

blending factor f라는 개념을 도입할건데, f_src와 f_dst가 있다 하자.

이때 블렌딩 팩터와 컬러를 사용해 어떤 연산 ▣을 통해 블렌딩을 처리해 c를 결정함. 이 연산은 우리맘대로 정할 수 있음.

The blend operation

Direct3D 12에서, D3D12_BLEND_OP이라는 구조체 안에 다음과 같은 사용할 수 있는 ▣ 연산들이 정의됨. 

블렌딩을 어떻게 수행할지를 CPU에서 Description(PSO 안의 D3D12_BLEND_DESC)을 통해 넘겨줌 ㅇㅇ.

 

 The blend factors

blend factor은 D3D12_BLEND 구조체로 정의됨

얘는 옵션이 참 많음.. 많이 쓰이는 몇가지만 보자.

f를 저런 애들로 선택하겠다는 뜻.

 

Blend State

Direct3D 12에서, 블렌딩에 관한 정보들(연산, 블렌딩 펙터..)은, PSO의 D3D_BLEND_DESC에다 넣어서 최종적으로 정의함.

 

디폴트로는 블렌딩이 disable 되어있으므로 하고싶으면 켜주고, 블렌드 스테이트를 잘 정의해주면 됨.

블렌드 스테이트를 만드는 예시~

내가 이 시스템에서 사용할 세팅 같은 걸 정의해주는 것.

D3D12_GRAPHICS_PIPELINE_STATE_DESC samplePsoDesc;
ZeroMemory(& samplePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); 
D3D12_RENDER_TARGET_BLEND_DESC sampleBlendDesc;
sampleBlendDesc.BlendEnable = true;
sampleBlendDesc.LogicOpEnable = false;
sampleBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
sampleBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
sampleBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
sampleBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
sampleBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
sampleBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
sampleBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
sampleBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
samplePsoDesc.BlendState.RenderTarget[0] = sampleBlendDesc;

 


 Color Blending - 세팅 예시

많이 쓰이는 예시들을 좀 살펴보자.

여기 들어갈 factor들과, 연산을 선택하는 방법에 따른 예시.

 

1) Keeping destination pixel

새로 들어오는 c_src와 상관없이, 항상 c_dst를 유지하고자 하는 경우 (UI 따위에 사용)

 

  • f_src = D3D12_BLEND_ZERO
  • f_dst = D3D12_BLEND_ONE
  • Blend Operation = D3D12_BLEND_OP_ADD

이렇게 설정하고 계산해보자!

 

 

2) Alpha Blending

source의 alpha 값 α_s을 통해 source pixel이 몇 퍼센트 정도를 통과하도록 할 것인지 결정.

일반적으로 가장 많이 생각하는 '블렌딩'시 하는 작업.

 

  • f_src = D3D12_BLEND_SRC_ALPHA
  • f_dst = D3D12_BLEND_INV_SRC_ALPHA
  • Blend Operator = D3D12_BLEND_OP_ADD

이렇게 설정하고 계산해보자!

 

※ 실제로 Alpha Blending이 어떻게 동작하는지 보자.

1. 우선, 전부 불투명한 primitive들을 먼저 렌더링해준다.

2. 그다음에, 불투명하지 않은 친구들을 렌더링해준다. 이때 순서(order)가 있다는 점이 중요하다!

  • back-to-front order : 멀리 있는 ~> 가까이 있는 순서로 렌더링함 (보통 이거 씀)
  • front-to-back order : 가까이 있는 ~> 멀리 있는 순서로 렌더링함

2번 처리시, 1번 친구들도 고려해줘야한다는 점 기억.

만약 불투명하지 않은 애들이 불투명한 애보다 뒤에 있다면, 렌더링 안해줘도 됨!!

 

그림은 back-to-front order로 처리한 경우이다.

멀리있는 빨간 삼각형부터 그린다.

파란 삼각형이 들어올 때, α_src = 0.5이다.

따라서 최종적으로 c = 0.5*(0, 0, 1) + (1-0.5)*(1, 0, 0) = (0.5, 0, 0.5)가 결정된다.

 

 

▽ order에 따라 다른 결과값이 나오니, order을 지켜주는 게 중요하다!

 

'게임 > 게임 그래픽 프로그래밍' 카테고리의 다른 글

11. Screen-space Object Manipulation  (0) 2023.05.28
10. Euler transforms and quaternions  (0) 2023.05.28
8. Lighting  (0) 2023.05.27
7. Image Texturing  (0) 2023.05.26
6. Rasterization  (0) 2023.05.13
Comments