# Part 13 | 커스텀 라이트 - Lambert Light

Shader Graph의 라이팅 계산법은 Lit와 Unlit 정도.

Lit 셰이더는 퀄리티는 좋지만 모바일에선 약간 무거움. 물리적으로 옳지 않은 창의적 효과 표현 못 함. 라이팅 구조를 커스텀으로 조정해서 창의적 표현해야됨.

이를 위해 SimpleLit이라 부르는 전통적 라이팅 구조를 만들어보는 실습할 것.

## 01 커스텀 라이트 기본형 만들기

라이팅 구조를 새로 만들어야 하니 아무 라이팅 연산 없는 Unlit Shader Graph를 만든다. Normal Vector 노드를 추가한다.\
Light Vector를 받아오기 위해 Custom Function 노드를 추가한다.\
Custom Function 노드는 직접 HLSLI 코딩으로 노드에 없는 기능을 작성하는 노드이다.

**Normal Vector 노드 Space 옵션**

* Object : 로컬 좌표계
* View : 카메라 좌표계
* World : 월드 좌표계
* Tangent : 각 면이 가지고 있는 좌표계. 모두 0,0,1이라 시각화하면 전부 파랗다. 3D를 2D처럼 생각하는 좌표계이므로 노말맵에서 주로 사용된다.

**Custom Function 노드의 Graph Inspector 옵션**

* Precision : 사용될 값의 정밀도. 일반적으론 높은 품질의 Single로 해도 괜찮다.
  * HLSL 함수 이름에 Precision에 맞춰서 자동으로 접미어가 붙는다
  * 출력 변수의 단위가 Precision과 맞지 않으면 에러가 발생한다
  * Inherit : 해당 노드 이전의 노드에서 상속받은 정밀도로 설정
  * Single : Float 32비트
  * Half : 16비트
* Preview : 노드 프리뷰 2D로 볼건지 3D로 볼건지
* Inputs / Outputs : 함수 입력값과 출력값
* Type
  * File : 외부 HLSL 파일을 연결
  * String : HLSL 코드를 노드에 직접 작성

Custom Function의 Precision은 Single, Preview는 Preview3D, Outputs는 Direction이라는 이름을 가진 Vector3로 설정해놓고

```cpp
#ifdef SHADERGRAPH_PREVIEW
	Direction = float3(1,1,1);
#else
	Light light = GetMainLight();
	Direction = light.direction;
#endif
```

Type을 String으로 바꾼 뒤 Name은 CustomLight, Body에는 위의 코드를 적어준다.\
이후 Directional Light의 각도를 바꿔주면 색깔도 바뀌는 것을 확인할 수 있다.

코드 설명

* 프리뷰면 direction은 float3(1,1,1)로 둬라 (분홍색이면 보기 안 좋으니까 설정)
* Precision을 Half로 했다면 float3가 아니라 half3로 써줬어야 했을 것
* light를 받아서 방향을 direction에 할당
* 받아오는 light는 MainLight, 즉 Directional Light. 다른 라이트들은 Additional 라이트라 불리고, 받아오는 방법도 다르다.

> **Troubleshoting**\
> 책에서 하라는대로 Outputs에 변수를 추가하긴 했는데 이름을 Direction으로 안 바꿔서 변수가 할당되지 못하는 오류가 생겼었음.

