유니티2022. 4. 13. 21:19

0.지난주차 보충

 

0-1. Geometry Node의 기초

 

Geometry에서 꼭 알고 넘어가야할 노드는 아래와 같다.

 

 

Position

픽셀의 위치 값을 출력한다.

Object, View, World, Tangent 등의 공간을 선택할 수 있다.

World와 Absolute World의 차이는 Absolute World는 모든 SRP에서 절대적인 월드값을 의미한다.

World는 렌더 파이프라인에 따라서 의미가 달라지는데

HDRP에서는 Camera Relative가 되고 URP에서는 Absolute World가 된다.

< Position 노드의 이해를 위한 셰이더 그래프, 큐브의 좌표는 ( 0, 0.523, 0 ) >
< World 기준, 월드 좌표를 기준으로 출력된다. >
< object 기준 >
< View 기준, Z값에 대해서는 전부 0으로 반횐되었다. >

Screen Position

Mesh Vertex 나 Fragment의 Screen Position에 접근할수 있도록 하는 노드이다.

출력값은 Mode 파라메터에 따라 다른데 Default, Raw, Center, Tiled중 선택할 수 있다.

 

Default

< R,G가 아닌 B, A는 검정색으로만 출력되었다. >

Raw

< R,G가 아닌 B는 검적색으로만(0), A는 하얀색(1)으로만 출력되었다. >

Default와 달리 Screen Position을 space position W 컴포넨트(?)로 나누지 않는다고 한다.

 

Center

스크린의 중심의 위치 값이 float2(0,0)으로 출력된다.

Tiled

Center 처럼 화면의 중심이 float2(0,0)이 되고 Fraction노드를 사용한것 같이 타일링 된다.

 

UV

 

UV값을 출력하는 노드이다. 사용된 셰이더 그래프는 최상단예시에서 Position노드만 UV로 변경하였다. 

< UV의 U값을 시각화 한 모습 >
<이펙트용 폴리곤을 V값으로 시각화>

Vertex Color

Mesh Vertex나 Fragment의 Vertex Color에 관여할수 있도록 하는 노드이다.

사용된 셰이더 그래프는 Position 예시에서 Position노드를 Vertex Color노드로 변경하였다.

< vector 4로 출력됨 >

다음과 같은 버텍스 페인팅으로 제작된 폴리곤이 아래와 같이 존재한다 가정하자.

< 1만폴리곤짜리! >

이를 각각 R,G,B,A 값만 출력한 형태이다.

 

Normal Vector

Mesh Vertex나 Fragment의 노멀 벡터에 관여할수 있도록 하는 노드이다.

사용된 셰이더 그래프는 최상단예시에서 Position노드만 Normal Vector 노드로 변경하였다. 

 

모든 픽셀에 노멀방향이 존재한다.

<(1,0,0) 인 벡터를 가진 면을 시각화 >
< 월드 기준에 X방향만 보기에 돌리면 달라진다 )
< Object 기준의 x >

 

<View 좌표계 일때의 노멀>

0-2 Z-test, Z-write?

Z-test (= Z-Read)

   현재 그리려는 픽셀을 기존의 Z-Buffer 내용과 비교하는 것

Z-Write

   그려도 되는 픽셀이면 Z-Buffer에 현재 거리 내용을 기록하는 것

 

 

 

 

 

1-1 반투명 오브젝트의 Sorting

 

유니티(레거시)방식 - 2Pass로 첫 패스에는 Z-Buffer만 기록, 2번째 패스에서 최종 렌더링_참고링크

언리얼 방식 - 2Pass로 안되다보니 안보이는 메시를 스폰해서 겹쳐서 depth만 처리하는 식으로 처리

더보기

Sorting이 중요한 이유

아래는 투명과 반투명이 섞인 모델링을 가져와 단순히 Surface Type을 trasparent로 설정했을때 생길수 있는 모습

z-test만 진행하고 버퍼에는 기록을 안해서 생긴 문제이다.

< 고글만을 위해 투명 설정을 했다 난리가 나버렸다! >

 

1-2. Early-Z

 

그래픽 파이프라인을 보면 Fragment Shader (Pixel-Shader)이후 Output-Merger에서 Depth Test를 수행한다.

Fragment Shader 연산 이후 그릴지 말지 검사하므로 오버드로우가 발생한다.

이런 비효율을 개선하기 위해 Fragment shader 이전에 미리 Depth Test를 위해 가려진 Fragment는 Fragment Shader를

수행하지 않는다. (실제 작동은 다양한 하드웨어 특성에 따라 달라질 수 있다.)

요즘 대부분의 GPU는 early depth testing이라는 하드웨어 기능을 지원하는데,

Early depth testing은 FS이전에 뎁스 테스트를 수행한다.

제한사항 - FS에서는 깊이 갚에 쓰기를 하지 말아야 Early Depth Testing이 유효함

 

Unity URP 12.0.0이상 버젼 부터는 Unity Depth Prepass( Depth Priming Mode)라는게있다.

Asset > Rendering > Rendering Path, Forward > Depth Priming Mode

보이는 모든 Opaque 메시들은 FS 비용없이 Depth Buffer에 그려넣어지는 패스로

이후 모든 컬러 패스들은 이 버퍼를 재활용한다.

URP 12 이전에는 URP 렌더 파이프라인 세팅의 Depth Texture 옵션에 의해 간접적으로 활성화 되던 기능이다.

