티스토리 뷰
728x90
반응형
제주 4·3 용천수 Scalar Field 생성 문서
- Scalar Field (스칼라 필드)
- 각 복셀에 숫자 하나 (0~1)
- 용천수까지 거리/영향도

목적
제주 지형에서 각 지점으로부터 가장 가까운 용천수까지의 거리를 계산하여 텍스처로 출력. Unity VFX Graph에서 파티클이 용천수로 끌려가는 효과에 사용.
데이터
- 제주 지형: HeightField (1080 x 1800, 높이 ~50)
- 용천수 포인트: 1025개, GeoJSON에서 로드하여 제주 좌표계로 변환됨
Houdini 노드 체인

1. heightfield_file1 (제주 지형 로드)
2. python (용천수 GeoJSON 로드)
3. transform (좌표 변환)
4. pointwrangle1 (용천수를 지형 높이로 올림)
└─ 코드: pos.y = volumesample(1, "height", pos);
5. HeightField Copy Layer
└─ Source: height
└─ Destination: spring_dist
6. Volume Wrangle (거리 계산)
└─ 입력 1: HeightField (height, mask, spring_dist)
└─ 입력 2: 용천수 포인트
7. HeightField Remap (0-1 정규화)
└─ Input Min: 578.666
└─ Input Max: 1796.660
└─ Output Min: 0
└─ Output Max: 1
8. HeightField Output (EXR 저장)
Volume Wrangle 코드
if (@primnum == 2) {
float total = 0.0;
float radius = 20.0; // 20m
for(int i = 0; i < 1025; i++) {
vector sp = point(1, "P", i);
float dx = @P.x - sp.x;
float dz = @P.z - sp.z;
float dist = sqrt(dx*dx + dz*dz);
if (dist < radius) {
total += 1.0 - (dist / radius);
}
}
f@spring_dist = clamp(total, 0.0, 1.0);
}
코드 설명
- @primnum == 2: spring_dist 레이어만 처리 (primitive 2번)
- volumeindextopos(): 복셀 인덱스를 월드 좌표로 변환
- npoints(1): 두 번째 입력(용천수 포인트) 개수
- point(1, "P", i): 용천수 포인트 위치 가져오기
- XZ 평면 거리만 계산: 높이 차이 무시


