본문 바로가기
DirectX 12/Rendering

[DirectX12 - Rendering] Chapter 02. Render Target

by song.ift 2023. 6. 26.

GitHubhttps://github.com/developeSHG/DirectX12-Rendering/commits/02.Render_Target

 

GitHub - developeSHG/DirectX12-Rendering: DirectX12 - Rendering

DirectX12 - Rendering. Contribute to developeSHG/DirectX12-Rendering development by creating an account on GitHub.

github.com

 

결과물

 

 


 

 

리가 여태 사용하고 있던 쉐이더 방식은 forward 쉐이더 였다.


우리가 지금까지 한 렌더링 파이프라인은
물체를 그릴 때, 버텍스와 인덱스 정보를 넣어주고
쉐이더를 통해서 어떤 일을 해야할지 기술해준 다음에
버텍스 쉐이더와 픽셀 쉐이더의 연산을 거쳐 결과물을 출력해주고 있었다.

위의 출력해주는 부분은 렌더링 파이프라인 마지막 단계인 Output merger에서
연산된 결과물이란 걸 끄집어내서 RTV(Render Target Views)에 그려주고 있었다. (최종결과물의 도면)

지금까지 스왑체인 만들어 준 2개의 버퍼(백버퍼, 프론트버퍼)를 번갈아가면서 왔다갔다하는데
후면버퍼(백)에다 그려달라고 요청하고 있었다.
그 작업들이 우리가 작성한
C++과 픽셀 쉐이더와 같이 맞물려서 OM 단계에서 최종적으로 연산한 색상 자체를 지정한 렌더타겟에 그려주고 있었던 것이다.

 




여기서 아쉬운 부분은 예전에 고전적으로 사용하던 게임들. 2000년대 게임들은 다 이런 포워드 쉐이더를 사용하고 있었다.
근데 이 방식에서 이렇게 아쉬운건 우리가 열심히 만들어준 중간 부분들이 다 날라간다는 문제가 있다.

픽쉘 쉐이더에서 결과물을 도출하기까지 중간 과정들을 보면 
여태까지 연산을 했을 때 사용하기위해 알았던 정보(노말정보, 뷰 스페이스 포지션 등등)
그런 것들을 다 만들고 빛 연산을 해줬을텐데, 
그 연산의 최종 결과물은 return color; 와 같이 float4짜리만 도출하고있다.

그렇다는 것은 color 색상을 제외한 중간계산한 정보들은 다 소실된다고 보면 된다.
그것을 대항하기 위한 게 디퍼드 쉐이더다.

 




기본적은 개념은 그렇고, 렌더타겟이란 거에 대해 알아보자
렌더타겟이란, 최종으로 쉐이더 그려준 결과물을 어디에 그릴것인가를 얘기하고있는것이다.
"어떤 대상한테 그릴것이냐"

근데 화면에다 그릴 뿐 아니라, 별도의 텍스쳐를 하나 더 만들어서 그 버퍼에다 그려다주는 경우가 많다.
보여주고 있는 카메라안에 cctv 같은 컨텐츠를 보여준다고 했을 때 생각해보면 된다.
카메라가 찍고 있는 그 화면 자체를 우리는 전체 화면에다 보여주고 있었지만, 그게 아니라
중간 단계를 거쳐서 어떤 텍스쳐라는 버퍼에다가 그 화면 결과를 보여준 다음에, 
실질적으로 테레비전에는 우리가 그려준 텍스쳐 자체를 그려주면 된다.
(유니티에선 카메라 컴포넌트에 target texture를 지정할 수 있따. -> 카메라가 찍는 것을 화면에다 보여주는 것이 아닌, 텍스쳐에다 최종 결과를 보여달라. 다른데서 또 활용가능하다)



결국에는 우리가 지금까지 포워드 쉐이더 방식에는 낭비가 심한 문제가 있다.
빛에 대한 정보, 뷰 등등 그런 정보를 연산 후에 최종 결과물 color를 제외한 나머지를 다 버린다.
또한, 포워드 쉐이더에서의 빛 연산의 속도가 느리다는 단점이 있다.
모든 정보를 글로벌 버퍼에다가 담았다가 for문으로 하나하나 순차적으로 연산해주는 경우가 느리다. (한땀한땀)

diretional List는 모든 공간의 한 방향이라 다 픽쉘을 연산해줘야 겠지만
point랑 spot는 특정 범위를 제외한 곳은 연산을 안해줘도 되는데, 멀리 떨어진 곳에도 굳이 빛 연산을 해야한다는 게 연산 낭비가 있었다.
하지만, 포워드 쉐이더에선 딱히 그게 영향을 받을지 안받을지 알길이 없었기때문에 그렇게 할 수 밖에 없었다.