다양한 하드웨어 상황들이 있는만큼 여러 예외처리들이 있고 최적화를 할수있도록 URP엔진이 최선을 다하고 있다고

이런 기능들을 예시로 이해하면 좋을것 같다.

 

쨋튼, Early-Z와 SRP의 Depth Prepass는 같은 목적의 기능이다.

 

1-3. Render Queue

반투명이 그려진뒤에 불투명이 그려지면 반투명에는 z-buffer가 존재하지 않아 문제가 발생한다.

따라서 유니티에서는 불투명의 기본 렌더 큐 값을 2000으로 먼저 그려지고, 반투명의 기본 렌더 큐 값은 3000으로

나중에 그려지도록 되어있다.

 

즉 렌더큐는 언제그려질지 정해주는 번호이고, 적은 수일수록 빨리 그려진다.

 

유니티 Material의 Advance Option에서 Sorting Priority(Render Queue)를 통해서

기본으로 정해진 렌더큐 값에 ± x하여 렌더링될 순서를 조정할 수 있다.

커스텀 셰이더에서는 수동으로 설정할 수 있다.

 

Background - 1000

Geometry - 2000. Default

AlphaTest - 2450

Transparent - 3000

Overlay - 4000 (GUI나 렌즈 플레어 등에 사용)

 

커스텀 셰이더에서의 사용 예는 아래와 같다.

Tags { "Queue" = "Transparent" }

Tags { "Queue" = "Geometry+1" }

Tags { "Queue" = "2001" }

렌더 큐를 제어하는 방법은 아래와 같다.

다만 커스텀 셰이더 재질의 렌더큐를 수동으로 변경(직접 숫자로 입력)하는 것은 비추천한다.

재질에서 셰이더를 변경하는 순간 렌더큐는 셰이더 디폴트값으로 리셋되는데,

이를 인지하기도 어렵고 리셋되기 전의 번호도 알기 어렵다.

셰이더별 렌더큐를 정하면 프로젝트에서 일괄적으로 해당 셰이더의 렌더큐를 변경하기 좋다.

"Queue" = "Transparent+1" (덧셈 좌우로 띄어쓰기 X )

게임 지면에 그려지는 반투명 UI같은 경우 강제로 렌더큐를 지정해야하는 경우가 있다.

 

게임 시스템의 복잡한 사정에 의해 강제로 그리는 순서를 지정하는 경우가 있다.

개발하는 도중 혹은 서비스 중에 특정 요소의 렌더 큐 번호를 일괄 변경해야할 경우 셰이더에 의해 렌더큐 번호가

결정되는 구조는 셰이더 코드 변경으로 일관 세팅이 가능하여 관리 이슈를 피할 수 있다.

 

2-1 알파 sorting

Blending Mode가 Add나 Multiply가 되는 반투명 오브젝트라면 렌더되는 순서가 상관없지만 Alpha라면 문제가 된다.

< 어..? 너가 위로오면 안되는데...? >

유니티는 알파 선택시 듬성듬성 소팅(거리에 따라 그리는 순서를 변경) 해주는 것으로 보인다.

반투명 오브젝트는 불투명 오브젝트와 다르게 멀리 있는 것부터 그려지는데, 이를 알파 소팅(Alpha Sorting)이라 한다.

이 때 거리값을 판별하는 기준은 "카메라에서 오브젝트 피벗까지의 거리를" 이용한다.

 

다만 이러한 방법은 한계점이 확실하다.

카메라 시점이 회전함에 따라 뒤에 있던 오브젝트의 피벗이 더 가깝다고 판정되면 불쑥 튀어나와 앞에 있는 오브젝트를

가리게 될수있다.

빌보드 플랜이 아닌 복잡한 평태의 반투명 폴리곤은 오브젝트 거리에 의한 소팅으로도 해결 불가능하다.

빌보드 평면도 오브젝트의 중심에 의한 거리 순서와 버텍스의 거리 순서에 오차가 발생하기도 한다.

그리고 소팅 비용도 만만치 않다!

 

2-2. Alpha Clipping

 

알파 테스트, 컷아웃, 알파 클리핑 등 다양한 용어로 불린다.

Alpha Clipping은 알파 텍스쳐의 모양대로 Z버퍼에 그려지게 할 수 있는 기능이다.

(Opaque에 사용해야 Z-Buffer에 기록된다.)

Alpha Clipping을 적용하고 그림자를 그리게 될 경우 알파에 들어간 텍스쳐의 형태대로 그려진다.

Z-Buffer에 기록하므로 소팅 이슈도 없다.

풀이나 나뭇잎에 주로 사용하게 된다.

 예시로 이해하기

알파 클리핑은 Transparent Alpha와 함께 사용이 가능하지만 클리핑과 블렌딩이 동시에 작동하며 Z버퍼에

기록되지 않는다. (유니티 기본 재질의 경우)

Transparent Alpha + 클리핑이 Z-Buffer에 기록하려면 커스텀 셰이더에서 직접 제어해야한다.

 

모바일기기에서 느린 기술들

더보기

 

링크 #1, #2

 

 

 

3-1. 변수

변하는 수, 변하는 값을 담는 저장공간이다.

 

변수는 선언 후 사용한다.

(HLSL임을 가정하여 설명한다.)

