Develog
4. Shader Graph 본문
Shader Graph
셰이더 그래프(Shader Graph)는 노드 기반으로 셰이더(Shader)를 제작할 수 있는 패키지.
렌더 파이프라인(Render Pipeline)의 종류와 무관하게 사용할 수 있다.
셰이더 그래프으로 제작한 에셋을 하나의 렌더 파이프라인에서 제작한 다음, 다른 렌더 파이프라인으로 옮기는 것이 이론상 가능은 하지만 호환성 문제가 있어 권장되지는 않는다. 가급적 셰이더 그래프를 작성하기 전에 렌더 파이프라인을 먼저 설정하는 것이 좋다.
셰이더 그래프 에셋을 생성하면 아래와 같은 기본 설정값을 볼 수 있다.
위 사진 속 Vertex, Fragment라고 적힌 2개의 박스는 각각 셰이더 스테이지(Shader Stage)를 의미한다.
즉, 별다른 처리 없이 URP 셰이더 그래프를 처음 사용하게 된다면 기본적으로 버텍스 셰이더 스테이지(Vertex Shader Stage)와 프래그먼트 셰이더 스테이지(Fragment Shader Stage)를 하나씩 제공한다.
코드를 직접 작성한다면 Tessellation, Geometry 등 더 많은 셰이더 스테이지를 구현하는 것도 가능하지만, 셰이더 그래프만 사용한다면 이는 불가능하다.
위 사진에서 알 수 있듯이, 셰이더 스테이지는 버텍스 셰이더를 먼저 계산한다. 그 계산 결과는 프래그먼트 셰이더 스테이지에서 보간된다.
Vertex Shader Stage
노드(Node)를 이용해서 정점 좌표에 계산을 추가하는 셰이더 스테이지.
자주 사용되는 값으로 Position, Normal, Tangent가 있다. 그 중에서 Position 값은 필수이다.
만약, UV를 비롯하여 필요한 값이 더 존재한다면 Add Block Node 메뉴를 통해 직접 추가하는 것도 가능하다.
프래그먼트 셰이더 스테이지에서 무거운 계산을 해야하는데 디테일이 크게 중요하지 않은 작업이라면 해당 작업을 버텍스 셰이더 스테이지로 넘겨서 미리 계산하도록 수정하면 픽셀 단위로 세밀한 작업은 불가능하지만, 성능 개선에는 많은 도움이 된다.
예) UV 좌표는 버텍스 셰이더 스테이지에서 처리해도 큰 문제가 없지만, 텍스처 샘플링 데이터는 불가능.
Space
스페이스(Space)는 다양한 기준점을 바탕으로 아래와 같이 종류가 구분된다. 이러한 여러 스페이스는 서로 변환도 가능하다.
- 월드 스페이스(World Space): (0, 0, 0)을 기준으로 좌표를 측정하는 스페이스.
- 오브젝트 스페이스(Object Space): 현재 렌더링 중인 내 오브젝트 기준으로 좌표를 측정하는 스페이스.
- 탄젠트 스페이스(Tangent Space): 면 단위 스페이스. 면의 앞면이 Z 축이 된다.
- 클립 스페이스(Clip Space): 카메라 단위 스페이스, 카메라의 앞/뒤가 Z축이 된다.
무게 중심 좌표계 연산 (보간법)