그래서 결국에는 디퍼드 렌더랑이 2010년대부터 유행하기 시작헀고,
포워드는 제한적인 사항 예를 들면 ui 출력할때나 사용하고, 왠만해선 디퍼드를 사용한다.

그럼 디퍼드는 뭐가 달라지냐?
포워드처럼 모든 정보(노말, 좌표 등등)들을 연산해서 한번의 색상만을 내뱉어주는게 아니라,
중간에 필요한 정보들을 다 저장한다는 특징이 있다. (재사용성 가능)
데이터가  유실되지 않고, 보존된다는 것이다.

포워드 쉐이더는 VS -> GS -> PS -> 최종으로 RenderTarget까지 순차적으로 진행이 되었는데,
디퍼드 쉐이더는 VS -> GS -> PS -> Multiple Render target[Depth, Normals, Color] -> Final Render Target 순이다.
디퍼드는 픽쉘 쉐이더에서 빛과 관련된 연산들을 당장하지않고, 보류를 해주게 된다.
(렌더타겟이란게 결국 텍스쳐임)
픽쉘 쉐이더에서는 중간에 보존할만한 가치가 있는 데이터들을 렌더타겟에다 그려주게 된다. 그게 [Depth, Normals, Color] 라는 각각의 렌더타겟의 텍스쳐.
( [Depth, Normals, Color] 이런 것들을 렌더타겟들을 묶어서 관리한다해서 MRT(Multiple Render target)이라 한다)
그다음 최종적으로 모든 정보를 합쳐셔 Final Render Target외 최종 결과물을 만든다.
이 방식의 가장 큰 장점은 중간에 계산된 결과물에서 어떤 물체에 대한 정보를 또 텍스쳐다에다 기입하게 되면
해당 픽셀에 대해서 물체가 있는지 없는지 판별할 수 있게 된다. 그걸 이용해서 빛 연산을 한다닌 식으로 수정할수도있고,
다양한 방법에서 응용할 수도 있다. 라이팅 기법이 노말 정보를 필요하게 되면 노말 텍스쳐랑 라이팅 연산을 해서
파이널 런더타켓에 적용할 수도 있다.

이렇게 중간에 필요한 부품들을 조립해서 편한대로 만들어주게 된다는 점이다.
이렇게 하지않고, 예전 포워드 같았으면 중간에 있던 계산 과정들이 손실되었다보니까 만약에 새로운 기법으로 
특수 이펙트를 만들고 싶었는데 하필 노말정보를 필요하기 위해 다시 처음부터 돌려야된다는 점이 있을수도 있다는 것이다.

정리해서 얘기하자면
다중으로 텍스쳐를 준비한 다음에, 각각의 해당하는 결과물을 gpu에 그려달라는 요청하는 것이다.
당장 출력된 화면을 만드는 게 아닌, 렌더타겟에다가 텍스쳐에다가 중간 정보를 저장해고, 최종 결과물을 그리는 것이다.

 



swapchain은 back_buffer와 front_buffer를 얘기하고, 후면 버퍼에 항상 그려주면서 교체해준다

지오메트리 버퍼 (POSITION, NORMAL, COLOR)
이것은 위에 여태 얘기한 최종 렌더타겟을 만들기 위한 각각의 텍스쳐를 저장하는 과정.
기하학적인 정보를 텍스쳐에다가 기입한 다음에, 그걸 나중에 최종적으로 조립해서 렌더링하는게 디퍼드 렌더링이라 했다.
그걸 준비하기 위한 정보가 지오메트리 버퍼

깊이 텍스쳐는 따로 저장. (조합X)
렌더링 파이프라인을 할 때, DepthStencill버퍼(깊이 버퍼)를 참조해서 실제로 그려줄지 말지를 결정했었다.
당연히 렌더타겟을 조립해서 만드는 건데, 깊이 버퍼는 공용으로 사용하는 셈 (어느 렌더타겟은 안보여주고 누구는 보여주고 할 수 없는 개념이니까)



지금까지 우리는 항상 화면을 그리는 것만 생각해서 
렌더타겟 == 화면
으로 생각했겠지만, 그게 아니였다.
텍스쳐를 만든 후, 보내준다음에 쉐이더에서 연산된 결과물을 우리 텍스쳐에다가 그려주고
그 텍스쳐를 다시 리소스나 ui로 꽂아줘서 만들수있따는 사실이다.
(그래서 cctv도 가능하다는 거. 굉장히 다양하게 응용가능)

 

댓글