float a; // 값 지정 없이 선언. 후에 값을 지정해야함

float a = 1.0; // 값 지정과 함께 선언

 

선언은 이렇게 두 가지 경우가 있을 수 있는데, a를 두 번 선언하면 에러가 발생하니 둘 중 하나만 사용해야한다.

 

일단 선언된 변수는 float 없이 그냥 a만 사용한다.

a = a + 1.0; // 기존 a 값에 1.0을 더한다. 선언시 값 지정이 없었다면 에러가 난다.

 

변수명을 지을시 주의사항은 아래와 같다.

1. 특수문자나 공백을 사용할 수 없음 / #Test 불가

2. 영문자로 시작해야함. 숫자로 시작하면 안됨 / 123Test 불가

3. 대문자와 소문자를 구분함 / teST와 TEST는 다른 변수다.

4. 예약어를 변수명으로 사용할 수 없음 / float, int 이런 것들

 

3-2. 상수 (Constant)

변하지 않는 언제나 같은 값

ex) const float threshold = 0.2f

변수를 상수처럼 쓸수는 있지만 내부적으로 비효율적으로 작동한다.

 

2진수로 표현하는 숫자

 

 


참고한 글

유니티 반투명, 스텐실 개념 익히기 - 링크

 

소중한 교수님의 수업자료

 

 

'유니티' 카테고리의 다른 글

8(+1)주차  (0) 2022.05.04
7(+1)주차  (0) 2022.04.26
5주차  (0) 2022.04.06
4주차  (0) 2022.03.29
3주차  (0) 2022.03.24
Posted by 텃파
유니티2022. 4. 6. 19:07

0. 4주차 보충

0-1. 라이팅이 Cos인 이유

 

라이팅과 리니어 그라데이션과 비교를 해보자.

라이팅이 단순히 y=x의 형태였다면 리니어 그라데이션 처럼 나오겠지만, Linear한 형태가 아니다.

< 비교 예시 >

0-2 . 노멀라이즈

 

위 이미지는 램버트 라이팅의 셰이더 그래프 노드이다.

유니티 상에서 라이팅 등의 연산을 진행할때 가져오는 노멀벡터의 값은 1이 되도록 자동화되어있다.

빛의 방향벡터와 노멀벡터를 내적할때 벡터의 크기가 1이다는 가정하에 연산을 하게되는것이다.

즉, 1보다 큰 벡터값이 들어간다면 라이팅이 잘못들어가게될것이다.

길이가 1인것에 대한 내적이 cos연산인것이지, 벡터의 내적자체는 값과 상관없이 계산된다.

< 벡터의 내적 >

셰이더그래프의 노드에서는 이미 정규화 되어있지만 ,HLSL에서는 노멀라이즈 함수를 사용하여야한다.

 

1-1. 셰이더란

 

셰이더는 2D나 3D 리소스가 모니터 화면에 그려질 때 어떤 색으로 그려질지 결정하는 프로그램이다.픽셀의 색을 결정하는 함수 같은 것으로 픽셀 숫자만큼 계산을 반복한다.

 

색은 필셀 단위로 그려지고, 픽셀의 색을 결정하는데 필요한 정보는 아래와 같다.

  • 폴리곤을 비추고 있는 빛의 방향 (각도)
  • 픽셀이 위치한 3D 폴리곤 표면의 방향 (각도)
  • 폴리곤 주변 환경의 밝기 (Ambient)
  • 픽셀이 위치한 3D 폴리곤 표면의 재질 정보

 

1-2. CPU, GPU

 

CPU : 복잡하고 다양한 연산을 수행하지만 순차적으로 하나씩 처리한다.

GPU : 단순하고 정해진 연산을 수행하지만 동시에 병렬적으로 처리한다.

 

더 쉽게 이해한다면, CPU는 작업을 지시하는 부사관, GPU는 작업을 수행하는 병사들이다.

이해를 도와주는 영상의 링크이다.

 

1-3. 3D폴리곤이 화면에 그려지는 과정

 

< 노란색 BOX는 아티스트 혹은 프로그래머가 개입할수있는 부분이다. >

맥스 또는 마야에서 작업하여 FBX파일로 Export를 하는데 폴리곤에 대한 정보를 담는다.

이후 이를 게임 엔진에 import를 하고 게임 엔진이 프로젝트에 맞게 가공을 하게된다.

화면에 출력해야하는 상황이 될때 GPU에 필요한 정보를 전달한다.

버텍스 셰이더에서 버텍스의 위치를 계산하고, 레스터라이저에서 폴리곤을 픽셀화한다.

이후 픽셀 셰이더에서 픽셀의 색을 계산하게 되고, 모니터에 출력된다.

 

5,7번 부분은 아티스트 혹은 프로그래머가 개입하여 코딩 또는 스크립팅 할 수 있는 영역이다.

 

5,6,7과정이 시각화 된 예시

유니티 렌더링 파이프 라인 과정이 설명된 글

 

1-4 레스터라이저

 

버텍스 셰이더는 화면의 어디에 그려져야되는지를 계산한다.

 

레스터라이저에서 폴리곤을 픽셀화한다.

그 이외에도 보간을 해주는 역할을 해주기도한다. 이는 아래에 후술하게된다.

 

픽셀 셰이더에서는 아티스트가 주문한 색대로 픽셀의 색을 채운다.

 

1 - 5 . 보간 ★

