본문 바로가기

유니티 쉐이더 스타트업

[ 유니티 쉐이더 스타트업 ] 01 . 쉐이더와 그래픽스 API 초기화

[ 쉐이더란 ]

[ 쉐이더를 사용하는 이유 ]

https://nitr0.tistory.com/172

마인크래프트의 쉐이더 적용 전 후 모습이다 .

 

마인크래프트의 쉐이더는 그래픽요소를 추가하기 위해 제작된 유저패치이다 .

쉐이더를 통해 빛반사 / 그림자 / 블럭의 모양과 색등을 변형하여

그래픽에 현실성을 입힐수도 있고 ,  분위기를 바꾸어 줄 수 도 있다.

 

이처럼 쉐이더를 통해 개발자는 시각적으로 뛰어난 고성능 그래픽을 다룰수 있는 능력을 갖추어

사용자에게 매력적인 경험을 선사 할 수 있다.

+성능 최적화도 가능하다 .


 

[ 프로그래머를 위한 쉐이더 정의 ]

  • 렌더링 파이프 라인 (GPU를 사용해 리소스를 2D이미지로 렌더링 하는 과정) 변경 가능한 일부를 유연하게 변경하는 프로그램 ( 동작 방식에 의한 정의 )
  • 3D 컴퓨터 그래픽에서 최종적으로 화면에 출력하는 픽셀의 색을 정해주는 함수

 

[ 아티스트를 위한 쉐이더 정의 ]

  • 화면에 색을 칠하는 (Shading)프로그램 ( 목적에 의한 정의 )
  • 그래픽 데이터이 음역과 색상을 계산하여 다양한 재질을 표현하는 방법 

이미지 출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=a2zygote&logNo=220873730654

 


 

 

[ 셰이더와 인쇄소의 동작방식 ]

=>셰이더의 동작 방식은 그림 그리기 / 색칠하기 / 찍기 등의 분업화가 잘 되어있는 중세 인쇄소와 같다.

셰이더와 인쇄소의 공통적인 특성은 다음과 같다 .

  • 이전단계에서 작업물이 나에게 어떻게 도착할지는 통제가 안된다 .
  • 처리할 작업의 개수는 이전단계에서 전달된 작업물의 개수와 일치하다 .
  • 각 단계의 사람은 자기한테 도착한 작업물에 무엇을 할지 (ex .무슨 색으로 색칠할지 ) 스스로 정할 수 있다 .

 

[ 쉐이더 동작 방식 ]

셰이더도 마찬가지이다 .

  • 렌더링 파이프라인에서 개발자가 수정 가능한 부분이 셰이더인것
  • 셰이더는 렌더링 파이프 라인의 일부분으로 이전단계에서 받은 그래픽 데이터를 어떻게 변형하여 전달할지를 정한다 
  • 또한 쉐이더는 입력 받은 그래픽 데이터의 수만큼 반복실행된다 .

 

이런 쉐이더를 알기위해서는 셰이더가 주로 동작하는 렌더링 파이프라인에 대한 이해가 필요하다 . 

그전에 렌더링 파이프 라인의 실행전 CPU에서 처리하는 준비과정

01 . 그래픽스 API 초기화 -  02 . 사용할 데이터 로드 - 03 . 드로우 콜에 관해 알아보자 .

 


 

[ 01 . 그래픽스 API 초기화 ]

[ 그래픽스 API 초기화 ]

=>렌더링 파이프라인이 실행되기전 , 최초 한번 실행되야 하는 과정이다 .

렌더링파이프라인을 구성할수있도록 그래픽스 API에서는 초기화가 이루어진다 .

 

대부분 CPU에서 일어나는 해당 과정은 GPU에게 명령을 전달할 수단을 만들고 사용할 쉐이더를 셋업하는 단계이다 .

다음 3개의 과정으로 이루어진다 .

  • 01 . GPU 디바이스 생성
  • 02 . 커맨드 큐 생성
  • 03 . 렌더링 파이프라인 상태 생성

 


 

 

[ 01 . GPU 디바이스 생성 ]

 

  • GPU 디바이스는 현재 기기의 하드웨어인 GPU를 추상적으로 표현하는 오브젝트 
  • 한번만 생성 , 하나만 존재한다
  • 커멘트 큐와 연결되어 , 커멘드 큐를 통해서 GPU에 전달할 명령을 CPU로부터 받아들인다                                           (GPU 디바이스에게 명령어를 전달함은 실제 GPU에 명령을 전달함을 뜻한다 )

 


 

[ 02 . 커멘드 큐 생성 ]

  • 커멘드큐는 GPU에 전달할 명령 (커멘드)를 쌓아두는 곳이다
  • 한번만 생성 , 하나만 존재한다
  • 커멘드 큐가 필요한 이유는 CPU와 GPU가 직접 통신하지 않기 때문이다 .

(GPU는 수많은 데이터를 빠르게 처리 가능하지만 CPU와 GPU가 직접통신하게 되어 동기화 한다면

GPU는 CPU의 처리가 지연된만큼 대기하며 성능을 낭비한다.)

=>이러한 연유로 ,CPU가 GPU한테 명령을 내릴때 커멘드큐에 쌓아 예약하는 방식을 사용한다 .

 

+ 커멘드 큐의 과정 

=>CPU는 커멘드큐에 커멘드 버퍼를 쌓게 된다 .

커멘드 버퍼는 커멘드를 쌓는 오브젝트이다 .

여기에는 GPU에 전달할 데이터 또는 GPU 실행 명령을 포함한다 .

=>이 과정에서 버퍼에 쌓인 커멘드가 실행되게 되면 커멘드가 품고 있는 데이터는

시스템 메모리인 RAM에서 GPU 전용 메모리 VRAM으로 복제되어 전달된다

