Mesh 표면에서 확산이나 전파 같은 시뮬레이션을 하려면 각 계산 단위의 상태와 이웃을 정해야 한다. 한 가지 방법은 표면을 UV 공간에 펼친 뒤, UV atlas를 격자로 나누고 각 texel을 simulation node로 사용하는 것이다.
이때 단순히 텍스처의 좌우·상하 texel만 이웃으로 보면 표면의 연결성을 놓친다. UV chart 사이에는 빈 공간이 있고, 3D에서 붙어 있어도 UV에서는 끊긴 seam이 있기 때문이다. 따라서 먼저 Mesh 표면과 UV 격자 사이의 대응 관계를 만들고, 이후 그 대응을 이용해 이웃 관계를 구성해야 한다.
세 공간을 구분하기
이 작업에는 세 가지 공간이 등장한다.
| 공간 | 무엇을 표현하는가? | 좌표 예 |
|---|---|---|
| Mesh 공간 | 실제 표면의 위치와 연결 | 정점 위치, triangle, 원래 위치 인덱스 |
| UV 공간 | 표면을 텍스처 위에 펼친 위치 | $(u,v)$ |
| Grid 공간 | 격자 셀 또는 texel의 인덱스 | $(x,y)$ |
UV는 3D 표면을 격자에 배치하는 매개 좌표다. UV 좌표가 서로 가깝다고 해서 두 표면점이 3D에서도 가깝다는 보장은 없다. 반대로 3D에서 같은 edge를 공유하는 점들이 seam 양쪽에서 서로 다른 UV를 가질 수도 있다.
Mesh surface UV atlas Simulation grid
triangle → 펼친 triangle → texel nodes
Triangle을 UV Grid에 올린다
각 triangle corner는 3D 위치와 UV를 함께 가진다.
3D triangle: UV triangle:
P2 uv2
/\ /\
/ \ / \
P0----P1 uv0----uv1
UV atlas의 해상도가 $W\times H$라면 정규화 UV 좌표를 픽셀 좌표로 바꿀 수 있다.
이미지 좌표에서 $v=0$이 위인지 아래인지는 렌더링 API와 텍스처 규약에 따라 다를 수 있다. 실제 구현에서는 이 방향을 한 번 정해 rasterization과 texture lookup 양쪽에서 일관되게 사용해야 한다.
triangle의 UV bounding box에 속하는 texel을 훑고, 각 texel의 중심점이 triangle 안에 들어오는지 검사하면 해당 표면을 덮는 texel을 찾을 수 있다. 삼각형 내부 판정에는 Edge Function이나 barycentric 좌표를 사용할 수 있다.
UV triangle과 texel 중심
┌───────────────────┐
│ · · · · · │ · : texel center
│ · ● · │ ● : triangle 내부인 sample
│ · · │
└───────────────────┘
texel 중심이 UV triangle 안에 들어오면, 그 점을 triangle의 barycentric coordinate $(\lambda_0,\lambda_1,\lambda_2)$로 표현한다.
이 가중치로 3D 위치와 필요한 표면 속성을 보간한다.
정점 법선을 보간해 정규화하거나, 3D triangle의 기하학적 법선을 계산할 수도 있다. 시뮬레이션이 위치와 법선만 필요로 한다면 이 정보를 texel마다 저장할 수 있다.
Texel → surface sample
├─ triangle index
├─ barycentric coordinate
├─ 3D position
├─ normal
└─ chart ID
이 과정은 보통 UV rasterization이라고 부를 수 있다. 결과로 각 유효 texel이 표면의 어느 점을 대표하는지 알게 된다. 아무 triangle도 덮지 않은 texel은 atlas 배경이며 simulation node로 취급하지 않는다.
Simulation 상태를 texel에 둔다
표면을 덮는 texel마다 상태를 하나 저장한다. 상태는 문제에 따라 온도, 농도, 높이, 에너지, 변위처럼 달라질 수 있다.
SurfaceState[TexelIndex]
시뮬레이션의 각 반복은 현재 상태를 읽고, 이웃 상태를 모아 다음 상태를 계산한다.
여기서 $N(i)$는 texel $i$의 표면 이웃 집합이다. 중요한 구현 선택은 상태 배열 그 자체보다 각 texel의 이웃을 어떻게 정하는가다.
UV 격자 이웃만으로는 부족하다
단순한 2D grid라면 texel의 4방향 또는 8방향을 확인하면 된다.
8-neighbor stencil
● ● ●
● x ●
● ● ●
하지만 atlas에는 서로 다른 chart들이 한 텍스처에 배치되어 있다.
UV atlas
┌────────┐ ┌────────┐
│ Chart A│ │ Chart B│
└────────┘ └────────┘
두 영역이 텍스처에서 가깝거나 대각선으로 닿아도, 실제 표면에서 이웃이라고 할 수 없다. 따라서 texel 이웃을 만들 때는 chart 경계나 triangle 연결 정보를 확인해야 한다. 유효 texel 여부만 보고 주변 8개 칸을 무조건 잇는 방식은 chart 사이의 padding을 건너 잘못 연결할 수 있다.
반대 문제도 있다. 3D에서 공유 edge를 가진 두 triangle의 UV가 seam에서 갈라지면, 격자에서는 멀리 떨어져 있어 기본 4/8-neighbor로는 연결되지 않는다.
Chart 안의 연결과 seam 연결을 합친다
이웃 그래프는 두 단계로 구성할 수 있다.
- 같은 UV Chart 안에서는 rasterized triangle과 texel 인접성을 사용해 이웃을 만든다.
- UV Seam 양쪽에서는 공유 3D edge를 따라 마주 보는 texel 후보를 찾아 별도 연결을 추가한다.
첫 단계에서 texel은 대개 주변 8칸을 검사한다. 실제 구현은 유효성, chart ID, triangle 연결 같은 조건을 확인해 서로 다른 표면 영역이 우연히 닿은 경우를 제외해야 한다.
둘째 단계에서는 seam의 양쪽 triangle에 속한 UV edge를 각각 살핀다. edge 위의 진행 비율을 $t\in[0,1]$로 두고, 각 seam 쪽에서 가까운 texel 중심을 후보로 모은다.
Seam 한쪽 UV edge: A────────────B
↑
t = 0.5
반대쪽 UV edge: A────────────B
↑
t = 0.48
edge의 방향은 양쪽 triangle에서 반대일 수 있으므로, 양쪽 모두 같은 원래 endpoint에서 시작하는 방향으로 정렬한 뒤 $t$를 비교해야 한다. 그래야 같은 3D edge 위치에 대응하는 texel 후보를 서로 짝지을 수 있다. 예를 들어 각 후보에 대해 seam edge에서 가장 가까운 점의 매개변수 $t$를 계산하고, 반대편에서 $t$가 가장 가까운 후보를 찾는다.
Chart A texel 101 ───── Chart B texel 850
Chart A texel 102 ───── Chart B texel 851
Chart A texel 103 ───── Chart B texel 852
찾은 쌍은 양방향 이웃으로 추가한다. 이렇게 만든 이웃 목록은 UV상 거리와 상관없이 시뮬레이션에서 하나의 표면 경계처럼 이어진다.
표면 이웃 그래프
결과적으로 각 유효 texel은 이웃 texel들의 목록을 가진다.
UV grid neighbors + seam neighbor links
↓ ↓
Surface neighbor graph
solver는 이 목록을 따라 상태를 읽고 갱신한다. 이웃 수가 항상 4나 8로 고정되지 않는다면, 고정 크기 배열과 함께 neighbor count를 저장하거나, CSR(Compressed Sparse Row) 같은 희소 그래프 표현을 쓸 수 있다.
Texel 101: [100, 102, 850, ...]
Texel 102: [101, 103, 851, ...]
시뮬레이션 결과를 다시 Mesh에 적용할 때는 반대 방향의 대응이 필요하다. 표면점의 UV를 계산하고 해당 texel 또는 주변 texel 상태를 샘플링한다. 또는 rasterization 때 저장한 triangle과 barycentric 좌표를 사용해, texel 상태를 triangle corner나 정점으로 모아 적용할 수 있다. 정점 하나가 여러 UV corner에 나타나는 seam에서는 UV가 다른 corner를 하나의 렌더링 정점으로 무조건 합치지 않도록 주의해야 한다.
실무에서 확인할 조건
- 겹치는 UV: 서로 다른 표면이 같은 texel을 차지하면 texel 하나에 어떤 표면 상태를 저장할지 모호해진다. 이 방식은 보통 겹치지 않는 UV atlas를 전제로 하거나, 겹침을 허용하는 정책을 별도로 둔다.
- Chart padding: 빈 texel은 표면 sample이 아니다. 필터링용으로 주변 값을 확장(dilation)한 texel과 실제 simulation node를 구분한다.
- 해상도와 왜곡: 같은 texel 폭이 표면에서 나타내는 실제 길이는 UV stretch에 따라 달라진다. 물리적 확산 속도를 일정하게 하려면 UV 격자의 간격만으로 계수를 정하지 말고 표면 거리 또는 metric을 고려해야 한다.
- 이웃 중복: 같은 texel 쌍이 여러 triangle 경로 또는 seam 후보를 통해 반복 등록될 수 있으므로 중복 제거가 필요할 수 있다.
- 비정상적인 topology: 한 edge에 triangle이 세 개 이상 붙는 non-manifold mesh는 양쪽을 하나로 특정할 수 없다. 입력 정책을 정하거나 오류로 처리해야 한다.
정리
UV rasterization은 Mesh의 triangle과 UV 좌표를 이용해 각 texel이 표면의 어느 점을 나타내는지 기록한다. 하지만 UV 좌표의 2D 인접성만으로 실제 표면 인접성을 복원할 수는 없다. 같은 chart 안의 grid 연결에 더해, UV에서 끊긴 seam 양쪽의 texel도 원래 공유 edge를 기준으로 연결해야 한다.
요약하면:
Mesh triangle + UV
↓ rasterize
Texel ↔ triangle / barycentric / surface attributes
↓
Chart 내부 grid neighbors + seam links
↓
Mesh 표면을 따라 이어진 simulation graph
앞 글 「Union-Find로 UV Chart 만들기」에서 만든 Chart와 Seam 분류가 바로 이 mapping 단계에서 사용된다.