보간 Interpolation은 2개의 정보를 일정한 비율로 섞어서 중간 값을 도출해내는 것을 의미한다.

<아티스트에게 익숙한 컬러 그라데이션 형태>

관점을 달리하여 RGB의 각각의 세 값을 x,y,z 위치 좌표라고 생각해보자.

색상의 보간을 색 공간에서 생각하게되면 두 위치 사이의 한 지점이 된다.

이러한 원리로 두 색상의 차이를 distance 함수로 계산하기도 한다.

또한 레스터라이저에서 수행하는 역할 중 보간기라는 역할이 있다.

노멀, 버텍스 컬러, 텍스처 좌표 등이 보간되게 된다.

< 버텍스컬러의 보간 >

모든 버텍스마다 지정되어 들어온 어디를 바라보고 있는지를 의미하는 노멀 방향이 있는데, 이를 보간하는 것이다.

한 버텍스의 2차원 노멀 방향이 (-1,1), 다른 하나는 (1, -0.7) 이라 가정한다.

정가운데 위치를 보간하면 (0, 0.15)가 될것이다.

다만 값을 보면 알듯이 벡터의 크기가 1로 일정한 형태가 아니기에 노멀라이즈하여 크기를 1로 만들어 사용하게 된다.

출처

 

 

1 - 6 . Graphics API

 

GPU는 하드웨어인데, 직접적으로 아티스트나 프로그래머가 직접적으로 제어하는 코딩을 하는 것은 난이도가 매우 높다.

중간에 완충장치로서 Graphics API를 활용함으로서 하드웨어를 제어해줌으로서 하드웨어가 다양하거나 달라져도

큰 신경 씀없이 제어할 수 있게 된다.

Direct3D 11 Pipeline

 

1 - 7. Pixel Shader, Fragment Shader

Vertex shader는 vertex수만큼 실행되지만 Pixel shader는 화면의 해상도에 따라 달라진다.

Pixel shader는 상황에 따라 더 계산될 수도 있다.

(4K등 높은 해상도까지 가능한 좋은 모니터에는 좋은 그래픽카드가 따라줘야 하는 이유일까?)

 

Pixel shader와 Fragment shader은 서로 동의어로 사용된다.

그렇다고 완전히 같은 뜻은 아니다.

Fragment shader는 여러 테스트(깊이, 알파, 스텐실)를 통과하여야하고 안티 앨리어싱에 사용될 수 있어  

Fragment = Pixel은 아니다. 즉, 아직 픽셀이 아닌 것이다.

Fragment Shader가 Pixel Shader와 동일한 기능에 대한 더 정확한 이름인것이다.

( 막 생산라인에서 막 생성된 공산품이 Fragment라면 포장 등을 거쳐 소비자에게 간 공산품은 Pixel 이랄까...? )

 

 

 

2-1. Z-Buffer

 

Z-Buffer란 픽셀이 얼마나 멀리 있는지 기록하는 버퍼이다.

오브젝트가 하나 그려질 때마다 테스트 (Z-Test) 후 업데이트 (ZWrite)하게된다.

픽셀이 그려질지 말지 결정하는 역할을 한다.

Z-Buffer의 의미가 중요한 것은, 렌더링 시 물체끼리 겹쳐있거나 가려져 있을 떄 보이지 않게 되는 면을렌더링에서 제외하는 등의 처리에서 위치값을 인식하는 기준이 되기 때문이다.

( 상:프레임버퍼 / 하: Z버퍼 )

0~1사이의 값으로 거리정보를 기록하게되는데, 가까운 거리는 0, 먼 거리는 1이 된다.

16 또는 32 비트의 정밀도를 사용한다.

 

< 유니티 상 카메라 >

카메라의 프로퍼티 중 Clipping Planes라는 프로퍼티가 있다.

렌더링을 시작 및 중지할 카메라로부터의 거리를 의미하는데 Near와 Far의 값을 조정하여 설정한다.

Near는 그리기가 수행되는 카메라에 가장 가까운 점, Far가장 먼 점이 된다.

이때 Near와 Far값 사이의 간격이 늘어남에 따라 Z-Buffer 정밀도의 분포에 차이가 생긴다.

 

그렇다면 z-buffer의 0~1 분포는 단순히 동일한 간격으로 되는가하면 꼭 그런것은 아니다.

카메라와 가까운 쪽의 정밀도를 높이기 위해 비 선형적인 형태를 주로 사용한다.

0~1이 되는 거리 정중앙이 0.5가되는 선형적 형태가 아닌 아래와 같은 형태가 되는것이다.

 

< Z-buffer와 Depth buffer는 동의어이다. >

< Non-linear depth buffer >
< Linear depth buffer >

같은 단계의 정밀도를 사용한다면, Near와 Far의 거리가 멀어짐에 따라 정밀도의 분포에 차이가 생긴다.

<간단한 이미지의 예시 >

2-2 Z-Fighting

 

z-fighting 이란 두개의 폴리곤이 유사한 z-depth값을 가지고 있어 노이즈가 발생한것처럼 깜박거리는 현상이다.

 

일반적으로 16,32 비트 크기의 z-buffer 사용하기 때문에, 즉, 값이 유한하기 때문에 발생할수밖에없다.

< z-fighting 현상 >

2-3 Z-Buffer의 역할

 