주요 문제와 해결
문제 1: s@name 작동 안 함
증상: if (s@name == "spring_dist") 조건이 실행 안 됨
원인: HeightField의 volume primitive는 name 속성이 비어있음
해결: @primnum으로 직접 지정
- primitive 0 = height
- primitive 1 = mask
- primitive 2 = spring_dist
HeightField Output 설정
Output Type: Deep Raster
Type: 32b Floating Point
Filename: $HIP/spring_distance.exr
Layers: spring_dist
내보내기:
- HeightField Output 노드 선택
- "Save to Disk" 버튼 클릭
- $HIP/spring_distance.exr 파일 생성됨
Unity 사용법
1. 텍스처 Import
- spring_distance.exr를 Unity 프로젝트로 드래그
2. VFX Graph에서 사용
Sample Texture2D
├─ Texture: spring_distance
├─ UV: Particle Position (XZ를 0-1로 정규화)
└─ Output: distance (float)
Force
└─ Direction: 용천수 방향으로 계산
└─ Strength: 1.0 - distance (가까울수록 강함)
3. 값 해석
- 0 (검은색): 용천수 바로 근처 → 최대 끌림
- 1 (흰색): 용천수에서 가장 멀리 → 최소 끌림
결과물
- 파일: spring_distance.exr (32-bit floating point, single channel)
- 해상도: 540 x 900 (HeightField 해상도와 동일)
- 값 범위: 0.0 ~ 1.0 (정규화됨)
- 의미: 각 복셀에서 가장 가까운 용천수까지의 2D 거리
참고사항
- 2D 수평 거리만 계산 (Y축 높이 무시)
- 용천수 포인트 개수: 1025개
- 거리 원본 범위: 578 ~ 1796 (미터 단위)
원인
Volume Wrangle에서 npoints(), nprimitives() 같은 geometry 쿼리 함수가 두 번째 입력에 대해 제대로 작동하지 않음.
해결
직접 loop으로 point 개수 확인하거나, 알고 있는 개수 하드코딩:
c
// ✅ 해결책 1: 알고 있는 개수 사용
for(int i = 0; i < 1025; i++) {
vector sp = point(1, "P", i);
// ...
}
// ✅ 해결책 2: loop으로 카운트
int count = 0;
for(int i = 0; i < 10000; i++) {
vector testP = point(1, "P", i);
if(testP.x == 0 && testP.y == 0 && testP.z == 0) break;
count = i + 1;
}
```
---
## 최종 작동 코드
### 노드 구조
```
heightfield_file1 (540×900×1 HeightField)
↓
heightfield_copylayer1
- Source: height
- Destination: spring_dist
- Copy Source Data: ✓
↓
volumewrangle1
- Input 0: heightfield_copylayer1
- Input 1: pointwrangle1 (용천수 1025개)
↓
heightfield_visualize1 (확인용)
↓
heightfield_output1 (spring_distance.exr)
Volume Wrangle 코드
c
if (@primnum == 2) {
float total = 0.0;
float radius = 20.0; // 영향 반경 (m)
// npoints(1) 대신 직접 1025 사용
for(int i = 0; i < 1025; i++) {
vector sp = point(1, "P", i);
// XZ 평면 거리만 계산 (Y 무시)
float dx = @P.x - sp.x;
float dz = @P.z - sp.z;
float dist = sqrt(dx*dx + dz*dz);
if (dist < radius) {
float influence = 1.0 - (dist / radius);
total += influence;
}
}
f@spring_dist = clamp(total, 0.0, 1.0);
}
디버깅 과정에서 발견한 것들
1. HeightField에서 @P는 이미 월드 좌표
c
// ❌ 불필요
vector worldPos = volumeindextopos(0, @primnum, @P);
// ✅ 직접 사용
float dx = @P.x - sp.x;
2. @primnum으로 레이어 구분
- @primnum == 0: height
- @primnum == 1: mask
- @primnum == 2: spring_dist
3. Y축 차이 문제
- 복셀 Y: -1078 (volumeindextopos 사용 시)
- 용천수 Y: 0.03
- 해결: XZ 평면 거리만 계산 (2D)
4. 해상도 확인 중요
- 정상: 540 × 900 × 1
- 비정상: 540 × 1 (망가진 상태)
- i@resx, i@resz로 확인
주요 실수들
❌ 틀린 접근
c
// pcfind는 Y값도 고려해서 실패
int nearSprings[] = pcfind(1, "P", worldPos, 100.0, 10);
// npoints()가 작동 안 함
int npts = npoints(1);
// volumeindextopos()가 이상한 Y 반환
vector worldPos = volumeindextopos(0, @primnum, @P);
✅ 올바른 접근
c
// 직접 loop
for(int i = 0; i < 1025; i++)
// @P 직접 사용
float dx = @P.x - sp.x;
// XZ만 계산
float dist = sqrt(dx*dx + dz*dz);
결과
- Min/Max: 0.0 ~ 1.0
- 빨강: 용천수 20m 근처 (영향 강함)
- 흰색: 용천수 없는 지역 (한라산 중심부)
- 바다: mask로 제외됨
교훈
- Volume Wrangle의 geometry 함수는 제한적 - 특히 두 번째 입력
- 디버깅 = 작은 단계로 - setdetailattrib으로 중간값 확인
- HeightField @P는 월드 좌표 - 변환 불필요
- 2D 거리로 충분 - Y축 무시
- 함수 안 되면 직접 구현 - npoints() 대신 loop
728x90
반응형
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- ai film
- opencv
- 라즈베리파이
- VR
- MCP
- colab
- TouchDesigner
- opticalflow
- 4d guassian splatting
- Express
- AI
- node.js
- Unity
- RNN
- DeepLeaning
- Python
- krea
- houdini
- 후디니
- 유니티
- docker
- CNC
- Java
- three.js
- 4dgs
- Arduino
- MQTT
- sequelize
- VFXgraph
- Midjourney
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
글 보관함
반응형

