3D 수학과 소프트웨어 래스터라이저 · 조명과 회전 · 이론
밝기는 어디서 나오는가
한 줄 요약
표면의 밝기는 법선과 빛 방향이 이루는 각도의 코사인에 비례합니다. 이것이 램버트 모델이고, 내적 하나로 계산됩니다.
왜 이게 필요했나
앞 단계까지 하면 삼각형을 색으로 채울 수 있습니다. 그런데 정육면체를 한 가지 색으로 칠하면 육각형 실루엣만 보이고 입체감이 없습니다. 사람이 형태를 읽는 단서가 밝기 차이이기 때문입니다.
밝기를 어떻게 정할까요. 물리적으로는 표면에 도달하는 빛의 양이 문제인데, 같은 세기의 빛이라도 표면이 기울어져 있으면 같은 면적에 더 적은 빛이 떨어집니다. 정면으로 받을 때가 가장 밝고, 90도로 기울면 0 입니다. 이 관계가 정확히 코사인입니다.
어떻게 동작하나
램버트 확산 반사는 이렇게 계산합니다.
n = normalize(표면 법선)l = normalize(빛이 오는 방향)k = max(0, dot(n, l))색 = 기본색 * (ambient + (1 - ambient) * k)max(0, ...) 이 필요한 이유는 내적이 음수면 빛이 표면 뒤에서 오는 것이기 때문입니다. 그대로 두면 음수 밝기가 나와 색이 반대로 넘칩니다. 그리고 ambient 는 다른 표면에서 튕겨 온 빛을 뭉뚱그린 상수인데, 이것이 없으면 빛을 등진 면이 완전히 검게 되어 형태가 사라집니다. 물리적으로 정확한 값은 아니고, 전역 조명을 계산하지 않는 대가로 넣는 보정입니다.
법선을 어디서 얻느냐가 다음 문제입니다. 삼각형마다 하나씩 두면(면 법선) 각 면이 균일한 밝기가 되어 각진 모습이 나옵니다. 정육면체에는 이게 맞습니다. 반대로 꼭짓점마다 법선을 두고 무게중심 좌표로 보간하면(퐁 셰이딩) 곡면이 매끄럽게 보입니다. 구를 삼각형으로 근사할 때 필요한 방식입니다.
법선은 모델 변환을 따라 돌아야 합니다. 물체를 회전시키면 표면도 함께 돌기 때문입니다. 다만 법선은 위치가 아니라 방향이므로 w=0 으로 두고 곱해야 합니다. 그러지 않으면 이동량까지 더해져 법선이 엉뚱한 곳을 가리키고, 물체가 원점에서 멀어질수록 조명이 이상해집니다. 원점 근처에서 시험하면 안 걸리는 종류의 버그입니다.
애니메이션은 별것이 아닙니다. 각도만 바꿔 같은 장면을 여러 번 그리면 됩니다. 다만 회전 행렬을 매 프레임 곱해 누적하면 부동소수점 오차가 쌓여 물체가 서서히 찌그러지므로, 각도를 상태로 들고 프레임마다 행렬을 새로 만드는 편이 안전합니다.
램버트만으로는 금속이나 젖은 표면의 반짝임을 만들 수 없습니다. 확산 반사는 빛을 모든 방향으로 고르게 되쏘는 모델이라 보는 각도와 무관하기 때문입니다. 반짝임은 정반사이고, 보는 방향이 관여합니다. 퐁 모델은 빛의 반사 방향과 시선 방향의 각도를, 블린-퐁 모델은 빛과 시선의 중간 벡터와 법선의 각도를 씁니다. 어느 쪽이든 코사인을 큰 지수로 거듭제곱해 좁은 각도에서만 밝게 만드는 것이 요점이고, 그 지수가 표면의 매끈한 정도를 나타냅니다.
그리고 밝기를 곱한 값을 그대로 파일에 적는 것에도 문제가 있습니다. 화면에 적히는 0~255 는 빛의 세기에 비례하지 않고 감마 곡선을 거친 값이기 때문입니다. 물리적으로 정확하려면 선형 공간에서 조명을 계산한 뒤 마지막에 감마를 적용해야 하고, 이것을 빠뜨리면 밝은 곳이 하얗게 뜨고 어두운 곳이 뭉개집니다. 이 실습은 단순함을 위해 그대로 두지만, 결과가 어딘가 탁해 보인다면 여기를 의심하는 것이 맞습니다.
현장에서 만나는 모습
조명이 반대로 보이는 버그의 대부분은 법선의 부호입니다. 삼각형의 꼭짓점을 도는 순서가 뒤집혀 있으면 외적의 결과가 안쪽을 가리키고, 그러면 빛을 받는 면이 어둡고 등진 면이 밝아집니다. 진단법은 법선을 색으로 칠해 보는 것입니다. (n * 0.5 + 0.5) * 255 로 바꿔 그리면 어느 면이 어느 쪽을 보는지 그림으로 드러납니다.
또 하나는 정규화를 빠뜨리는 것입니다. 법선의 길이가 1 이 아니면 내적이 코사인이 아니게 되어 밝기가 배로 뛰거나 죽습니다. 비균등 크기 조절이 들어간 뒤에 특히 잘 생깁니다.
빛의 종류도 구분해 둘 값어치가 있습니다. 이 실습이 쓰는 것은 방향광입니다. 태양처럼 아주 멀리 있다고 보아 모든 지점에서 방향이 같고 거리에 따른 감쇠가 없습니다. 계산이 가장 싸고 그림자도 다루기 쉽습니다. 반대로 점광은 위치를 갖고, 표면의 각 지점마다 빛 방향을 다시 구해야 하며 거리 제곱에 반비례해 약해집니다. 스포트라이트는 거기에 각도 제한이 붙습니다. 실습에서 방향광부터 시작하는 이유는 조명 계산에서 방향이 하는 일과 거리가 하는 일을 분리해서 보기 위해서입니다.
그리고 밝기와 그림자는 다른 문제입니다. 램버트 계수가 0 이라는 것은 그 면이 빛을 등지고 있다는 뜻일 뿐, 다른 물체에 가려 그늘이 졌다는 뜻이 아닙니다. 가림을 알려면 그 지점에서 광원까지 빛이 실제로 도달하는지 따로 확인해야 하고, 그것이 그림자 맵이나 그림자 광선이 하는 일입니다. 셰이더 코스의 마지막 실습에서 이 확인을 직접 해 보게 됩니다.
다음 실습에서 할 것
직육면체 메시를 만들고 면마다 법선을 구한 뒤, 램버트 모델로 여섯 면을 각각 다른 색과 밝기로 칠합니다. 그다음 각도를 45도씩 바꿔 여덟 장을 그려 브라우저에서 돌려 보고, 마지막에는 여덟 장을 한 장으로 이어 붙인 대조 시트를 만듭니다.