Z-buffer의 역할을 알수있는 가장 빠른방법은 있고없고를 비교해보는것이다.

< 평화로운 기본값 환경 >

구와 큐브의 z-buffer 기록을 끄면 아래와 같이 된다.

< _zw 을 0으로 >

거리정보가 없어져 거리정보에 따라 그려지는게 아닌

단순히 늦게 그려진 오브젝트가 먼저 그려진 오브젝트를 가리게 되는 모습이다.

 

< 노란 구가 먼저 그려진 경우 >

그려지는 순서대로 렌더가 되어 두 오브젝트가 겹치는 부분은 픽셀 셰이더 관점에선 2번 그려지게 되었다.

 

< z - buffer가 개입했다면 >

Z-Buffer가 있다면 Z-Test에 의해 겹쳐지는 부분을 그리지 않는다.

 

즉, Opaque(불투명) 오브젝트는 Z-Buffer에 의해 그리는 순서와 상관 없이 정상적인 깊이(Depth)를 렌더링 한다.

 

다만, 반투명의 경우에는 오히려 Z-write를 꺼주어야한다.

Z-Buffer가 기록이되면 불투명 오브젝트의 폴리곤 모양대로 기록하게되는데,

이는 Z-Buffer가 픽셀이 어느 거리인지만 기록하기 때문이다.

< Zwrite가 켜진 경우 >

폴리곤으로 자리를 점유하고 있다고 기록이 되어 아래에 있는 오브젝트는 가려진다 생각하여

겹치는 부분을 그리지 않게 된것이다.

 

이런 이유로 Zwrite를 꺼주어야 하는 것이다.

그래도 Z-Test는 켜주어야 한다.

Z-Test가 켜진 상태라면 다른 불투명 오브젝트와 겹쳐있을때 기존에 있는 오브젝트와 비교는 진행하게 된다.

이를 통해 또 다른 반투명 오브젝트가 주변에 그려지더라도 해당 오브젝트가 잘려보이지 않고

불투명 오브젝트와는 정상적으로 가림처리가 진행되게된다.

 

 

정리하자면 다음과 같다

불투명 오브젝트는 Z-Buffer에 존재한다. (Zwrite On)

반투명 오브젝트는 Z-Buffer에 존재하지 않는다. (Zwrite Off)

Add나 Multifly는 그리는 순서에 상관 없이 동일한 효과가 발생한다.

 

 

 


참고한 글

 

 

Z 버퍼의 Read / Write 개념 (1부. Z 값과 Z 버퍼) 

[Shader] 쉐이더(Shader)란?

Wikipidia - Z-fighting

[정리노트] 3D 그래픽: 002-3차원 그래픽의 기초개념2

 

 

그리고 교수님의 수업자료

 

 

'유니티' 카테고리의 다른 글

8(+1)주차  (0) 2022.05.04
7(+1)주차  (0) 2022.04.26
6주차  (0) 2022.04.13
4주차  (0) 2022.03.29
3주차  (0) 2022.03.24
Posted by 텃파
유니티2022. 3. 29. 02:31

0. 3주차 보충

셰이더의 나눗셈은 가급적 곱셈으로 진행하는게 좋다. (최적화 측면)

굳이 이유가 궁금해서 찾아보았는데 이 링크에 잘 설명되어있다. 컴퓨터의 연산과정과 관련된 것.

 

수학적으로 0으로 나누는 것은 불가능하다.

아래 영상을 참고하면 이해하기 쉬울것이다.

다만 어쩔수없이 셰이더를 구성하면서 나누기를 사용하는 경우도 생긴다.

프로그래밍이라면 0으로 나누는 경우를 조건문으로 제외하면 되지만 셰이더에서는 복잡한 조건문을 구성하기 힘들다.

결론은...0대신 0에 가까운 작은 값을 사용한다!

ex) 나누는 값에 대한 최소,최대치 지정시 0.001~1 로 지정한다.


1 - 1 . Keyword

Keyword 노드는 셰이더 그래프 상에서 정적분기를 사용할 수 있게 해주는 노드이다.

불린(boolean)은 논리적 데이터 유형으로 참(true) or 거짓(false) 값만을 가질 수 있다.

< Keyword 노드 >
< 인스펙터 상에서 보이는 모습 예시 >

1 - 2 . Shader Feature, Multi Compile

위 처럼 셰이더 그래프 상에서 Keyword 노드를 통해 분기가 남아있을때 유니티는 On과 Off 모두 계산해놓는다.

이를 멀티컴파일 이라고 하며 이 과정에서 셰이더 베리언트가 생성된다.

동일한 셰이더를 각각 다른 Material에 할당한 경우 한 곳에서 셰이더 코드를 작성하고 유지하며, 프로젝트에서 더 적은 셰이더 에셋을 사용할 수 있다.

< 셰이더를 컴파일 할 떄 임시로 에디터에 보이는 모습 >

Keyword 노드를 사용할 때 Definition를 잘 설정해주어야한다.

Shader Feature : 재질에 설정된 경우만 컴파일

Multi Compile  : 재질과 관계 없이 모든 경우의 수를 컴파일

 

ex) 다른 재질의 오브젝트 A와 B에 X셰이더가 적용되었다. A와 B 모두 True로 설정되어있다.

< Keyword 노드 세팅 Definition >
< SF 와 MC 간단이해 >
< 설정에 따라 달라지는 베리언트 수 >

