그린 적 없는 산은 왜 자연스러워 보일까
그림 파일 없이 코드만으로 밤 풍경을 그렸다. 손대지 않은 산 능선이 그럴듯하게 나왔다.
그림 파일이 없는 그림
아래 장면에는 그림 파일이 하나도 없다. 하늘 · 별 · 달 · 산 둘 · 나무 · 전선을 브라우저가 열 때마다 식으로 계산해 칸마다 색을 찍는다. 가로 200칸짜리 작은 캔버스를 크게 키운 것이다.
AI 가 픽셀을 하나하나 찍은 것도, 이미지 생성 모델이 그린 것도 아니다. 그런데 손으로 그린 적 없는 산 능선이 꽤 그럴듯하다. 왜일까.
넓은 굽이 + 좁은 굽이
먼 산의 능선은 이 식 하나다. x 는 왼쪽부터 센 가로 칸 번호, h 는 맨 위에서부터 센 세로 칸 번호다. 칸 x 마다 h 부터 맨 아래까지 산 색으로 칠한다.
h = 84 − ( sin(x·0.035 + 1)·9 + sin(x·0.09)·3 )
앞의 사인파는 굽이 간격이 넓고(×0.035) 높낮이가 크다(×9). 뒤의 사인파는 굽이 간격이 좁고(×0.09) 높낮이가 작다(×3). 넓은 굽이 위에 잔굴곡을 얹으면 능선이 된다. 아래 막대를 움직여 보면 바로 보인다.
맨 아래 '겹' 막대를 올리면, 앞 물결보다 굽이 간격이 절반이고 높이도 절반인 물결이 한 겹씩 더해진다. 그래픽스에서는 흔히 사인파 대신 노이즈를 겹친다. 지형에 쓰는 노이즈는 사인파처럼 매끄럽게 오르내리되 같은 모양이 규칙적으로 되풀이되지 않는 함수다. 노이즈를 여러 겹 더하되 겹마다 간격과 높이를 일정한 비율로 줄이는 방법을 fBm(fractional Brownian motion)이라고 부르고, 흔히 절반씩 줄인다. 위 실험은 사인파로 그 원리만 보여 준 것이다. 가장 널리 쓰인 노이즈인 펄린 노이즈는 1983년 켄 펄린이 영화 CG 를 덜 기계처럼 보이게 하려고 만들었다. 마인크래프트도 노이즈를 겹쳐 지형을 만든다.
앞뒤는 칠하는 순서
깊이는 따로 계산하지 않는다. 하늘 → 별 · 달 → 먼 산 → 가까운 산 → 나무 → 전선 순서로 덧칠하면, 나중에 칠한 것이 먼저 칠한 것을 덮는다. 이 방법을 화가 알고리즘이라고 부르고, 1972년 그래픽스 논문에 나왔다. 2D 게임은 지금도 이렇게 그린다.
위 장면에서 '가까운 것부터' 단추를 눌러 보면 순서만 거꾸로 바뀐다. 나무와 산을 먼저 칠하고 하늘을 맨 마지막에 칠하니, 하늘이 화면 전체를 덮어 산도 나무도 보이지 않는다.
색도 거리를 만든다. 먼 산은 밝은 남색, 가까운 산은 더 어두운 남색으로 칠했다. 먼 것일수록 대비가 낮고 하늘색에 가까워 보이는 공기 원근법이다. 모양이 단순해도 색만으로 거리감이 생긴다.
숫자는 눈이 정한다
새로 찾은 식은 없다. 원 · 포물선 · 사인파 · 해시처럼 그래픽스에서 수십 년 써 온 식을 골랐다. 해시는 번호마다 불규칙해 보이는 값을 돌려주되, 같은 번호에는 늘 같은 값을 돌려주는 함수다. 별 자리를 정할 때 썼기 때문에, 별이 흩어져 보이면서도 새로 고쳐도 같은 자리에 뜬다.
0.035 · 9 · 84 같은 숫자도 계산해서 나온 값이 아니다. 넣고, 찍어 보고, 고쳤다. 그래서 필요한 실력은 수학보다 두 가지다.
- 레퍼런스를 보고 이름 붙일 수 있는 모양으로 쪼개는 눈
- 숫자 하나 바꾸고 바로 다시 보는 빠른 반복
달과 나무는 원, 처진 전선은 포물선이다. 초승달은 밝은 원 위에 하늘색 원을 조금 옮겨 덮은 것이다. 쓰는 식은 열 개 남짓이다.
다음에 해 볼 것
그림은 칸마다 색을 정하는 규칙이다. 먼 것부터, 크기 다른 물결을 더해, 몇 안 되는 색으로 칠하고, 숫자는 눈이 정한다.
그렇다면 구름 가장자리는 몇 겹이면 될까. 위 막대로 먼저 짐작해 보고, 아래 자료에서 확인해 보자.
Why does a mountain nobody drew look natural?
I drew a night scene with code and no image files. The ridge I never touched came out looking right.
A picture with no image files
The scene below has no image files. Every time the page opens, the browser computes the sky, stars, moon, two ranges of hills, trees and a power line from formulas and sets each pixel's color. It's a canvas 200 pixels wide, scaled up.
No AI placed these pixels one by one, and no image model painted them. Yet the ridge line, which nobody drew by hand, looks believable. Why?
Wide bends + tight bends
The far ridge is a single formula. x is the column counted from the left; h is the row counted from the top. For each column x, everything from h down to the bottom is painted in hill color.
h = 84 − ( sin(x·0.035 + 1)·9 + sin(x·0.09)·3 )
The first sine wave has wide bends (×0.035) and large height (×9). The second has tight bends (×0.09) and small height (×3). Lay small wiggles over big curves and you get a ridge. Move the sliders below and you'll see it right away.
Raise the "layers" slider and each step adds another wave with half the spacing and half the height of the one before. Graphics work often layers noise instead of sine waves. The kind of noise used for terrain rises and falls smoothly like a sine wave but doesn't repeat in a regular pattern. Stacking layers of noise while shrinking each layer's spacing and height by a fixed ratio — usually half — is called fBm (fractional Brownian motion). The experiment above only shows the idea with sine waves. Perlin noise, the most widely used kind, was created by Ken Perlin in 1983 to make film CG look less mechanical. Minecraft builds its terrain by layering noise too.
Depth is painting order
Depth isn't computed separately. The scene is painted back to front — sky, stars and moon, far hills, near hills, trees, and finally the power line — and each new shape covers earlier ones where they overlap. This is called the painter's algorithm, from a 1972 graphics paper. 2D games still draw this way.
Press "Nearest first" in the scene above and only the order flips. Trees and hills get painted first and the sky last, so the sky covers the whole frame and nothing else shows.
Color makes distance too. The far hills are a lighter navy, the near hills a darker one. Things farther away have less contrast and drift toward the sky color — aerial perspective. Even with simple shapes, color alone gives a sense of distance.
Your eyes pick the numbers
No new formula was invented here. I chose ones graphics has used for decades: circles, parabolas, sine waves and a hash. A hash is a function that returns an irregular-looking value for each number, but always the same value for the same number. I used it to place the stars, so they look scattered yet land in the same spots every time you reload.
Numbers like 0.035, 9 and 84 weren't calculated either. I put them in, looked, and adjusted. So the skills that matter are less about math and more about two things:
- An eye that breaks a reference into shapes you can name
- Fast iteration — change one number, look again
The moon and the trees are circles; the sagging wire is a parabola. The crescent is a bright circle with a sky-colored circle shifted slightly over it. About a dozen formulas cover everything.
What to try next
A picture is a set of rules that decide the color of each cell. Paint the far things first, add waves of different sizes, use only a few colors, and let your eyes pick the numbers.
So how many layers does the edge of a cloud need? Guess with the sliders above first, then check the resources below.