후디니 VEX rand seed 짓는 법: 같은 값·매 프레임 바뀜 해결
VEX rand 값이 모든 점에서 거의 같거나, 프레임을 옮길 때마다 달라지나요? 두 증상은 난수 자체가 새로 뽑히는 문제가 아니라 seed에 무엇을 넣었는지에서 갈립니다. 특히 rand(@ptnum + 17)은 독립된 수열을 만드는지, 아니면 이미 있는 수열을 밀어 쓰는지 확인할 필요가 있습니다.
Houdini 19.5.435에서 점 100개를 만들어 입력 타입, 정수와 소수 입력, Frame 1과 2를 비교했습니다. 함수 이름이 같은 HScript 파라미터 표현식과 VEX rand()의 차이도 함께 짚겠습니다.
1. rand는 같은 seed를 다시 계산합니다
렌더를 다시 하거나 프레임을 되돌렸다고 해서 rand()가 자동으로 새 값을 뽑지는 않습니다. SideFX 문서에 따르면 같은 seed에는 같은 값이 돌아옵니다. 따라서 모든 점에 rand(7)을 계산하면 7이라는 같은 입력을 쓰므로 점마다 결과도 같습니다.
f@value = rand(7);
점마다 다른 값을 만들려면 seed에 현재 점을 구분할 값을 넣어야 합니다. 가장 흔한 시작은 rand(@ptnum)입니다. 이때 @ptnum은 현재 점 번호이며 첫 점은 0부터 시작합니다. 이 예제는 Attribute Wrangle의 Run Over를 Points로 둡니다. 실행 단위가 바뀌면 현재 element와 번호도 달라지므로 Wrangle Run Over 차이를 먼저 확인하세요.
f@value = rand(@ptnum);
rand()는 0 이상 1 미만의 결정적 값을 반환합니다. 같은 점과 같은 seed를 다시 계산하면 프레임이 달라도 값은 그대로입니다. 시간 변수가 식에 들어가야만 시간에 따라 달라집니다.
2. 정수 seed를 더하면 수열이 옆으로 이동합니다