여기서 기억해둘 점은 SF사용되지 않는 배리언트는 게임 빌드에 포함시키지 않는다.

단, SF로 빌드된 셰이더에서 실행 중 컴파일 되지않은 셰이더로 변경될 경우 빌드에 포함되어있지않아

의도치 않은 마젠타가 보일 수 있다.

 

< Scope 설정 >

해당 키워드의 사용범위를 Scope를 통해 설정할 수 있다.

셰이더 베리언트를 사용할 때 Unity에서 키워드가 256개로 제한되고 이 중 약 60개가 내부적으로 사용된다.

실제로 사용 가능한 키워드는 더 적으므로 큰 프로젝트의 경우 Keyword 사용 제한을 고려하여 내부의 룰에 의거해서

사용하도록 한다.

 

1 - 3 . 셰이더 폭발

 

키워드에 의한 정적 분기는 파생 버전의 셰이더(배리언트)를 자동 생성한다.

키워드의 종류가 많아질수록 배리언트의 수는 제곱으로 증가한다.

다양한 조건에서 불필요한 코드를 줄이기위해 필요한 기능이다.

하지만, 이런 조건이 많아지면 오히려 배리언트의 숫자가 기하급수적으로 증가해 셰이더 수가 증폭된다.

이를 셰이더 폭발이라고 한다.

< 3가지 Shadow 조건과 Fog 여부 / 총 6개의 배리언트가 생성된다. >

1 - 4 . SRP Batcher

 

DrawCall은 CPU가 GPU한테 그려라 명령을 내리는 작업이다.

쉽게 말해 화면에 보이는 오브젝트들을 그리라고 하는 명령이라고 생각하면 된다.

유니티에서는 DrawCall + SetPass Call Batch 숫자로 환산한다.

 

SetPass Call이 무엇인가?

 

우선, CPU는 렌더링해야 할 것들을 Command Buffer라는 곳에다 명령이라는 형식을 전달을 한다.

그다음, 화면에 그려질 오브젝트들을 위해 필요한 정보들이 충분히 쌓이면 GPU에게 넘긴다.

< 출처 : 유니티 코리아 유투브 채널의&amp;amp;amp;nbsp; [Dev Weeks : 성능을 고려한 파이프라인. Universal Render Pipeline >

매테리얼에 필요한 정보들이 가장 많은 부하를 일으키게 된다.

그래서 따로 모아서 처리하게 되는데, 매테리얼과 셰이더 관련된 커맨드들을 모아서 Set Pass Call라고한다.

해당 명령의 공통점은 GPU에 상태값을 전달하는 명령이다.

따라서 SetPassCall을 줄일수록, 상당히 많은 부하를 잡을 수 있다고 한다.

같은 매테리얼을 사용하게 되면 한 번에 그리게 되고,

반대로 다른 매테리얼을 사용한 오브젝트가 있다면 따로 그리게 될것이다.

그 이유는 같은 매테리얼을 쓰면 SetPassCall에 해당하는 명령들을 첫번째 오브젝트 그릴 떄 전달했던 값을

그대로 쓰면 되기 때문이다.

 

즉, SetPassCall을 줄일수록 GPU에 주는 부하가 적어지고, 이는 최적화로 연계된다.

SetPassCall을 최대한 적게 호출하면서 DrawCall을 수행하는 것을 유니티에서 배칭이라고 표현한다. 

 

기본적으로 매테리얼이 달라지게되면 렌더링을 따로 하게 되어, SetPassCall이 늘어나게된다.

하지만 SRP Batcher는 SetPassCall을 더 크게 셰이더 단위로 묶어준다.

매테리얼이 달라져도 셰이더가 같으면 하나로 묶어서 처리해준다는 것이다. 

 

URP 12.1.4부터 SRP Batcher 옵션이 기본적으로 숨겨져있다. Preferences > Core Render Pipeline > Additional Properties > Visibility를 All Visible로 변경하면 모두 보인다.

 

예시를 통해 이해를 해보자

 

위쪽 오브젝트 5개는 텍스처를 다르게 넣어 매테리얼은 다르지만 같은 셰이더를 사용하고 있다.

아래쪽도 텍스처를 다르게 넣어 매테리얼을 달리하고 각 색마다 서로 다른 셰이더를 사용하고 있다.

각 그룹별로 오브젝트를 1개씩 차례대로 활성화시키면 Stats창에서 어떤 변화가 있을까?

 

위쪽의 경우 Batches만 늘어나고 SetPassCall은 늘어나지 않는다.

하지만 아래쪽의 경우 Batch, SetPassCall 모두 늘어나는 모습을 확인 할 수 있다.

 

같은 셰이더를 사용하는 다른 매테리얼이 많을 경우, SRP Batcher를 통해 최적화에 도움을 줄 수 있는 것이다.

 

 


2 - 1 . 커스텀 라이팅

셰이더 그래프 12.1.4 기준으로 Light Direction 설정해주는 노드가 없다.

이렇게 유니티의 기본 노드들이 원하는 기능을 모두 해결해줄수는 없다.

그에따라 유니티에서는 Custom Function 노드를 통해 직접 HLSL코드를 입력하여 셰이더 그래프에 사용할 수 있도록

기능을 제공하고있다.

 

Create Node > Utility > Custom Funcion 의 경로를 통하여 생성할 수 있다

 

<Custom Function 노드의 Node Setting>

Precision & Preview - 정밀도를 선택한다. Inherit 는 기존 걸 사용하고, Single은 32Bit, Half는 16Bit 정밀도이다.)

