---
title: "평평한 면을 울퉁불퉁하게 보이게 하는 법"
description: "밝기를 정하는 게 모양이 아니라 법선이라면, 모양은 그대로 두고 법선만 자리마다 바꾸면 됩니다. 그 법선을 담아 둔 텍스처가 노멀맵입니다."
author: "유니얼"
pubDate: 2026-09-14
category: "game-dev/graphics"
tags: ["렌더링", "셰이더", "텍스처"]
canonical: https://blog.unialgames.com/posts/normal-map-fake-bumps/
---
벽돌 벽을 만든다고 해봅시다. 줄눈이 파여 있고 벽돌마다 모서리가 둥글게
깎여 있습니다. 그걸 진짜 삼각형으로 만들면 벽 한 장에 정점이 수만 개
들어갑니다.

그런데 조명 계산은 그 삼각형들을 보지 않습니다. 각 픽셀에서
**법선이 어디를 보는지**만 봅니다. 표면의 밝기는 법선과 빛 방향이 이루는
각도로 정해지고, 그 각도를 계산할 때 쓰는 값은 법선 하나뿐입니다.

그러면 모양은 평평하게 두고 법선만 자리마다 다르게 넘겨주면 어떻게 될까요.

## 텍스처는 색만 담으라는 법이 없습니다

텍스처는 그냥 픽셀마다 숫자 세 개가 든 표입니다. 보통 그 세 개를 빨강,
초록, 파랑으로 읽어서 색으로 쓰는 것뿐입니다.

같은 자리에 **방향 벡터의 x, y, z** 를 넣어도 아무 문제가 없습니다.
셰이더가 그걸 색이 아니라 방향으로 해석하면 됩니다.

그게 노멀맵입니다. 픽셀마다 "이 자리에서는 이 방향을 법선으로 써라" 가
적혀 있는 표입니다.

## 같은 띠, 다른 모양

아래에서 세 가지를 같은 자리에서 바꿔 볼 수 있습니다.
위쪽 띠가 화면에 칠해지는 결과, 아래가 그 면을 옆에서 자른 진짜 모양입니다.

<x-lit>
<script type="application/json">
{
  "label": "모양을 바꿨을 때와 법선만 바꿨을 때",
  "modes": ["flat", "map", "geo"],
  "note": "노멀맵과 실제 요철은 위쪽 띠가 완전히 같습니다. 아래쪽 단면만 다릅니다."
}
</script>
</x-lit>

**노멀맵** 과 **실제 요철** 을 번갈아 보면 위쪽 띠가 구분이 안 됩니다.
빛을 좌우로 움직여도 밝은 자리와 어두운 자리가 똑같이 따라 움직입니다.
다른 것은 아래쪽 단면뿐입니다. 한쪽은 선이 곧고 한쪽은 물결칩니다.

이게 노멀 매핑이 파는 거래입니다. **진짜 요철을 만드는 대신 요철이 있었다면
나왔을 법선을 적어 두고, 조명 계산만 속입니다.**

## 셰이더 안에서 일어나는 일

<x-steps>
<script type="application/json">
{
  "steps": [
    { "tag": "STEP 1", "title": "정점 법선이 보간되어 온다", "text": "삼각형 세 정점의 법선이 섞여서 이 픽셀의 법선으로 들어옵니다. 평평한 면이면 전부 같은 방향입니다." },
    { "tag": "STEP 2", "title": "노멀맵을 UV 로 읽는다", "text": "그 픽셀의 UV 좌표로 텍스처를 한 번 샘플링합니다. 색 텍스처를 읽는 것과 똑같은 비용입니다." },
    { "tag": "STEP 3", "title": "읽은 값을 방향으로 되돌린다", "text": "0에서 1 사이로 저장된 세 숫자를 -1에서 1 사이의 방향 벡터로 펴 줍니다." },
    { "tag": "STEP 4", "title": "그 법선으로 조명을 계산한다", "text": "정점에서 온 법선 대신 이 값을 씁니다. 나머지 계산은 하나도 안 바뀝니다." }
  ]
}
</script>
</x-steps>

