1-1. 여러 텍스처를 샘플링할때, Sampler
위와 같이 2개의 구가 있을때 양쪽의 구는 동일하게 가,나 텍스처를 불러온다.
셰이더에서 불러오는 방식이 다른데 이것으로 인해 생기는 차이를 알아보자.
좌측공은 combined, 우측공은 seperated 방식이다. (공식명칭X)
좌측은 텍스처를 sampler2D라는 HLSL로 우측은 TEXTURE2D라하는 매크로를 통해 불러온다.
(전처리 지시어, #define에 의해)
이러한 방식의 차이로 우측은 샘플러와 텍스처가 분리되었지만 좌측은 텍스처와 샘플러가 하나로 묶였다.
그렇다면 샘플러는 무엇인가?
렌더 파이프라인 상에서 텍스처를 처리할때 타일링을 할지, 필터링을 할지 등 어떻게 다뤄야할지에 대한 선택사항을 샘플러라고 한다.
위에서 설명한 차이로 생기는 결과를 살펴보도록 하자.
좌측은 합쳐진 형태이므로 텍스처와 샘플러가 한 덩어리이므로 2개의 텍스처를 사용했다면 샘플러도 2개이다.
우측은 분리된 형태이므로 텍스쳐는 2개이지만 샘플러는 공용으로 사용된다.
하나의 텍스처의 타일링 방식을 변경한 후 결과를 살펴보자.
사용된 텍스처 중 '가'텍스처의 Wrap mode를 Repeat에서 Clamp로 변경해보았다.
좌측 구는 '가'텍스처와 '나'텍스처가 서로 다른 샘플러를 사용하기때문에 '나'텍스처 영향을 주지 않았다.
우측 구는 '나'텍스처도 함께 영향을 받은 모습을 볼 수 있다.
이는 Wrap Mode외에도 Filter Mode를 변경하여도 비슷한 결과를 얻을 수 있다.
이번에는 '나' 텍스처의 Wrap Mode를 변경해보자.
좌측 구는 '나' 텍스처 부분에 영향이 그대로 보여진다.
우측 구는 '가' 텍스처를 변경한 것과 달리 변함이 없다.
이 결과를 통해서 우측 방식의 특징을 명확히 확인할 수 있는데,
'가'텍스처의 샘플러를 '나'텍스처가 공유하여 '나' 텍스처만의 샘플러가 없는 것을 알 수있다.
위의 경우 '나'텍스처의 샘플러가 따로 없고 '가'의 샘플러를 같이 사용하기에 변함이 없는 것이다.
코드를 살펴보면 더욱 명확히 알 수 있다.
'가'텍스처 와 '나'텍스처가 하나의 샘플러를 통해서 불러옴을 알 수 있다.
하드웨어의 제약 등으로 인해서 샘플러의 수에 제약이 있을때 이러한 방식을 사용한다.
제약이 많은 샘플러는 적은 수로 사용하고 텍스처는 다양하게 쓰기 위함이다.
메뉴얼 상에서도 아래와 같은 내용이 있는데
"많은 그래픽스 API의 GPU에서는 텍스처보다 적은 샘플러를 사용할 수 있으며~"
한정된 자원에서 최대한의 성능을 이끌어내기 위해서 분리된 방식을 사용한다.
combined(연결된)방식이든 sepreted(분리된)방식이든 _ST를 꼭 붙여주어야한다.
TRANSFORM_TEX의 경우 유니티에서 define매크로 처리에 의해
_BaseMap의 키워드를 기준으로 _ST 이름으로 자동 변환되어서 사용하며,
SAMPLER(sampler_BaseMap)에서 또한 _BaseMap 이름을 기준으로 _미리 약속된 처리를 한다.
일종의 약속된 규칙이다.
1-2. UV회전
회전 행렬을 복기하여 보자. ( 이미지 출처 )
uv의 경우 x,y 가로세로의 평면 회전이기에 이동할 필요가 없는 경우 동차좌표를 사용하지 않아도 된다.
예재를 통해 이해보도록 하자.
UV회전을 회전행렬을 생성시키는 함수를 호출한다.
해당 함수는 RotMatrix로 아래와 같이 구성된다.
함수에서 2x2 크기의 행렬을 구성해서 return함을 알 수 있다.
이렇게 계산된 행렬을 uv정보와 mul을 통해서 곱하는데 코드에서는 Offset and Tilling을 고려하여 원점으로 돌리고나서
해당 과정을 거침을 볼 수 있다.
Tilling을 하게되면 pivot에 변화가 생기는데
TRANSFORM_TEX가 타일링과 오프셋을 감안해서 uv정보를 가공하여 준다.
이 과정이 없다면 무조건 원점을 기준으로 돌아가게 되어 아래와 같은 결과를 얻게된다.
2-1. Diffuse Wrap (Toon Ramp) , Colorlize
아래 예재를 보도록하자.
좌측 예재 오브젝트는 N(노멀)dotL(라이트)한 렘버트 셰이딩 값인 1 ~ -1의 값을 기반으로
Ramp Map을 샘플링 한것이다.
RampMap텍스처는 아래와 같다
코드를 보도록 하자.
SAMPLE_TEXTURE2D라는 매크로에 의해 텍스처와 uv를 불러오고있다.
~ (_RampMap, sampler_RampMap, ~ / 텍스처와 샘플러가 분리된 방식으로 불러오고 있다.
uv를 half2(NdotL, 0.5)라는 코드로 불러오는데, 텍스처를 얻어올때 uv의 세로 값을 0.5로 고정하고있다.
가로방향으로 NdotL을 사용하고 있다.
발췌 코드 2번째 줄을 보면 1~-1로 가던 NdotL을 1~0으로 가는 Half Lambert로 가공하고있다.
이 코드를 거친 결과 빛을 제일 많이 받는 구간은 1쪽에서 색을 얻어오고, 반대는 0쪽에서 얻어올것이다.
결과적으로 라이팅을 아래 그라데이션처럼 그리게 되는것이다.
리턴을 단순히 NdotL로 받았다면 아래 좌측과 같은 형태가 되었겠지만 uv를 통해 색을 얻어옴으로서 그라데이션의 색을 얻어온것이다.
이 방식을 사용할때 텍스처의 Wrap Mode를 Clamp로 해주어야한다.
Repeat이라면 색이 제일 밝거나 어두운곳에서 텍스처의 타일링으로 의도치 않은 색끼어듬이 발생할 것이다.
또한 비압축을 권장한다.
또한 텍스처의 세로크기가 1pixel크기여도 된다.
만약 하나의 그라데이션만 있는것이 아니고 하나의 텍스처의 여러 그라데이션을 세로로 쌓아놓을수도 있는데,
위 예재 코드에서 uv의 세로값을 0.5로 고정하여 얻어온 것처럼 목적에 맞추어 세로 uv값을 달리하여 얻어오면 된다.
다만 이 경우 세로 uv정보에 어떤 위치는 어떤 그라데이션인지 개발 내부적으로 약속을 할 필요가 있다.
Ramp텍스처는 글로벌 텍스처로 사용하기도 한다.
라이팅은 글로벌한 값이므로 하나의 씬안에 라이팅을 받을 수 있는 오브젝트나 재질이 여러가지 일수도 있기때문이다.
아티스트가 매번 서로 다른 재질마다 일일히 다른 Ramp텍스처를 지정하는건 위험부담이 크다.
개발팀마다 사정이 다르겠지만, 씬내에 빈 오브젝트 생성하여 플머가 만든 컴포넌트를 달고 여기에 씬에 사용되는 라이팅 용 그라디언트 세팅을 담는다. 플머는 컴포넌트 코딩 시 글로벌 프로퍼티를 세팅한다.
아티스트는 셰이더를 코딩할때 이러한 세팅이 있다는 가정하에 셰이더에서 프로퍼티를 사용하지않고 HLSL로 바로 가져다 사용한다.
(ex 셰이더에서 재질과 관련된 프로퍼티를 작성하지 않고, 글로벌 프로퍼티를 가져온다는 가정하에 바로 사용)
이러한 방식은 Colorize에서도 사용된다.
GrayScale로 작업된 이펙트 텍스처의 밝기정보를 이용하여 그라디언트 텍스처를 샘플링하는 것이다.
여러개의 그라디언트를 하나의 텍스처에 층지어 쌓고 재질에서 uv값을 통해 여러개의 다양한 색상을 가져오는것이다.
3-1. 굴절
화면을 캡처하여 Distortion을 적용하는 간단한 방식으로 정확한 Ray Tracing이 아니다.
URP에서는 Geometry Reder Queue 2000번 시점에서 렌더 파이프라인이 화면을 캡처하여 _CameraOpaqueTexture라는
글로벌 변수에 텍스처를 매 프레임 설정한다.
이는 아래 이미지와 같이 렌더 파이프라인 에셋에서 Opaque Texture를 체크해야 캡처가 수행된다.
(레거시 렌더 파이프라인에서는 Grab Pass에 의해 재질이 그려질 때 캡처를 수행한다.)
굴절 오브젝트는 반투명, 뒤에 있는 바닥 등은 불투명한 오브젝트이다.
실제로 렌더 파이프라인의 그리는 순서도 불투명 오브젝트를 그리고나서 Transparet를 그린다.(렌더큐)
Opaque와 Transparent사이에 매프레임마다 Opaque를 캡처하여 사용할수 있게 준비하는 것이다.
(캡쳐 결과는 렌더링한2D이미지,반투명없는상태,uv좌표도 있음)
관련 코드 알아보기(접은글)
UV는 코드의 어디에서 가져오는가?

IN.uv가 값을 얻어오는 부분은 강조 된 부분으로 ComputerScreenUV함수로 클립공간의 버텍스를 기준으로 화면좌표를 계산 받는 부분이다.
그리려고 하는 픽셀이 화면상에서 가로세로 어느 위치에 있는지 알고싶을때 ComputerScreenUV함수를 사용한다.
굴절 구현에 있어서 프레넬이 사용된 것을 볼 수 있다.

프레넬 구현 코드 아래를 보면 IN.normalISS 라는 생소한 구문이 보인다.
이는 ScreenSpaceNormal을 버텍스 셰이더에서 계산해 온 값이다.
코드를 살펴보면 아래와 같이 이해할 수 있다.
Attribute 구조체에 의해 들어온 normalOS가
vertex셰이더에서 IN이라는 구조체 함수에 의해 오브젝트공간에서 클립공간으로 공간변환이 된다.
클립공간 노멀 정보중에 xy값만 따로 계산하는데 클립공간기준 좌우와 상하 방향만 사용한다는 것인데,
클립공간은 보는 카메라 방향인 것을 생각하면 카메라 방향에서 좌우상하만 따로 사용하는 것이다.
xy공간만 스크린스페이스 노멀(normalSS)에 저장을하고
xy만 사용하여 길이가 1이 아니게 되버리기에 정규화(noramlize)한다.
이런 정보가 저장된 것이 OUT.normalSS인것이다.


화면을 보는 기준에서의 픽셀의 프레넬 정보가 normalSS인 것.
결론은 프레넬 정보를 기준으로 uv를 밀어서 찍는 것이다.

CameraOpaqueTexutre에서 IN.uv로 색을 얻어오는데 IN.uv는 아래와 같이 가공을 거치는 부분이 있는데,

프레넬(fresnel) 만큼 uv를 밀어서(IN.normalSS) IN.uv를 가공겠다는 뜻이 되는것이다.
_Refraction은 프레넬 외에 얼마나 증폭할지 정하는 부분이다.


아래는 반사에 관련된 내용들이다.

GlossyEnvironmentReflection는 반사를 계산해주는 함수이다.



그렇게 얻은 reflect를 원래색과 굴절사이에서 얼마나 반사(reflect)할지 보간을 한다.
reflectAmount에서 _Reflection은 인스펙터에서 조절 가능한 값으로 반사의 정도를 조정하고자 하기위함이다.


위에 더보기를 누르면 글이 펼쳐집니다.
굴절 오브젝트 뒤에 굴절 오브젝트의 모습이 안보이는 것을 볼수있다.
CameraOpaqueTexture는 2000번대 Opaque렌더큐를 마치고 나서 준비된 이미지이기에
굴절 오브젝트가 그려지기 전의 이미지를 가공하여 사용했기에 보이지 않는 것이다.
Legacy와 달리 URP에서는 최적화 이슈로 Opaque텍스처를 지정된 순간 한번 찍고 마는데, Legacy에서는 촬영 지시를 GrabPass를 통해 셰이더에서 하는 것이 가능했다.
Grab Pass라는 Pass를 끼워넣으면 재질이 셰이더를 렌더링할때 캡쳐를 진행한다.
서로 다른 Grab Pass를 든 오브젝트가 여럿있으면 그만큼 캡쳐를 진행하는 것이다.
다만 그런 오브젝트가 많아질수록 기하급수적으로 성능저하가 생기기 쉬운데,
URP에서는 엔진이 성능의 최적화를 위해 어쩔수없이 그렇게 된듯하다.
그 외 참고자료 (접은 글)


URP에서는 GlossyEnvironmentReflection(월드반사벡터,WorldSpace위치,roughness,Occlusion)를 통해 구현.

4-1. 노멀맵
버텍스 기준의 노멀 정보로 지금까지 라이팅하는 법을 배웠지만
노멀맵의 사용은 픽셀단위로 방향값을 텍스처로 제어해보자는 취지에서 시작하였다.
노멀맵 텍스처는 픽셀의 방향단위를 가진 텍스처이다.
아래 예재를 통해 이해해보자.
위의 평면 오브젝트는 아래와 같이 노멀맵이 적용되어 있다.
적용되어있는 노멀맵 텍스처를 포토샵에서 불러와서 채널을 보면 아래와 같이 되어있는 것을 볼 수 있다.
R채널은 좌우에 대한 정보를, G채널은 상하, B채널은 앞뒤 대한 정보를 가진다.
더 이해하기 쉬운 예재로 보도록하자.
좌측의 이미지는 노멀맵, R채널, G채널, B채널 순으로 열거한 이미지이다.
좌측의 이미지에 라이팅이 얹어지면 우측처럼 라이팅이 렌더링된다.
차이가 가장 극명한 R채널을 유심히 보도록 하자.
우측 이미지는 각 지점을 스포이드로 찍어 컬러의 RGB값을 본 것이다.
R채널은 좌우에 대한 정보를 가진다고 위해서 설명하였다.
쉽게 생각해서 -1~1을 0~255로 나타낸것이다.
자세한 설명이 있는 글
[study] 노멀맵은 왜 파랗게 보일까? / by. gardiss
차후 설명 보강 예정
*노멀맵* 을 사용할 때 OpenGL용인지 DirectX용인지 구분하여 사용하여야한다.
참고링크 OpenGL, DirectX용 노멀맵을 구분하는 방법
그 이유는 서로 RGB에서 G채널의 계산이 서로 반대이기 떄문이다.
번외. 픽셀아트를 위한 노멀맵 생성 툴
필자가 사용해본 2가지 노멀맵 생성툴을 소개하겠다.
1.SpriteLamp
사용자가 상,하,좌,우,앞 에서 어떻게 빛을 받을지 Grayscale로 만들어주면 몇가지 설정을 통해서 노멀맵을 생성해준다.
노멀맵 외에도 Depth map과 Ambient occlusion map도 생성가능하다.
다만, 무료 툴은 아니고 유료 툴이다.
2. Laigter
이미지를 집어넣으면 사용자는 생성하고자 하는 맵의 설정을 조금식 건들여주면 자동으로 노멀맵을 생성해준다.
노멀맵 외에도 Specular맵, Parallax맵(노멀맵보다 발전된 형태로 알고있음.), Occlusion맵을 만들 수 있다.
SpriteLamp와 달리 오픈소스 프리웨어로 접근성이 좋다.
다만, 설정수치에 따른 완전자동생성형이므로
자율성은 조금 떨어지는 편이다.
'유니티' 카테고리의 다른 글
13강 (14주차 진행) (0) | 2022.06.09 |
---|---|
12강 (13주차 진행) (0) | 2022.06.01 |
11강(12주차진행) (0) | 2022.05.25 |
10강 (11주차) (0) | 2022.05.18 |
9강 (10주차) (0) | 2022.05.10 |