RAM 과 VRAM에 관하여

  • 이후 커멘드큐에 쌓인 커멘드 버퍼는 먼저 쌓인 순서대로 GPU가 처리가 가능할때마다 빼간다 .이 과정에서 커멘드들은  GPU가 이해가능한 하드웨어 언어로 번역된다 .
  • 이때 , 커멘드 버퍼/커멘드를 생성하는 행위는 CPU가 명령을 예약하는 행위일뿐 GPU가 해당 명령을 가져가 실행시키기 전까진  해당 명령이 즉시 실행되는건 아니다 .

 

 

[ 03 . 렌더링 파이프라인 상태 생성 ]

  • 렌더링 파이프 라인의 현재 상태를 표현한다
  • 하나의 렌더링 파이프 라인 상태 오브젝트는 대응되는 렌더링 파이프라인이 사용하는 버텍스/프라그먼트 쉐이더는 무엇이고 , 정점은 어떤것으로 구성될지등 현재 사용중 리소스들의 정보테이블과 옵션을 쥐고 있다 .
  • 또한 렌더링 파이프 라인 상태는 복수 생성이 가능하다 .

내부를 살펴보면 렌더링 파이프라인 서술자라는 오브젝트가 존재한다 .

렌더링 파이프 라인의 동작을 묘사하는 오브젝트이다 .

내부 구성은 다음과 같다 .

  • 정점 서술자는 정점의 구성을 표현 , 정점 서술자를 통해 날것의 데이터를 GPU에서 정점 형태로 조립이 가능하다
  • 이외에도 사용할 세이더와 등등의 정보가 들어있다

렌더링 파이프 라인 상태는 복수 생성이 가능하기에 각각 다른 종류의 쉐이더를 가진 렌더링 파이프 라인중 어떠한 렌더링 파이프 라인로 커멘드를 실행할지 결정가능하다

 

렌더링 파이프라인이라는 일련의 처리를 다른 방식으로 구성하고 준비하여 오브젝트마다 다른 렌더링 파이프라인을 적용가능하다는 말이다 .

 

즉, 서로 다른 오브젝트에 서로 다른 쉐이더를 먹인다는 말과 같다 .

여기까지가 그래픽스 API를 초기화 하는 단계이다 .

 

 


 

[ 02 . 사용할 데이터 로드 ]

[ 사용할 데이터 로드 ]

사용할 데이터를 시스템 메모리에 로드하여 그래픽카드로 무엇을 그리려면

그래픽 카드로 전송할 데이터를 메모리에 올려놔야한다 .

 

[ 과정 ]

3개의 정점에 대한 데이터를 가지고 있고 이를 메모리에 올린다 가정해보자

정점은 컬러/위치/노말 등 여러가지 잡다한 속성을 포함해 묶어놓은 구조체이다 .

위치는 정점의 속성중 하나일뿐이다 . (정점 != 위치 )

해당 정점이 가지고 있는 값들을 정점 속성이라고 한다 .

이러한 정점데이터들은 쉐이더안에서는 구조체 형태로 다루지만 커멘드버퍼를 통해서 램에서 GPU로 데이터가 전달시

스트림 형태의 버퍼로 전달된다 .

 

직렬화된 배열로 GPU에 들어오고 ,GPU에서는 이렇게 전달 받은 정점 데이터들을

미리 전달 받은 렌더링 파이프 라인상태의 버텍스 명세를 통해 구조체로 조립한다 .

 

또한 , 데이터의 종류를 기준으로 두개이상의 스트림으로 쪼개서 전송가능하다 .

즉 ,GPU로 구조화 되지 않은 스트림형태로 데이터가 들어오는데

정점 조립단계에서 정점 서술자를 통해 개발자가 원하는 상태의 정점 구조체로 구조화된다 .

 

여기까지의 과정이 그래픽스 API초기화하고 필요한 에셋들을 메모리에 로드하는 과정이다 .

 


 

[ 03 . 드로우 콜 생성 ]

[ 정의 ]

CPU에서 GPU에 전달할 명령을 커맨드 버퍼에 담아 커맨드 큐에 넣는 과정

쉽게 말해, CPU가 GPU에게 메시 좀 그려 달라고 요청하는 것.


 

[ 종류 ]

  • Set Pass Call : 렌더 상태 변경 요청(메시를 그리기 위한 환경 설정)
  • DP Call(Draw Primitive Call) : 메시 그리기 요청

=>일반적으로 Set Pass Call 이후 DP Call로 이어진다.

 

cf ) 배치(Batch)

  • Set Pass Call과 DP Call을 합쳐서 지칭하는 드로우 콜
  • 흔히 말하는 드로우 콜이 바로 배치를 의미한다.

cf ) 배칭(Batching)

  • 여러 번 나누어 발생할 드로우 콜을 하나로 통합하는 것
  • 같은 마테리얼을 사용하는 경우 등등

 

 

[ 예시 ]

  • 기본적으로 드로우콜은 CPU에서 실행되는 단계이다  .
  • 삼각형을 표현하는 데이터 + 삼각형 그리기 위한 명령을 커멘드 버퍼에 추가함
  • 이렇게 삼각형을 그리기 위한 커멘드의 하나의 묶음이 하나의 드로우콜이 된다 .

이러한 커멘드버퍼는 커멘드큐에 쌓이고 GPU에 전달되어 처리 될 것이다

여기까지의 과정이 렌더링 파이프 동작 전( GPU동작전)에 CPU 또는 응용 프로그램에서 처리되는 일들이다 .

 

 

출처

유니티 쉐이더 스타트업

https://rito15.github.io/posts/rendering-pipeline/

https://www.youtube.com/watch?v=aYTJY_DneSY