Type - 외부 파일을 선택할지, 직접 HLSL 코드를 입력할지 선택한다.

Name - HLSL 코드의 함수명을 입력한다.

Source - 외부 파일 에셋을 선택한다. Type 이 File 아닌 String일 경우 Body로 변경된다.

 

Node Setting 상의 Inputs, Outputs 이 HLSL 코드의 입,출력과 일치해야한다. (이름, 정밀도)

 

< 좌측 / Source 로 가져온 코드 원본 , 우측 / Node Setting 의 Input 과 Output >

*Name이 공백이거나 다른이름인 경우에도 에러가 나기때문에 보라색으로 표시하였음

< 좌측 / Input 에 Evil 이라는 원본에 없는 입력이 있어 에러가 난 모습 , 우측 / Input과 Output이 일치한 예시 >

 

2 - 2 . 벡터

 

게임 속에 존재하는 모든 오브젝트들은 어느 위치에 존재하거나, 어딘가를 향해 이동한다.

이런 오브젝트의 위치와 이동 등에 대해서 다루는 도구가 벡터이다.

벡터는 간단하게 요약하자면 공간에서 방향(Direction)과 크기(Magnitude)를 표현하는 도구로서 주로 화살표로 표시되는 개념이다.

여기서 공간은 2차원 혹은 3차원으로 정의되며, 유니티에서도 2차원과 3차원의 벡터만 사용한다.

 

< 방향, 좌표 >

위와 같이 평면에 벡터가 표시하는 좌표까지 화살표를 그리는 것으로 방향을 표시할 수 있다. 

또는 x축과 y축의 교차지점을 원점으로 가정할 때 오브젝트가 해당 벡터 위치에 있다하면, 그 오브젝트가 원점으로부터 (3,4)의 위치에 있다는 좌표의 개념으로도 활용 할 수 있다.

< 크기 >

또는 서로 다른 방향을 가리키는 2 벡터를 두고, 같은 방향으로 놓고 비교하면 빨간 화살표가 더 긴 것을 알수있다.

1초 동안 각 벡터의 길이만큼 이동한다 가정할때는 같은시간이라면 빨간화살표가 더 빠르게 이동하는 속력으로  

또는 두 개의 벡터의 길이를 비교하면서 두 벡터의 크기를 비교할수도있다.

 

< 벡터의 덧셈 >
<벡터의 덧셈>

위 이미지는 벡터의 덧셈 과정이다. v1(2,3) + v2(5,2) -> v1+2(7,5)

설명을 위해 v2 벡터를 공중에 뜬 화살표로 표현하였다.

이렇게 벡터는 공중에 떠있는 화살표로 표현할 수 있지만, 벡터는 원점을 기준으로 한다.

< 예시 >

유니티의 빛의 방향벡터는 어디에 있어도 동일하다.

 

2 - 3 . 삼각함수 의 기초(?)

 

한번은 알고리즘의 간택을 받아 반드시 들어봤을 전설의 노래의 링크가 있다면 삼각함수도 무섭지 않다...!

또한, 삼각함수를 이해하기 쉽게 도움을 주는 사이트도 있다.

<&nbsp;https://www.mathsisfun.com/algebra/trig-interactive-unit-circle.html >

2 - 4 . Dot Product ( 내적 )

 

유니티 상에서 라이팅 등의 연산을 진행할때 가져오는 노멀벡터의 값은 1이 되도록 자동화되어있다. (?)

빛의 방향 벡터와 면의 노멀 벡터를 Dot 하면 Cos 계산이 진행된다.

<내적은 계산 방법이다.>

 

