22.02.25 쉐이더 라이팅, 커스텀 라이트

2022. 2. 26. 14:13UnityShader/수업 내용

쉐이더 라이팅의 종류

 

  • 램버트

반사광이 없는 가장 가벼운 라이팅 구현 방식.

단지 빛에 의한 밝고 어두움만 구현했다.

이 라이팅을 사용하려면 pragma의 surface surf Lambert 를 써야 하고,

surf 함수의 두번째 인수의 타입을 surfaceOutput으로 지정해야 한다.

 

  • 블린퐁

스페큘라(반사광)을 가볍게 구현했다. (스탠다드 방식과 그 구현법이 다름)

블린퐁 방식은 스페큘라를 구현하기 위해 프로퍼티에 반드시 _SpecColor 를 선언해줘야 한다.

또한 변수는 절대 생성해선 안 되고, 

surf함수에서 o.Specular와 o.Gloss 값을 float로 조절해준다.

o.Specular는 반사광의 크기, o.Gloss는 반사광의 강도를 뜻한다.

#pragma surface surf BlinnPhong 으로 써줘야 하고,

램버트 방식과 마찬가지로 surf 함수의 두번째 인수의 타입을 surfaceOutput으로 지정해야 한다.

 

  • 스탠다드

#pragma surface surf Standard 으로 써줘야 하고,

surf 함수의 두번째 인수의 타입을 surfaceOutputStandard로 지정해야 한다.

물리기반 쉐이더 라이트이기 때문에 세 가지 중 가장 무겁고, 사양을 필요로 한다.

현실의 거의 모든 것을 광범위하게 표현할 수 있다.

 


디지털 라이트의 분류

 

  • 디렉셔널 라이트

직진성을 가진 조명이다.

세 가지 중 가장 가벼운 라이트로 통한다.

 

  • 포인트 라이트

점 모양의 광원에서 빛이 사방으로 뻗어나간다.

디렉셔널보다 무겁다.

 

  • 스팟 라이트

특정 부분을 강조하거나 표현할 때 사용된다.

 


디지털 라이트의 원리

 

물체에 빛을 비추면 밝아진다.

프로그램 상에서 오브젝트에 빛이 비춰지고 있음을 판별하는 기준은 

빛이 향하는 방향과 물체가 향하는 방향(노말 방향)이 같은가이다.

즉 빛의 방향과 물체의 방향이 같으면 밝아지고, 다르면 어두워진다.

이 방향의 각도 차이가 0도일 때 가장 밝고, 90도일 때 가장 어둡다.

 

그래서 물체의 방향은 어떻게 구하나?

 

 

[벡터]


벡터는 위치 벡터와 방향 벡터가 있다.

여기선 방향 벡터를 다룬다.

벡터는 방향과 크기를 나타낸다.

여기선 크기가 1인 단위벡터를 사용한다. 크기가 1이여야 계산하기 편하기 때문이다.

 

  • 방향 벡터의 특성

벡터의 위치는 의미가 없다.

벡터는 방향과 크기가 같다면 같은 벡터이다.

벡터를 음수로 바꾸어 주면 정반대의 방향을 나타낸다.

 

쉐이더의 rgb에서 방향 벡터는 컬러로 나타낼 수 있다.

쉐이더의 uv에서 방향 벡터는 위치로 나타낼 수 있다.

그 방향 벡터는 그 자체로 방향을 나타낼 수 있다.

즉, 방향 벡터는 숫자이며 컬러이자 위치이자 방향이다.

3차원 벡터는 float3 타입으로 xyz는 rgb와 동일하다.

ex) float3 (1, 0, 0)은 빨강색이기도 하면서 x축으로 길이가 1인 벡터이기도 하다.

 

※ 노말 맵 또한 이러한 벡터의 특성을 이용하고 있는데, 노말 맵에서는 rgb라는 xyz를 나타내고 있다.노말 맵은 벡터들의 픽셀 집합인데, 라이트 연산을 할 때 노말 맵이 가지고 있는 rgb 데이터를 이용해가짜 음영을 만들어내어 디테일을 살리기 위한 것이 노말 맵이다.