![](https://velog.velcdn.com/images/biomatrix117/post/9913cfce-fd59-4461-b2f0-bf9a85fd94df/image.png)

Normal Vector와 Dot Product 노드를 사용해 내적해주면 빛이 연산된다.\
내적 연산하면 -1 값도 나오는데, 이를 Saturate, Maximum, Clamp 노드 등을 이용해 0으로 잘라준다.\
앰비언트 라이트나 추가 라이트를 비출 때 음수에 숫자를 더하게 되어서 밝아지지 않는 등 문제가 생길 수 있기 때문이다.

![](https://velog.velcdn.com/images/biomatrix117/post/6c904b2a-cf16-49b2-b1ba-bd09e96794c9/image.png)

버텍스의 Normal Vector를 Normal Map 텍스쳐로 대체해주면 디테일이 살아난다.\
이 과정에서 Tangent Space인 Normal map을 World Space로 바꿔주기 위해 Transform 노드를 이용한다.\
Transform 노드에서 Type도 Direction으로 바꿔줘야 함.

![](https://velog.velcdn.com/images/biomatrix117/post/1b8c7e78-affe-4f51-9e33-c76d404ec333/image.png)

래버트 라이트는 가벼운 조명 공식이지만, cos 그래프 특성상 밝다가 너무 급격하게 음영이 떨어진다.\
하프 램버트 공식은 물리적으론 옳지 않지만 보기 좋기 때문에 만들어졌다.

이전의 NdotL에서 Saturate를 통해 무시해줬던 음수 부분을 끌어올리기 위해\
`*0.5+0.5`를 추가로 연산해주면 음영이 조금 더 부드러워진다.

실제로 사용할 땐 빛이 180도까지 미치다보니 음영이 너무 부드러워져서\
3제곱 혹은 `*0.7+0.3` 정도로 사용한다. (저자는 `*0.7+0.3`가 더 가벼워서 좋아한다고 함)

> **NdotL?**\
> N(Normal)과 L(Light)를 dot(내적)한 결과물

## 02 램버트 라이트 완성해 보기

지금까지 구현한 것 : 빛 방향에 따른 음영 구현해야 할 것 : 조명의 색상과 강도, Albedo 텍스쳐, 환경광 등등

![](https://velog.velcdn.com/images/biomatrix117/post/279d84b7-8fd3-4fe1-8834-8d6d63772e15/image.png)

* Base Color 텍스쳐를 꺼내서 NdotL 결과물과 곱해준다
* 커스텀 노드에 `Color = light.color;`을 추가하여 빛의 색깔을 받아온다
* Color를 NdotL 결과물과 곱해주는 과정을 추가한다

이제 조명의 색상과 밝기를 변화하면 모델의 색상이 변하는 것을 확인할 수 있다.\
이것은 '빛을 받는 부분은 자신이 가진 색상을 사방으로 방출해서 물체의 색이 보이게 된다'는 난반사(Diffuse Reflection)를 NdotL로 간단하게 구현한 것.

**환경광**

모든 난반사 물체는 자신의 색의 빛을 사방으로 연하게 반사한다. 이렇게 한 번 이상 반사된 빛이 다른 물체에 닿아 그 물체를 밝히면 이것을 환경광(Ambient Light)라 한다. 환경광이 아주 강해서 조명처럼 색이 묻어나는 것을 컬러 블리딩(Color Bleeding)이라 한다.

아주 맑은 날 야외라면 하늘에는 파란 환경광, 대지에는 지면 색의 환경광이 보일 것.\
흐린 날에는 주광도 약해져서, 물체에 음영이 강하지 않아 환경 차폐(Ambient Occlusion)만 강조되어 보일 것.

![](https://velog.velcdn.com/images/biomatrix117/post/66840bb6-5c7f-4274-9f31-e9b933c62733/image.png)

유니티는 스카이 박스에 환경광이 있다. 이를 받아오려면 Baked GI 노드를 사용하면 된다.

* World Space Normal을 연결한 Baked GI를 텍스쳐와 곱해준다
* 그 결과물을 Diffuse 연산에 Add해준다

![](https://velog.velcdn.com/images/biomatrix117/post/291339ea-33db-43eb-bd8c-88411c8eb73f/image.png)

Ambient Occlusio은 Ambient에 곱해주면 된다.

**남은 구현할 것**

* 스페큘러(정반사)
* 그림자 드리우기
* 그림자 받기


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lazyartisan.gitbook.io/note/main-page/books/urp/part-13-or-lambert-light.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
