[클라우디아 도피오슬래시]유니티 물리 기반 셰이더 개발 03
그래픽스 파이프라인
배워야 하는 이유
OpenGL, Metal, Vulkan, Direct3D은 그래픽스 API다. 이는 각기 다른 플랫폼을 기반으로 등장하였고, 밑바닥부터 배우지 않아도 사용이 가능하게 해준다.
소프트웨어 렌더러를 작성하는 것은 좋은 경험이지만 게임제작에 있어서 필수는 아니다. 게임엔진에서 API를 제공해주기 때문이다. 하지만 그래픽스 파이프라인과 그래픽스 API에 대해 알고 있다면, 셰이더를 최적화하는데 많은 도움이 될 것이다.
그래픽스 파이프라인 일반적인 구조
정점지정 -> 정점 셰이더 -> 정점 후처리 -> 기본형 조립 -> 레스터라이저 -> 프레그먼트 셰이더 -> 샘플별 연산
- 정점지정(Input) - 씬으로부터 데이터(메시, 텍스처, 재질)를 수집하고 파이프라인에서 사용할 수 있게 정리
- 정점 셰이더(vertex Shader) - 입력된 정보를 변환 및 기타 계산을 통해 3D 공간에서의 최종 위치를 결정
- 레스터라이저(Rasterization) - 2D 픽셀(프레그먼트)로 변환
- 프레그먼트 쉐이더(Fragment Shader) - 각 픽셀마다 색상을 계산
- 출력(Ountput) - 픽셀들과 결합 되어 최종 이미지를 화면에 출력
레스터라이저
렌더링 파이프라인에서 중요한 부분이다. 백터 이미지를 픽셀 이미지로 변환하는 작업이다.
언릿 셰이더의 구조
언릿 셰이더에는 두 가지의 셰이더 함수가 존재하고 정보 전달을 위한 두 가지의 데이터 구조체를 사용한다. 목표는 그래픽스 파이프라인의 일부분을 스크립트화 하는 것이다.
정점 데이터 appdata 구조를 통해 수집하고 이는 정점 함수로 전달된다. 정점 함수 v2f(vertex to fragment) 데이터 구조 멤버들의 내용을 채우고 이를 프레그먼트 함수의 인자로 전달한다
사물데이터 -> appdata구조체 -> 정점 셰이더 -> v2frnwhcp -> 프로그먼트 셰이더 -> 최종색상
좌표 공간
- 객체 공간(Object Space)
- 월드 공간(Wolrd Space)
- 카메라 공간(Camera Space)
- 절단 공간(Clip Space)
- 정규 디바이스 좌표(Normalized Device Coordinates)
- 스크린 공간(Screen Space)
객체 공간
객체를 중심으로 하는 좌표계
월드 공간
씬을 중심으로 하는 좌표계
공간과 공간 간의 변환
내장 함수
- float3 UnityObjectToWorldDir(in float3 dir) 객체 공간의 방향을 취해 월드 공간에서의 방향 값을 변환해준다.
- float3 UnityObjectToWorldNormal(in float3 norm) 객체 공간의 노멀을 취해 월드공간에서의 노멀 값으로 변환해준다. 빛을 계산하는데 유용하다.
- float3 UnityWorldSpaceViewDir(in float3 worldPos) 월드 공간에서의 정점 위치를 취해 월드 공간에서의 뷰의 방향을 반환한다. 빛을 계산하는데 유용하다.
- float3 UnityWorldSpaceLightDir(in float3 worldPos) 월드 공간에서의 정점 위치를 취해 월드 공간에서의 빛의 방향을 반환한다. 빛을 계싼하는데 유용하다.
어떤 좌표 공간을 다른 좌표 공간으로 변환하는 데에는 특정 행렬
을 사용한다.
다음은 객체 공간에서의 변환을 위한 몇 가지 내장 유니티 행렬이다.
내장 행렬
- unity_ObjectToWorld 객체 공간에서 월드 공간으로 변환하는 행렬
- unity_WolrdToObject 위의 행렬의 역행렬. 월드 공간에서 객체 공간으로 변환하는 행렬
객체 공간상의 정점의 위치를 월드 공간 기준으로 변형
float4 vertexWorld = mul(unity_ObjectToWold, v.vertex);
카메라 공간
시야 공간(Eye Space)혹은 뷰 공간(View Space)이라고도 한다. 카메라 공간은 월드 공간 좌표 체계와 동일한 씬을 담는다. 하지만 렌더링 시발점인 카메라 시점으로 씬을 담는다.
내장 행렬
- unity_WorldToCamera 월드 공간에서 카메라 공간으로 변환
- unity_CameraToWorld 위 행렬의 역행렬.
내장 함수
- float4 UnityViewToClipPos(in float3 Pos) 뷰 공간에서 절단 공간으로 위치를 변환한다.
절단 공간
렌더링 파이프라인의 정점 후처리 단계에는 절단(Clipping)를 포함한다. 카메라 공간에 보이지 않는 부분을 다 삭제하는 것. 범위는 -1부터 1까지 이다.
좌표 공간에는 3개의 좌표 축이 있을 것이라고 생각할 것이다(x, y, z축). 하지만 OpenGL 및 다른 API 에서 모두 4개를 사용한다(x, y, z, w).
3D렌더링에 카테시안 공간을 그대로 사용하면 문제가 발생한다. 두 평행선은 서로 만날 수 없기 때문에 원근법을 표현하기가 불가능한 것이다.
이 문제를 해결하기 위해 좌표 하나를 추가했다(w). 이것은 동차 좌표(homogeneous coordinates)라고 한다. 절단 공간에서 정규 기기 좌표로 가는 단계에 w 값으로 다른 값을 모두 나눌 것이다. 이 과정에서 원근법을 표현한다. 객체, 월드, 뷰, 절단 공간은 4차원 좌표를 사용해 표현한다. 절단 공간을 제외하고 w는 1이다.
뷰 공간에서 절단 공간으로 변환하는 데 사용하는 행렬에 의해 w값은 1이 아닌 다른 수로 바뀐다. 이 행렬을 OpenGL에서는 투영 행렬(Projection matrix)이라고 부른다. 이 행렬을 설정하기 위해 시야 부피(절경frustum 이라고도 한다)와 관련된 정보가 필요하다.
일반적으로 사용하는 투영법에는 원근투 투영(perspective projection)과 정사영(orthographic projection)이 있다. 원근 투영에서의 절경은 근평면(a near plane)과 원평면(a fal plane)으로 구성돼 있다.
내장 함수
- float4 UnityWorldToClipPos(in float3 pos) 위치를 월드 공간에서 절단 공간으로 변환
- float4 UnityViewToClipPos(in float3 pos) 위치를 뷰 공간에서 절단 공간으로 변환
- float4 UnityObjectToClipPos(in float3 pos) 위치를 객체 공간에서 절단 공간으로 변환
내장 행렬은 존재하지 않는다.
정규 기기 좌표
정기 기기 좌표(Normalized Device Coordinates, NDC)이다. 이것은 2D 공간이며, NDC 기준 좌표는 절단 좌표를 w로 나눠서 얻는다. 이 과정을 원근 분할(perspective division)이라고 부른다.
화면 공간
최종 단계이다. 이 좌표가 레스터라이저로 전달되고 프레그먼트를 생성한다.