rgb데이터로 물체의 노말방향을 바꾸어 음영을 만들어내는 것인가?

 

 

  • 벡터의 연산

곱셈 : 길이가 1 이하인 숫자를 곱하면 길이가 짧아진다.

 

덧셈 : 길이가 같은 두 벡터를 더하면 두 벡터 사이의 절반인 각도가 나온다.

만약 두 벡터의 길이가 1이 아니라면 그림처럼 절반 각도가 아닌 결과물이 나오므로,

벡터의 길이가 1임을 확인해야 한다.

또한, 길이가 1인 벡터를 더해 나온 절반 각도의 벡터는 길이가 1을 넘게 되므로,

길이를 다시 1로 맞춰 주어야 한다.

 

뺄셈 : A - B는 A + (-B) 와 같다.

 

 

  • 벡터의 내적(dot)과 외적(cross)

내적 : 두 벡터의 각도의 차이를 숫자로 표현한 것이다.

그러므로 두 벡터의 내적한 결과 값은 컬러 값 (-1 ~ 1)을 의미한다.

 

즉, A벡터가 버텍스가 갖고 있는 노말 벡터, B벡터가 빛의 방향 벡터라고 했을 때

두 벡터를 내적해 나오는 값은 color이다.

버텍스의 노말 방향과 빛의 방향이 같은 방향을 바라볼 때 가장 밝은 color값이 나온다.

위의 그림이 바로 위에서 설명된 Lambert 라이팅이 되는 것이다.

 

 


커스텀 라이트 구현하기

 

 

  • 매개변수 설명

라이팅 함수는 인수를 3개 받는다.

위의 사진에서 SurfaceOutput 타입의 s 변수는 surf 함수가 받는 inout 매개변수와 같은 객체이다.

그 객체는 유니티에서 받아온 구조체 값이며, 

이 객체는 surf에서 모든 연산이 끝나고 난 후에 라이팅 함수로 전달된다.

 

LightDir 변수는 조명의 방향 벡터이다.

조명 계산을 편하게 사용하기 위해 방향이 뒤집혀진 단위 벡터 상태이다.

 

atten 변수는 거리에 따른 빛의 감쇠를 표현해주는 float이다.

포인트 라이트에서 물체와 조명이 멀어질 때마다 색을 점점 어둡게 감쇠시키는 것이다.

 

 

  • 커스텀 라이트의 문제점과 해결책

위 코드의 36줄에서 물체의 노말방향과 조명의 방향을 내적해서 음영을 나타낸다.

그러나 내적값은 -1까지 떨어지는 반면, 물체의 색은 0 이하로 내려가지 않기 때문에

실제로 물체와 빛의 각도가 가장 어두워져야 할 각도가 아님에도 가장 어두운 색으로 표현이 된다.

 

이 문제점을 해결하기 위해 하프 램버트라는 게 있다.

 

하프 램버트는 매우 단순한 공식으로

내적값이 -1일 때, 그 값에 0.5를 곱하고 0.5를 더하는 것이다.

그렇게 되면 최소값은 0이므로 0 이하로 떨어질 일이 없다.

 

그러나 이 하프 램버트 또한 문제가 있는데,

음영이 너무 얕게 표현된다는 것이다.

 

그리고 이를 해결하기 위해 하프램버트로 연산한 값을 3으로 제곱해주는 것이다.

그러면 훨씬 자연스러운 음영이 나오게 된다.

 

총정리하자면

하프램버트는 기존 램버트 방식의 깜박이 없이 훅 어두워지는 현상을 방지하고자 만든 방식으로,
내적값을 0~1 사이로 만들어준다.

그러나 너무 인위적인 느낌이 나기 때문에 하프 램버트 결과값에서 3을 제곱한다.