점 100개를 만든 Grid SOP에 Attribute Wrangle을 연결하고 rand(@ptnum), rand(@ptnum + 17), rand(@ptnum + @Frame)을 계산했습니다. 표의 범위와 평균은 Houdini 19.5.435 Commercial에서 Frame 1과 2를 직접 측정한 값입니다. 이웃 상관은 기하학적 최근접점이 아니라 연속된 @ptnum 99쌍의 Pearson 상관계수입니다.
| VEX 식 | 평균 | 표본 최솟값–최댓값 | 이웃 상관 | Frame 1→2 |
|---|---|---|---|---|
rand(@ptnum) | 0.520119 | 0.000997–0.995248 | +0.055869 | 100점 모두 유지 |
rand(@ptnum + 17) | 0.541630 | 0.000997–0.995248 | -0.017216 | 100점 모두 유지 |
rand(@ptnum + @Frame) | F1 0.518794 F2 0.521771 | 두 프레임 모두 0.000997–0.995248 | F1 +0.045294 F2 +0.067455 | 100점 모두 변경 |
rand(set(@ptnum, 7)) | 0.486782 | 0.004999–0.970448 | -0.012970 | 100점 모두 유지 |
rand(@ptnum + 0.1) | 0.512232 | 0.002877–0.993385 | +0.022412 | 100점 모두 유지 |
random(@ptnum + 0.1) | 0.502319 | 0.004249–0.994313 | -0.052362 | 100점 모두 유지 |
random_sobol(0, @ptnum) | 0.497812 | 0–0.992188 | -0.635360 | 100점 모두 유지 |
표에서 평균만 보면 rand(@ptnum + @Frame)의 두 프레임은 거의 비슷합니다. 하지만 같은 점 번호끼리 비교하면 100개 모두 값이 바뀌었습니다. 분포의 평균이 비슷하다는 사실은 각 점의 값이 유지된다는 뜻이 아닙니다.
그리고 rand(@ptnum + 17)의 0번 점 값은 rand(@ptnum)의 17번 점 값과 정확히 같습니다. 정수 17을 더한 식은 새 차원의 독립 seed가 아니라, 같은 1차원 입력 수열에서 17칸 뒤를 읽습니다. seed를 바꿨는데 비슷하게 느껴진다면 이 이동 관계부터 확인하세요.
3. 소수 seed는 rand에서 버려지지 않습니다
VEX rand()는 float seed를 받을 수 있습니다. 같은 100점에서 rand(@ptnum + 0.1)과 rand(@ptnum + 0.2)를 비교하자 100점 모두 값이 달랐습니다. VEX rand 입력의 소수 부분이 자동으로 정수로 잘리는 것은 아닙니다.
벡터를 seed로 넣을 수도 있습니다. 예를 들어 set(@ptnum, 7)은 점 번호와 별도 seed를 담은 vector2 입력입니다. 이는 스칼라 식에 7을 더하는 것과 같지 않습니다. 이번 표본에서 rand(set(@ptnum, 7))의 평균은 0.486782였고, 정수 7을 더한 식의 평균은 0.541630이었습니다.
float seed = chf("seed");
f@value = rand(set(@ptnum, seed));
Wrangle의 파라미터 생성 버튼을 눌러 seed float 파라미터를 만들면 값을 바꿔 수열을 비교할 수 있습니다. 이 식은 점 번호와 seed를 서로 다른 입력 성분으로 전달합니다. 다만 vector seed라고 항상 이웃 상관이 낮아지는 것은 아닙니다. 이 표본에서 rand(set(@ptnum, 0.1))의 연속점 상관은 +0.338042였으므로 실제 작업의 점 배치와 분포를 확인하세요.
반환 타입도 확인해야 합니다. rand(@ptnum)을 float로 받으면 숫자 하나지만, v@variation = rand(@ptnum)처럼 vector로 받으면 X·Y·Z 성분마다 서로 다른 값이 나옵니다. 모든 축에 같은 배율이 필요하다면 scalar 값을 먼저 만든 뒤 사용하세요. 새 속성에는 결과에 맞는 f@ 또는 v@ 접두사를 쓰세요.
4. random과 random_sobol은 목적이 다릅니다
함수 이름이 비슷해도 입력을 바꾸는 방식이 다릅니다. VEX random()은 정수 격자 위치를 기반으로 합니다. 이번 100점 테스트에서는 random(@ptnum + 0.1)과 random(@ptnum + 0.2)가 100점 모두 같았습니다. 소수 차이를 seed로 쓰려면 rand()와 구분해야 합니다.
random_sobol()은 점별 수열이 한 구간에 뭉치는 현상을 줄이고 비교적 고르게 분포한 값을 얻고 싶을 때 쓰는 함수입니다. random_sobol(0, @ptnum)처럼 첫 인자는 sequence seed, 두 번째 offset은 점마다 증가하는 정수로 둡니다. 이번 표본의 0~1 열 구간별 개수는 10, 10, 10, 11, 9, 10, 10, 10, 11, 9였습니다.
반면 기본 rand(@ptnum)의 같은 열 구간 개수는 13, 9, 6, 12, 5, 9, 10, 10, 13, 13이었습니다. 이는 100개라는 작은 표본에서의 결과입니다. 일반적인 난수는 값이 고르게 흩어져 보이지 않고 우연히 몰릴 수 있으며, random_sobol()도 모든 seed와 표본 크기에서 완벽히 같은 개수를 보장하지 않습니다.
5. 파라미터 칸의 rand()는 HScript 표현식입니다
노드 파라미터 칸에서 입력하는 rand($F)와 Attribute Wrangle 코드의 rand(@ptnum)는 같은 언어의 함수 호출이 아닙니다. 파라미터 칸은 HScript 표현식을 평가하고, Wrangle 칸은 VEX를 실행합니다. SideFX 문서는 HScript rand(seed)와 VEX random(seed)를 대응 예로 들지만, 이 PC에서 같은 1.1을 넣은 값도 HScript는 0.721462, VEX random()은 0.356251로 숫자 자체가 같지는 않았습니다.
따라서 파라미터 값이 프레임마다 달라지길 바란다면 HScript 표현식의 seed에 $F를 넣고, Wrangle 안에서 점별 값을 만들려면 VEX 전역 변수 @ptnum을 사용하세요. 어떤 파라미터 칸은 현재 점의 로컬 변수를 제공하지만 모든 파라미터가 그런 것은 아닙니다. 해당 노드가 사용할 수 있는 변수를 제공하는지 확인해야 합니다.
6. 고정할지 애니메이션할지 먼저 정합니다
원인을 찾았으니 처음 질문에 답할 수 있습니다. 점마다 값은 다르되 시간에 고정하려면 seed에 점 식별자와 고정 seed만 넣고, 프레임마다 바꾸려면 시간 값을 명시적으로 추가하세요.
| 원하는 결과 | Wrangle 식 | 확인 방법 |
|---|---|---|
| 점마다 다른 값, 프레임 고정 | rand(set(@ptnum, seed)) | 같은 점에서 Frame 1과 2 값을 비교 |
| 점마다 다르고 매 프레임 변경 | rand(set(@ptnum, seed + @Frame)) | 같은 점의 두 프레임 값이 달라지는지 확인 |
| 전체 점에 같은 값 | rand(seed) | seed 입력이 모든 점에서 같은지 확인 |
| 파티클 삭제 후에도 ID 유지 | rand(set(i@id, seed)) | 시뮬레이션에서 고유 i@id가 보존되는지 확인 |
@ptnum은 현재 점의 번호라서 점이 삭제되거나 순서가 바뀌면 다른 값을 받을 수 있습니다. 파티클처럼 요소가 사라지거나 추가되는 작업은 시뮬레이션 동안 유지되는 i@id를 seed로 쓰세요. 단, 해당 geometry에 고유 ID가 실제로 있고 계속 보존되는지 먼저 확인해야 합니다.
랜덤 값을 0~1 밖의 pscale이나 원하는 범위로 옮길 때는 seed 식과 범위 변환을 분리하세요. 예를 들어 fit01()로 최종 범위를 조정할 수 있습니다. 입력이 0~1에서 어떻게 이동하는지는 후디니 fit·fit01·clamp 재매핑 계산기에서 확인할 수 있습니다.
VEX rand seed 100점 체험 도구
아래 식을 바꾸면 Houdini 19.5.435 hython에서 계산한 100점 값이 Frame 1과 2에서 어떻게 달라지는지 확인할 수 있습니다. 그래프의 각 점은 @ptnum 하나이며, 히스토그램은 10개 구간의 개수입니다. 이 도구는 미리 측정한 VEX 결과를 보여 주며, 브라우저 JavaScript로 VEX rand를 흉내 내지 않습니다.
VEX rand seed 100점 비교
식을 선택하면 Houdini 실측값을 점 번호별 그래프와 10구간 히스토그램으로 비교합니다.
가로축은 @ptnum 0~99, 세로축은 값입니다. 같은 점의 두 프레임 값이 겹치면 점 하나처럼 보입니다.
각 막대는 100점 중 해당 구간에 들어간 점 개수입니다.
기준 환경: Houdini 19.5.435 Commercial, Grid SOP 10×10점, Frame 1과 2. 브라우저에서 VEX 난수를 다시 계산하지 않고 미리 측정한 수치를 표시합니다. 표본의 최솟값·최댓값은 새 씬에서 보장되는 경계가 아닙니다.
점별 값이 다음 프레임에서도 그대로인지 먼저 확인하고, 그 다음에 전체 평균이나 구간 분포를 보세요. 평균이 0.5에 가깝더라도 같은 점의 값이 유지된다는 뜻은 아닙니다.
한 줄 정리
고정할 값이면 seed에서 @Frame을 빼고, 프레임마다 바꿀 값이면 @Frame을 넣으세요.
참고 자료
vfx 잡학지식

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