후디니 fit·fit01·clamp로 값 범위 바꾸기: 재매핑 계산기
후디니에서 값 범위를 바꾸려다 보면 속성은 새 범위에 들어왔는데, 양 끝에서 갑자기 멈추거나 램프가 기대와 다르게 반응할 때가 있습니다. 같은 입력값을 fit 함수, clamp(), smooth(), chramp()에 넣어도 결과가 다르기 때문입니다. 예를 들어 0~1 밖의 값은 잘라야 할까요, 직선을 계속 이어야 할까요?
이 차이는 후디니 기본 함수들을 같은 숫자로 직접 비교하면 분명해집니다. 먼저 계산 원리를 짚고, Houdini 19.5.435 hython에서 범위 안·밖과 뒤집힌 범위를 측정한 뒤 재매핑 계산기로 확인하겠습니다.
1. fit 함수는 입력 구간의 위치를 새 구간으로 옮깁니다
값 0.25를 0~1 구간에서 10~20 구간으로 옮긴다고 가정합니다. 입력 구간에서 0.25는 25% 지점이고, 새 구간의 25% 지점은 12.5입니다. SideFX VEX fit 문서도 0.3을 0~1에서 10~20으로 옮기면 13이라고 설명합니다.
float value = 0.25;
float omin = 0.0;
float omax = 1.0;
float nmin = 10.0;
float nmax = 20.0;
float t = (value - omin) / (omax - omin);
float result = nmin + t * (nmax - nmin);
f@remapped = result;
여기서 omin·omax는 입력 구간의 시작과 끝이고, nmin·nmax는 출력 구간의 시작과 끝입니다. 0.25를 10~20에 맞추면 10 + 0.25 × (20-10), 즉 12.5가 됩니다. 구간을 옮기는 것과 구간 밖 값을 자르는 것은 별개의 동작입니다.
그래서 fit과 clamp를 같은 함수처럼 보면 헷갈립니다. clamp(value, 0, 1)은 값을 0~1 사이에만 남기고, 0.25를 10~20으로 옮기지는 않습니다. fit(value, 0, 1, 10, 20)은 구간을 옮기면서 입력값이 0~1 밖으로 나가면 경계에 고정합니다.
2. 범위 밖 입력에서 함수가 갈립니다
같은 입력값을 원래 구간 0~1, 새 구간 10~20에 넣어 비교했습니다. smooth()에는 0~1을 넣었고, chramp()에는 램프 위치를 직접 넣었습니다. 램프는 0에서 0, 0.5에서 1, 1에서 0으로 이어지는 선형 키 세 개를 사용했습니다.
| x | fit | efit | fit01 | fit10 | fit11 | clamp | smooth | chramp* |
|---|---|---|---|---|---|---|---|---|
| -0.5 | 10 | 5 | 10 | 20 | 12.5 | 0 | 0 | 0 |
| 0 | 10 | 10 | 10 | 20 | 15 | 0 | 0 | 0 |
| 0.25 | 12.5 | 12.5 | 12.5 | 17.5 | 16.25 | 0.25 | 0.15625 | 0.5 |
| 0.5 | 15 | 15 | 15 | 15 | 17.5 | 0.5 | 0.5 | 1 |
| 0.75 | 17.5 | 17.5 | 17.5 | 12.5 | 18.75 | 0.75 | 0.84375 | 0.5 |
| 1 | 20 | 20 | 20 | 10 | 20 | 1 | 1 | 0 |
| 1.5 | 20 | 25 | 20 | 10 | 20 | 1 | 1 | 0 |
범위 안에서는 fit과 efit이 같지만, x=-0.5와 1.5에서 갈립니다. fit은 출력 경계를 지키고 efit은 직선을 밖으로 이어 갑니다. SideFX 문서도 fit()은 입력 범위를 먼저 clamp하며, efit()은 clamp하지 않는다고 구분합니다.
이 표에서 fit과 fit01은 같은 결과를 냅니다. 둘 다 입력 구간이 0~1이기 때문입니다. fit01()은 이 구간을 자주 쓰는 경우 입력 시작과 끝 두 인자를 생략하는 형태입니다. fit10()은 1에서 0으로 내려가는 구간, fit11()은 -1에서 1로 올라가는 구간을 새 범위로 옮깁니다. 이 세 함수도 이 테스트에서는 범위 밖 입력을 새 출력 경계에 고정했습니다.
clamp()는 재매핑하지 않습니다. x=1.5가 1로 바뀌는 것처럼 입력값을 지정한 최소·최대 안에 붙잡습니다. smooth()는 범위 안에서 0에서 1로 천천히 시작하고 끝나는 곡선을 만들며, 바깥에서는 0 또는 1입니다. 대상 범위까지 옮기려면 lerp(nmin, nmax, smooth(omin, omax, value))처럼 한 단계를 더 둡니다.
chramp()는 램프 파라미터를 특정 위치에서 읽습니다. 위치값은 0~1로 잘립니다. 따라서 fit으로 위치를 0~1로 정규화하고 chramp에 넣어도, 램프 밖으로 외삽되지는 않습니다. 곡선은 램프 키와 보간 방식이 정합니다.