바뀌는 건 조명 계산에 들어가는 법선 하나뿐입니다. 빛의 종류, 그림자,
반사 계산은 손댈 게 없습니다. 그래서 어느 조명 모델에나 그대로 얹힙니다.

## 무엇을 얻고 무엇을 치르나

<x-tabs>
<div class="pc" data-tab="정점을 늘린다">
<div class="pc-col" data-kind="good">
<span class="pc-tag">장점</span>
<ul><li><b>진짜 모양이 바뀐다.</b> 옆에서 봐도 파인 자리가 파여 보이고, 실루엣도 울퉁불퉁합니다.</li><li><b>그림자가 생긴다.</b> 튀어나온 부분이 옆을 가립니다.</li></ul>
</div>
<div class="pc-col" data-kind="bad">
<span class="pc-tag">단점</span>
<ul><li><b>정점 수가 폭발한다.</b> 벽 한 장에 수만 개가 들어가고 메모리와 전송량이 같이 늡니다.</li><li><b>멀리 있어도 비싸다.</b> 화면에서 몇 픽셀이 되어도 정점은 그대로 다 처리합니다.</li></ul>
</div>
</div>
<div class="pc" data-tab="노멀맵을 쓴다">
<div class="pc-col" data-kind="good">
<span class="pc-tag">장점</span>
<ul><li><b>정점이 넷이면 된다.</b> 사각형 한 장에 텍스처 한 장입니다.</li><li><b>비용이 픽셀에만 붙는다.</b> 텍스처 샘플 한 번과 벡터 연산 몇 줄입니다.</li><li><b>재질처럼 돌려 쓴다.</b> 벽돌 노멀맵 하나를 벽에도 바닥에도 붙입니다.</li></ul>
</div>
<div class="pc-col" data-kind="bad">
<span class="pc-tag">단점</span>
<ul><li><b>모양은 평평한 채다.</b> 실루엣은 그대로 곧은 선입니다.</li><li><b>텍스처 메모리를 먹는다.</b> 색 텍스처와 같은 해상도라면 용량이 한 벌 더 듭니다.</li></ul>
</div>
</div>
</x-tabs>

| 언제 | 무엇을 쓰나 |
| --- | --- |
| 표면의 잔 굴곡, 줄눈, 긁힘, 천의 결 | 노멀맵 |
| 가까이서 실루엣에 걸리는 큰 굴곡 | 실제 지오메트리 |
| 카메라가 스칠 듯 낮게 지나가는 바닥 | 노멀맵만으로는 부족합니다 |

## 노멀맵은 손으로 그리지 않습니다

보통은 **하이폴리 모델을 먼저 만들고 굽습니다(bake)**.

같은 물체를 정점 수백만 개짜리로 조각한 다음, 저폴리 모델의 각 픽셀에서
바깥으로 광선을 쏴서 하이폴리 표면에 맞은 자리의 법선을 받아 적습니다.
그 결과가 노멀맵입니다.

높이맵(회색 이미지)에서 기울기를 계산해 만드는 방법도 있습니다. 벽돌이나
천처럼 규칙적인 무늬는 이쪽이 빠릅니다.

## 정리

- 조명 계산이 보는 것은 법선 하나뿐이라, 법선만 바꿔치기하면 모양을 안 바꾸고도 요철이 보입니다.
- 노멀맵은 픽셀마다 쓸 법선 방향을 담아 둔 텍스처입니다. 색 텍스처와 자료 구조가 같고 해석만 다릅니다.
- 셰이더에서 바뀌는 건 조명에 넘기는 법선뿐이라 어떤 조명 모델에나 그대로 얹힙니다.
- 값은 대개 하이폴리 모델에서 구워 냅니다.
- 치르는 값은 실루엣입니다. 옆에서 자른 단면은 여전히 곧은 선입니다.