빨간색 버텍스와 초록색 버텍스 사이에 있는 픽셀들은 "완전한 빨강도, 완전한 초록도 아닌 중간색"을 가져야한다. 이때 두 지점의 거리 비율에 따라 값을 계산해 채워 넣는 과정인 보간을 통해 중간색이 결정된다.
이러한 보간 작업은 레스터라이저(Rasteriser)라는 이름의 GPU 안에 내장된 파이프라인이 여러 버텍스를 연결하여 면을 생성하는 작업 과정에서 함께 처리한다. 이 때문에 셰이더 코드를 이용해 직접 보간 작업 그 자체를 커스터마이징 하는 것은 불가능하다.
Xcode 프레임 디버거처럼 여러 IDE를 살펴보면 디버거를 통해 셰이더가 어디서 가장 느린지 알려주는 경우가 많아서 최적화에 많은 도움이 된다. 디버거 분석 결과, 픽셀 셰이더 연산이 느리다는 결과가 나오면 해당 부분을 최적화하면 되고, 메모리 참조 부분이 느리다면 메모리 접근 빈도를 줄이면 된다. 여기서 만약, 보간 작업의 속도가 느리다는 결과가 나오면 보간을 하는 채널의 수 자체를 줄여야 한다.
셰이더 그래프를 활용한 툰 셰이딩(Toon Shading) 실습
URP 프로젝트에서 기본 오브젝트를 생성하면 아래와 같이 수정할 수 없는 머티리얼을 함께 제공함을 알 수 있다.
셰이더 그래프로 머티리얼을 커스터마이징하려면 아래와 같이 새로 생성한 머티리얼로 작업해야 한다.
셰이더 코드를 사용하려면 'Create > Shader'로 이동하고, 셰이더 그래프를 사용하려면 'Create > Shader Graph'로 이동하면 된다.
URP 셰이더 그래프는 아래와 같이 다양한 종류로 제공된다.
- Sprite ~ Shader Graph: 2D 에셋에 셰이더를 넣는 방식을 사용한다.
- Decal Shader Graph: 렌더링이 끝난 오브젝트 표면 위에 데칼(Decal)을 그리고자 할 때 사용한다.
- Fullscreen Shader Graph: 화면에 적용하는 효과, 이미지 이펙트 등을 만들 때 사용한다.
- Lit Shader Graph: 빛을 받는 상태를 적용하는 셰이더 그래프. Unity의 광원과 물리적 상호작용(PBR; Physically Based Rendering)을 한다.
- Unlit Shader Graph: 빛을 받는 상태를 적용하지 않는 셰이더 그래프. PBR을 적용하지 않는다.
PBR (Physically Based Rendering)이란?
PBR은 현실 세계의 물리 법칙을 모방하여 빛이 물체 표면에서 어떻게 반사되고 흡수되는지를 계산하는 모델이다.
주변 광원(Ambient Light), 직사광(Direct Light), 그림자(Shadow)에 모두 반응하며 금속성(Metallic)이나 매끄러움(Smoothness)와 같은 속성에 따라 빛 반사도 달라진다.
Lit Shader Graph는 PBR을 사용하지만, Unlit Shader Graph는 PBR을 사용하지 않는다.
이 때문에 두 셰이더 그래프는 아래와 같은 차이점을 보인다.
| 구분 | Lit (릿) | Unlit (언릿) |
|---|---|---|
| 빛의 영향 | 광원의 위치, 강도, 색상에 영향을 받음 | 빛의 영향을 전혀 받지 않음 |
| 입체감 | 명암과 그림자가 생겨 입체적으로 보임 | 명암이 없어 평면적으로 보임 (Self-illuminated) |
| 연산량 | 빛을 계산해야 하므로 상대적으로 무거움 | 단순 텍스처 출력이라 매우 가볍고 빠름 |
| 용도 | 일반적인 3D 오브젝트, 캐릭터, 배경 등 | UI, 이펙트, 카툰풍 그래픽, 가이드 라인 등 |
셰이더 그래프를 사용한 셰이더 제작은 노드 기반 프로그래밍과 유사한 방식을 사용한다.
아래 사진처럼 프로퍼티(Property)를 먼저 생성한 뒤, 이를 드래그&드롭하여 컴포넌트에 선으로 연결하는 방식이다.
위와 같이 'Color' 프로퍼티를 연결하면 Inspector 창의 머티리얼 항목에서도 Color를 수정할 수 있는 프로퍼티가 새로 등장한다. 이때부터는 셰이더 그래프를 따로 수정하지 않고도 머티리얼 속성만으로도 색상을 변경할 수 있다.
프로퍼티를 연결하기 위해서는 셰이더 스테이지의 항목과 타입(Type)이 동일해야 한다.
프래그먼트 셰이더 스테이지의 Base Color 항목은 'Color' 타입의 프로퍼티만 연결이 가능하므로, 만약 Texture2D를 이용해 Base Color에 연결해주고 싶다면 아래와 같이 Texture2D 프로퍼티에서 컬러 항목만 추출하는 '샘플' 작업이 선행되어야 한다.
이 상태에서 Multiply를 이용하면 아래와 같이 2개의 색상을 섞는 것도 가능하다.
라이팅(Lighting)을 구현하기 위해서는 빛을 정의하는 '광원(Light)'과 오브젝트의 표면을 정의하는 '노멀(Normal)'이 필요하다.
광원과 노멀이 수직을 이루는 상태가 되면 가장 밝아지고, 광원과 노멀이 수평을 이루는 상태가 되면 가장 어두워지는데 이처럼 광원과 노멀의 각도에 따라 밝기가 변하는 원리를 '람베르트 코사인 법칙'이라고 부른다.
람베르트 코사인 법칙 (Lambert's Cosine Law)
광학에서 복사 강도 또는 광도가 관찰자의 시선과 표면 법선 사이의 각도x의 코사인에 정비례한다는 법칙.
손전등을 벽에 비춘다고 가정하면, 손전등과 벽이 수직일 때 빛 에너지가 최소 면적에 집중되므로 Cos(0) = 1이 성립하며 손전등과 벽이 수평일 때는 Cos(90) = 0이 성립한다. 만약 각도(x)가 기울어지게 된다면, 빛이 퍼지는 영역이 1 / Cos(x) 배만큼 넓어지게 되어 단위 면적당 받는 에너지(밝기)는 그 역수인 Cos(x)에 비례하여 줄어들게 된다.
컴퓨터 그래픽스에서는 두 벡터의 방향 관계를 계산할 때 효율을 위해 내적과 정규화를 주로 사용한다.
광원 방향 벡터(L)과 표면의 노멀 벡터(N)을 모두 단위 벡터로 정규화하고 광도(Light Intensity)를 내적을 통해 계산한다고 가정하면, LI = N x L = 1 x 1 x Cos(x) 수식에 따라 광도는 Cos(x) 그 자체가 되어 복잡한 수식과 과정 없이 코사인 값만으로도 광원을 연산할 수 있음을 알 수 있다.
Unity에서는 아래와 같이 Dot Product라는 노드를 이용해 코사인을 직접 연산하는 방법을 대신할 수 있다.
이때 광원과 노멀 벡터를 Dot Product 노드에 직결하게 되면 벡터의 방향 때문에 빛과 그림자의 방향이 반대가 되는 잘못된 결과가 나온다. 이는 광원에서 노멀을 바라보는 벡터와 노멀에서 광원을 바라보는 벡터가 서로를 마주보고 있기 때문이다. 즉, 광원 벡터와 노멀 벡터가 이루는 각도가 180도이므로, Cos(180) = -1이 성립한다.
이러한 문제를 해결하기 위해서는 노멀에서 광원을 바라보는 벡터를 기준으로 연산이 이루어져야 한다. 즉, 광원에서 노멀을 바라보는 벡터에 위 사진처럼 (-1, -1, -1) 벡터를 곱하여 반전된 값을 적용해야 한다. 이렇게 되면 광원 벡터와 노멀 벡터가 이루는 각도가 0도이므로, Cos(0) = 1이 성립한다.
툰 셰이더를 제작하기 위해서는 빛을 받는 영역과 그림자가 생기는 영역 중간에 존재하는 그라데이션 효과를 제거해야 한다.
위 사진처럼 자연스러운 그라데이션 효과를 적용하고 싶다면 Saturate 노드를 사용하지만, 툰 셰이더처럼 이를 의도적으로 수정하려면 Step 노드를 사용하면 된다. Step 노드는 Edge의 값을 기준으로 하여 Edge 값보다 크면 1, 작으면 0으로 표현한다.
완전히 검은색으로만 표현되고 있는 그림자 부분의 색상을 조정하고 싶다면 선형 보간법을 구현하는 Lerp 노드를 사용한다.
그림자의 색상 노드 그룹(A), 빛이 받을 때의 색상 노드 그룹(B)를 Dot Product 노드와 Step 노드를 이용해 만든 광원 표현 노드 그룹(T)과 함께 Lerp 노드에 연결하면 된다.
이를 셰이더 그래프로 표현하면 아래와 같다.
빛을 받는 부분에 프레넬 효과(Fresnel Effect)를 적용하면 반사광에 대한 효과를 표현할 수 있다.
프레넬 효과는 관찰자가 물체를 바라보는 각도에 따라 반사율이 달라지는 현상을 말하며, 물체를 정면으로 바라볼 때보다 측면에서 바라볼 때 반사광이 훨씬 강해진다.
잔잔한 호수에서 발밑의 물을 수직으로 내려다보면 물속이 투명하게 보이지만, 먼 곳의 수평선을 바라봤을 때는 하늘이 물에 반사되어 거울처럼 보이는 현상이 프레넬 효과의 대표적인 예시이다.
이러한 프레넬 효과는 물체의 입체감과 재질감을 살리는 핵심 요소로 사용된다.

위와 같이 광원 효과를 구현한 노드 그룹과 프레넬 효과를 구현한 Fresnel Effect 노드를 곱하게 되면 빛이 있는 부분만 프레넬 효과가 적용되도록 설정할 수 있다.
광원을 연산할 때 컴퓨터 상에서는 빛이 있는 곳은 1, 빛이 없는 곳은 0으로 표현된다. 이 때문에 프레넬 효과를 곱셈 연산으로 적용할 경우, 그림자가 있는(빛이 없는) 곳은 프레넬 효과에 0을 곱하게 되므로 효과가 적용되지 않는 결과를 만들어낼 수 있다.
프레넬 효과를 사용할 때도 아래와 같이 Step 노드를 적용하여 그라데이션 없이 단계별로 끊어지는 듯한 효과를 적용하는 것이 가능하다.
램버트(Lambert) 반사 모델
램버트 반사 모델은 빛을 받았을 때 어느 방향에서 봐도 밝기가 동일하게 보이는 완벽한 무광인 표면의 빛 반사를 모델링하는 방법이다.
밝기는 표면이 빛을 얼마나 정면으로 받고 있는가에 영향을 받는데, 정면에 가까워질수록 밝기가 밝아진다. 즉, 빛의 방향에 따라서 표면의 밝기도 변하기 위해서는 램버트 반사 모델의 적용이 필요하다.
셰이더의 덧셈과 곱셈
셰이더 구현 과정에서 빛 효과를 표현할 때 곱셈 연산은 빛이 다른 요소에 의해 영향을 받는 관계를, 덧셈 연산은 서로 독립적인 효과를 합치는 관계를 표현하기 위해 사용된다.
예를 들어, 램버트 반사 효과는 빛을 얼마나 정면으로 받고 있는가에 영향을 받아 효과가 달라진다. 이 경우 곱셈 연산을 사용해야 한다. 하지만 프레넬 효과는 시선이 오브젝트 가장자리를 스칠 때 밝아지는 효과이기 때문에 기본 조명이나 그림자와는 별개의 개념으로 다루어진다. 즉, 덧셈 연산을 이용하면 빛을 받는 부분이든 그림자가 진 부분이든 상관 없이 프레넬 효과는 독립적으로 그 효과가 적용될 수 있다.
'Technology > Shading & Rendering' 카테고리의 다른 글
| 3. Render Pipeline (0) | 2026.02.14 |
|---|---|
| 2. Graphics API (0) | 2026.01.25 |
| 1. Shader (0) | 2026.01.25 |