{"originWidth":300,"originHeight":240,"style":"alignCenter","caption":"<

 

 

2 - 5 . Lambert Cosine 법칙

 

우선 Lambertian surface에 대해 알아보자.

Lamberitan surface는 모든 방향에서 보아도 똑같은 밝기로 보이는 표면을 뜻한다.

보통 우리가 이동하면서 한 지점을 바라볼 때 보통 표면을 보는 위치에 따라 밝기가 변한다. 아래 그림처럼 한 지점에서 반사하는 빛은 방향에 따라 다르기 떄문이다.

하지만 Lambertian surface는 모든 방향으로 같은 양의 빛을 반사하여 관찰자의 위치에 관계없이 같은 밝기로 보인다.

< 출처 : https://blog.naver.com/jetarrow82/221252045857 >

램버트 코사인 법칙은, 램버시안 반사율을 가지는 램버시안 표면에서

어떠한 빛이 들어오는 각도에 따라 반사되는 빛의 양이 다르다는 것이다.

이는 Cosine 법칙을 따른다.

 

< 동일한 면적으로 빛이 들어올 때 입사각에 의해 빛의 양이 달라진다. >

45도 위치가 0.5가 아니라 값이 0.707이 되고 계속 밝다가 어느순간 훅 어두워지는 점은 코사인 그래프와 연관지어

생각해보면 이해하기 쉽다.

< 코사인 함수 그래프 >

이미지 출처

 

 

이러한 원리는 Lamber Lighting에 사용된다.

빛의 방향과 면의 방향을 Dot(점곱/벡터의 내적) 연산하는 것이다.

< 간단 렘버트 라이팅 예시 >

[커스텀라이트코드]

더보기

예시에 사용된 커스텀 라이팅 코드는 아래 링크이다.

https://github.com/Unity-Technologies/ShaderGraph-Custom-Lighting

 

GitHub - Unity-Technologies/ShaderGraph-Custom-Lighting: A sample project showcasing a simple method to calculate custom lightin

A sample project showcasing a simple method to calculate custom lighting inside of Shader Graph for the Lightweight Render Pipeline. Includes a sample toon shaded scene and example assets. Built fo...

github.com

 

해당 프로젝트는 2019.2 버전을 기준으로 제작된 코드이다.

 

이로인해 상위 버전에서 버전이 맞지 않아 [ Invalid conditional expression ] 이라는 에러가 발생한다.

아래와 같이 조치한다면 에러가 사라진다.

 

아직 알쏭달쏭한 내용

더보기

3. 커스텀 라이팅 진행

 

< 램버트 라이트 적용 / 상단 접은글의 커스텀 라이팅 코드가 적용되어있다. >

간단 램버트 라이팅을 표현한 셰이더 그래프 예시와 다른점이 있다.

커스텀노드의 Output에 DistanceAttend와 ShadowAtten이 있다.

 

ShadowAtten은 그림자와 관련된 것이다.

DitstanceAtten은 라이트의 Culling Mask와 관련된 값이다.

현재 오브젝트가 속한 레이어(마스크?)와 라이트의 Culling Mask에 의해

오브젝트가 라이트의 영향을 받는다면 이값은 1이되고 받지 않는다면 0이 된다.

 

Culling Mask가 무엇인지 몰라서 찾아보았는데, 이 링크에서 예시를 통해 이해할 수 있었다.


이어서 위 셰이더 그래프에 이어서 그림자를 받도록 해보자.

유니티가 기본제공 Lit 에는 그림자 관련 키워드가 자동제공되어 잘 작동하지만

위 셰이더 그래프는 Unlit 환경이므로 해야할 일이 추가적으로 있다.

 

그림자 관련 키워드를 추가해주는 것이다.

URP 셰이더 라이브러리가 멀티 컴파일 되면서 제대로 작동하기 위해 필요하다.

Name은 틀려도 되지만 Reference가 정확해야 한다.  (앞에 밑줄이 있기도 없기도 하니 주의)

< 해당 노드들은 exposed 될 필요가 없다 >

유니터 기본셰이더 라이브러리들의 조건분기들이 그림자를 제대로 연산하기위해 필요한데, Unlit에는 이 내용이 포함되어 있지않다. 이러한 이유로 Unlit에서 커스텀라이팅을 진행하면사 그림자 처리작업이 필요하다면 해당 키워드를 추가할 필요가 있다.

Definition은 Multi Compile을 권장하고 Scope는 Global로 한다.

실제로 셰이더 그래프 상에서 쓰이진 않지만 존재하는 것만으로 그림자 처리에 도움이 되는 목적의 키워드이다.

 

참고로, 그림자를 위한 위 3가지 키워드 외에도 다른 목적으로 필요한 다른 키워드들도 존재한다.

 

< 키워드가 추가되었다. >
< 조치 후 예시 >

다음으로 환경광을 받도록 조치한다.

아래 강조 표시한 부분이 추가된다.

<환경광 적용>

마지막으로 Fog 적용이다.

Fog는 Lerp로 계산하기에 Lerp 노드에 대해 할 필요가 있다.

Lerp는 Linear Interpolation, 선형 보간을 말하는데, 간단하게 말해서 '두 개를 섞는다'이다.

 

< Lerp 노드 예시 >

다시 셰이더 그래프로 돌아가보자 Lerp 노드에서 T 인풋으로는 Fog노드의 Density(밀도)가 들어갔다.

< Fog Applied를 보면 Lerp가 적용된 것을 볼 수 있다. >

안개가 아주 짙게 깔린 상황에서 기존 재질의 컬러와 무관하게 포그의 색으로 보인다는 점을 생각하면 

Lerp노드를 활용한 이유를 이해할 수 있다.

< 좋은 예시일지는 모르겠다만... Eternal Darkness의 스크린샷 >

 

 


 

 

 


참조한 글 리스트

Unity 3D :: 게임 최적화 기법 https://loadofprogrammer.tistory.com/148

Unity 메뉴얼/셰이더 베리언트 및 키워드 https://docs.unity3d.com/kr/2019.4/Manual/SL-MultipleProgramVariants.html

Batch 및 SetPass Calls https://shkim0811.tistory.com/42

[Unity3D] Vector - 좌표와 속도를 다루기 위한 도구 https://wergia.tistory.com/209

[베개발] 02. 유니티 셰이더그래프 https://rusalgames.tistory.com/18

램버시안 코사인 법칙이란 https://iskim3068.tistory.com/76

램버시안 반사 https://blog.naver.com/jetarrow82/221252045857

 

그리고

 

교수님의 열정으로 가득한 수업자료

 


'유니티' 카테고리의 다른 글

8(+1)주차  (0) 2022.05.04
7(+1)주차  (0) 2022.04.26
6주차  (0) 2022.04.13
5주차  (0) 2022.04.06
3주차  (0) 2022.03.24
Posted by 텃파