3. 뒤집힌 구간은 되지만, 0 길이 구간은 경계입니다
입력 구간을 1→0으로 뒤집으면 fit은 값을 반대로 매핑합니다. 예를 들어 x=0.25는 0.75, x=0.75는 0.25가 됩니다. efit도 같은 방향으로 계산하고, 범위를 벗어나면 그 방향으로 계속 외삽합니다. 출력 구간을 20→10으로 뒤집어도 같은 원리로 값이 감소합니다.
| Houdini 19.5.435 실측 | x=-0.5 | x=0.5 | x=1 | x=1.5 |
|---|---|---|---|---|
| fit(x, 1, 0, 0, 1) | 1 | 0.5 | 0 | 0 |
| efit(x, 1, 0, 0, 1) | 1.5 | 0.5 | 0 | -0.5 |
| clamp(x, 1, 0) | 1 | 1 | 1 | 0 |
| smooth(1, 0, x) | 0 | 0 | 0 | 1 |
반면 clamp(x, 1, 0)처럼 clamp의 최소·최대 순서를 뒤집는 것은 쓰지 마세요. 이 버전의 실측값은 x가 1 이하일 때 1, 1을 넘을 때 0으로 바뀌는 단계형 결과였습니다. smooth(1, 0, x)도 반전된 곡선이 아니라 1 부근에서 단계처럼 바뀌었습니다. 완만한 곡선을 반전하려면 1-smooth(0, 1, x)처럼 정상 순서의 결과를 뒤집고, clamp는 항상 작은 값을 먼저 넣으세요.
입력 시작과 끝이 같으면 나눌 구간이 없습니다. H19.5.435에서 omin=omax=0.5를 넣었을 때 fit은 x<0.5에서 0, x=0.5에서 0.5, x>0.5에서 1을 반환했습니다. efit은 모든 입력에서 0.5였습니다. 직접 (x-omin)/(omax-omin)을 계산한 테스트는 모든 점에서 0을 반환했습니다. 수학적 정규화로 쓸 수 없는 경우이며, SideFX 함수 문서는 이 퇴화 구간의 동작을 보장한다고 설명하지 않습니다.
Attribute Remap 노드도 Input Min과 Input Max를 모두 0.5로 둔 뒤 기본 Edge 모드로 시험하자, 입력값이 0.5보다 작을 때 Output Min, 0.5 이상일 때 Output Max로 갈리는 단계가 생겼습니다. 입력 구간 길이가 0이 되지 않도록 먼저 확인하세요. 아래 코드를 Wrangle에서 실행하려면 파라미터 생성 버튼으로 source_position(vector)와 near, far, large_scale, small_scale(float) 채널을 만든 뒤 씬 값을 설정하세요. 채널을 만들지 않아도 Houdini 19.5.435에서는 컴파일되지만, chv()와 chf()가 0으로 평가되어 @pscale도 0이었습니다. 같은 값일 때 쓸 결과를 직접 정하는 편이 안전합니다.
vector source_position = chv("source_position");
float near_value = chf("near");
float far_value = chf("far");
float large_scale = chf("large_scale");
float small_scale = chf("small_scale");
float dist = length(@P - source_position);
float t = 0.0;
if (abs(far_value - near_value) > 1e-6)
t = fit(dist, near_value, far_value, 0.0, 1.0);
@pscale = lerp(large_scale, small_scale, t);
여기서 near와 far가 같으면 t를 0으로 둡니다. 따라서 모든 점은 large_scale을 받습니다. 원하는 기본 크기가 다르면 t의 초기값을 바꾸세요. 자동으로 나온 값에 기대기보다 씬에서 어떤 결과를 원하는지 먼저 정하는 것이 중요합니다.
4. Attribute Remap은 램프와 바깥값 정책을 노드에서 고릅니다
Attribute Remap 공식 문서는 이 노드를 float 속성값을 새 범위로 이동·스케일하고, 램프로 매핑을 추가 조절하는 SOP라고 설명합니다. 입력 속성의 Class와 Original Name, Input Min/Max, Output Min/Max를 지정하며, 필요하면 New Name에 다른 속성명을 적어 원본을 보존할 수 있습니다. Compute Range는 현재 속성의 최소·최대값을 입력 범위에 채웁니다.
VEX fit()과의 핵심 차이는 Out of Range Values의 선택지입니다. Clamp to Edge Value는 램프의 첫·끝값에 고정하고, Linearly Extrapolate는 범위 밖에서 램프를 무시하고 선형 외삽하며, Roll Cyclically는 램프를 앞뒤로 반복합니다. 기본 램프가 직선일 때 Clamp는 fit에 가까운 결과를 냅니다. 중간 키로 곡선을 만들거나 다른 바깥값 처리를 고르면 fit()과 같지 않습니다.
| 범위 밖 모드 | 0~1 밖의 값 | 언제 쓰나 |
|---|---|---|
| Clamp to Edge Value | 첫·끝 램프값에 고정 | 색이나 크기가 끝값 이상으로 더 커지면 안 될 때 |
| Linearly Extrapolate | 램프를 무시하고 직선 연장 | 입력 범위 밖도 변화량을 이어야 할 때 |
| Roll Cyclically | 램프를 앞뒤로 반복 | 주기적으로 되풀이할 때 |
두 버전의 노드 이름도 확인했습니다. SideFX의 Houdini 19.5 문서와 현재 Houdini 22.0 문서는 Parameter 이름과 세 가지 범위 밖 모드가 같습니다. 공식 문서는 기본값을 나열하지 않으므로, 아래 기본값은 이 PC의 Houdini 19.5.435 Attribute Remap 노드에서 직접 읽은 값입니다.
기본 Class는 Point이고, Input Min/Max와 Output Min/Max는 각각 0과 1입니다. Out of Range Values는 Clamp to Edge Value, Use Ramp는 켜져 있었고 램프 키는 (0,0)에서 (1,1)까지 직선이었습니다. 메뉴 이름이나 기본 동작이 다른 환경에서는 노드의 실제 파라미터를 확인하세요.
5. 높이로 색을 바꾸고 거리로 pscale을 줄입니다
높이에 따라 포인트 색을 바꾸려면 먼저 씬의 높이 구간을 0~1로 정규화한 뒤 색 램프를 읽습니다. min_y와 max_y는 씬에서 실제로 색을 나누고 싶은 높이로 설정하세요. fit이 범위 밖 높이를 0이나 1에 고정하므로 램프 양 끝 색이 유지됩니다.
float t = fit(@P.y, chf("min_y"), chf("max_y"), 0.0, 1.0);
@Cd = chramp("color_ramp", t);
Wrangle의 파라미터 생성 버튼을 눌러 min_y, max_y, color_ramp를 만들고, color_ramp를 색상 램프로 바꾸세요. @Cd가 보이지 않으면 계산식만 고치기 전에 속성이 저장된 Class부터 확인하세요. 포인트·버텍스·프리미티브 속성 차이 글에서 클래스가 뷰포트 색과 pscale에 미치는 차이를 다뤘습니다.
거리로 복사본 크기를 줄일 때는 가까운 지점에서 큰 pscale, 먼 지점에서 작은 pscale이 나오도록 출력 구간을 뒤집습니다. 안전한 전체 코드는 위의 0 길이 구간 예시입니다. near=0, far=10이면 거리가 0일 때 large_scale, 10일 때 small_scale을 받습니다. 입력 범위가 같지 않은지 확인하고 값을 조정하세요.
near=0, far=10, large_scale=0.3, small_scale=0.05라면 원점에서는 0.3, 10유닛 거리에서는 0.05가 됩니다. 10유닛 바깥에서는 0.05보다 더 줄지 않습니다. 더 멀리 있는 점도 계속 작아져야 한다면 fit 대신 efit을 쓰거나, Attribute Remap에서 Linearly Extrapolate를 선택하세요. 단, pscale이 0 이하가 되지 않게 범위를 정해야 합니다.
재매핑 계산기
입력값과 구간을 바꾸면 각 함수의 반환값과 곡선을 확인할 수 있습니다. 그래프 1은 새 출력 구간을 받는 fit 계열이고, 그래프 2는 clamp·smooth·예시 chramp처럼 각각 다른 값을 반환하는 함수입니다. smooth와 chramp는 이 계산기에서 원래 반환하는 값(0~1)을 보여줍니다.
입력값 재매핑
입력 범위와 출력 범위를 바꿔 보세요. 뒤집힌 입력 구간도 시험할 수 있습니다.
| 함수 | 결과 | 동작 |
|---|
그래프 1 · fit 계열 결과
fit·fit01·fit10·fit11은 입력 범위 밖을 출력 경계에 고정하고, efit은 직선을 이어 갑니다.
그래프 2 · clamp, smooth, chramp
clamp는 입력값을 자르고, smooth는 0~1 곡선, chramp는 0→1→0 예시 램프를 읽습니다.
입력 최소와 최대가 같으면 재매핑할 길이가 없습니다. 실제 작업에서는 같은 값일 때 넣을 결과를 코드에서 따로 정하세요.
계산기에서 입력 구간을 0~1, 출력 구간을 10~20으로 두면 x=0.25일 때 fit과 efit은 12.5, fit10은 17.5, fit11은 16.25가 됩니다. x=1.5에서는 fit이 20, efit이 25입니다. 같은 결과가 표와 그래프에 함께 나오지 않으면 입력한 구간과 램프 위치를 확인하세요.
버전과 문서에서 확인할 점
표와 0 나눗셈 테스트는 이 PC에 설치된 Houdini 19.5.435 hython에서 실행했습니다. SideFX 공식 VEX 문서에서 확인한 함수명과 정의는 19.5와 현재 Houdini 22.0 문서가 같았습니다. Attribute Remap의 Class, Input/Output Min·Max, Out of Range Values와 세 모드 이름도 두 문서에서 같았습니다. 다만 공식 문서는 Attribute Remap의 기본값과 0 길이 입력 구간 결과를 명시하지 않으므로, 기본값은 H19.5.435에서 확인한 값으로만 적었습니다.
한 줄 정리
값을 자르며 새 구간으로 옮기려면 fit, 바깥까지 직선을 이어야 하면 efit, 범위만 자르려면 clamp, 부드러운 0~1 곡선은 smooth, 원하는 모양으로 조절하려면 chramp를 쓰세요. 결국 값보다 먼저 정할 것은, 어느 구간을 어떻게 바꿀지입니다.
vfx 잡학지식

댓글 0
첫 댓글을 남겨보세요.