この記事では、各プリミティブ中心と頂点位置の差分をベクトル化し、
放射状の法線を作成する方法を解説します。
VEXを使用しますが、短いのでついでに丁寧に解説していきます。
■ 対象読者
- エフェクトデザイナー
- Houdini の VEX に興味がある方
■ 環境
- Houdini 20.5
目次
■ 前提知識:プリミティブとポイントの関係
Houdini のジオメトリは階層構造を持っています。
今回のテクニックを理解するには、この関係を把握することが重要です。
ジオメトリの構成要素
Houdiniのジオメトリは Detail → Primitive → Vertex → Point という階層構造になっています。
| 要素 | 説明 | 例 |
|---|---|---|
| Point | 空間上の位置(座標) | @P = {0, 1, 0} |
| Vertex | ポイントとプリミティブを繋ぐもの | 面の「角」 |
| Primitive | 面(ポリゴン)やカーブ | 三角形、四角形など |
| Detail | ジオメトリ全体 | メッシュ 1 つ分 |
重要:ポイントの共有問題
通常のメッシュでは、複数の面が同じポイントを共有しています。
1つのポイントが複数の面に属しているため、「このポイントはどの面のもの?」が曖昧になります。
これが後述する Facet ノード(Unique Points) が必要な理由です。
■ 全体のノード構成
今回使用するノード構成は以下の通りです:
| ノード | 役割 |
|---|---|
| sphere1 | 球体を生成 |
| facet1 | ポイントの共有を解除 ★重要 |
| explodedview1 | 確認用:面を分離 |
| attribwrangle2 | 放射状法線を計算 ★メイン処理 |
| uvtexture1 | UV付与 |
| transform1 | スケール調整 |
| rop_fbx1 | FBX出力 |
ノード構成図

完成メッシュ
Exploded View で面を分離した状態です。各面が独立していることがわかります。

1. sphere1(Sphere)
球体メッシュを生成する基本ノードです。
今回はこの球体に放射状法線を設定していきます。
設定画面

設定のポイント(同じように設定してみてください)
| パラメータ | 設定値 | 説明 |
|---|---|---|
| Primitive Type | Polygon | ポリゴンメッシュとして生成 |
| Radius | 0.5 | 球体の半径 |
| Frequency | 2 | メッシュの細かさ(値を上げると分割が増える) |
| Orientation | Y Axis | 球体の向き |
球体を選ぶ理由は、放射状法線の効果がわかりやすいからです。
他のジオメトリにも同じことが適用できます。
公式ドキュメント
リンク
https://www.sidefx.com/docs/houdini/nodes/sop/sphere.html
2. facet1(Facet)★重要
このノードは今回のテクニックで 最も重要な前処理 を行います。
設定画面

設定方法
※Unique Points にチェックを入れてください※
Unique Points OFF の場合(デフォルト)
ポイントが共有されているため、pointprims(0, @ptnum) の結果が [0, 1, 2](3つの面に属している)になり、
どの面の法線を計算すべきかわかりません。
Unique Points ON の場合(今回使用)
各面が独自のポイントを持つため、pointprims(0, @ptnum) の結果が [0](1つの面にのみ属している)なため、明確にどの面か特定できます。
Unique Points の処理内容
Unique Points を ON にすると、こうなります。
- 共有ポイントを複製 – 各面が独自のポイントを持つようになる
- ポイント数が増加 – 元の共有ポイントが面の数だけ複製される
- 見た目は変わらない – 位置は同じなので、見た目は変化しない
処理前:ポイント数 = 100(共有あり)
処理後:ポイント数 = 300(各面が独自のポイントを持つ)
公式ドキュメント
リンク
https://www.sidefx.com/docs/houdini/nodes/sop/facet.html
3. explodedview1(Exploded View)
ポリゴンを「面ごとに分離」して、わずかに隙間を作るノードです。
今回の使用目的
確認用として使用しています。
- Unique Points が正しく機能しているか確認
- 各面が独立していることを視覚的に確認
- 法線の方向を確認しやすくする
設定のポイント
- Scale: 1.0 より少し大きい値(例:1.02)にすると面が分離
最終的な出力には不要なので、確認後は削除しても OK です~。
公式ドキュメント
リンク
https://www.sidefx.com/docs/houdini/nodes/sop/explodedview.html
4. attribwrangle2(Attribute Wrangle)★メイン処理
ここが今回の 核心部分 です。
たった 3 行の VEX ですがものすごく掘って解説していきます。
■ VEX コード全体
int prim = pointprims(0, @ptnum)[0];
vector center = prim(0, "P", prim);
@N = normalize(@P - center);
重要: Run Over は必ず Points に設定してください。
各ポイントに対して法線を計算するためです。
■ コード解説:1行目
int prim = pointprims(0, @ptnum)[0];
この行の目的
現在処理中のポイントが、どのプリミティブ(面)に属しているか を取得します。
分解して理解する
| 要素 | 意味 |
|---|---|
pointprims() | ポイントが属する prim 番号の配列を返す関数 |
0 | 第1引数:入力ジオメトリの番号(0 = 最初の入力) |
@ptnum | 第2引数:現在処理中のポイント番号 |
[0] | 配列の最初の要素を取得 |
なぜ [0] が必要なのか?
pointprims() は配列を返してくれます。
// Unique Points OFF の場合(共有あり)
pointprims(0, @ptnum) → [0, 1, 2] // 3つの面に属している
↑
どれを使う?
// Unique Points ON の場合(共有なし)
pointprims(0, @ptnum) → [0] // 1つの面にのみ属している
↑
これを使う
事前に Facet(Unique Points ON) で処理しているため、
配列には必ず 1 つしか入りません。なので [0] で確実に取得できます。
公式ドキュメント引用
リンク
https://www.sidefx.com/docs/houdini/vex/functions/pointprims.html
■ コード解説:2行目
vector center = prim(0, "P", prim);
この行の目的
プリミティブ(面)の中心位置 を取得します。
それぞれの要素
| 要素 | 意味 |
|---|---|
prim() | プリミティブ属性を取得する関数 |
0 | 第1引数:入力ジオメトリの番号 |
"P" | 第2引数:取得する属性名(位置) |
prim | 第3引数:プリミティブ番号(1行目で取得した値) |
“P” 属性について
プリミティブの "P" 属性は特殊で、そのプリミティブを構成する頂点の中心位置 を返します。

公式ドキュメント
リンク
https://www.sidefx.com/docs/houdini/model/attributes.html
■ コード解説:3行目
@N = normalize(@P - center);
この行の目的
中心から現在のポイントに向かう方向ベクトルを計算し、それを法線として設定します。
分解して理解する
ステップ 1:方向ベクトルの計算
@P - center
面の中心(center)から現在のポイント位置(@P)へ向かうベクトルを計算します。
ベクトル = 終点 – 始点 = @P – center
ステップ 2:正規化(normalize)
normalize(@P - center)
normalize() は、ベクトルの方向はそのままに、長さを 1 にする関数です。
おそらくこの記事を読んでいるエフェクトデザイナーの方は「ノーマライズってなんか…こう数値をいい感じに0~1にしてくれるアレだよなあ」と覚えがあるはずです。
正規化前は長さがバラバラですが、正規化後は長さが全て 1 に揃います。
なぜ正規化が必要?
法線はシェーディング計算で使われるんです。
計算を正しく行うために、法線の長さは 必ず 1 なんですね。
ステップ 3:法線属性への代入
@N = ...
@N は Houdini の組み込み法線属性です。
この属性に値を代入することで、法線を設定できます。
公式ドキュメント
リンク
https://www.sidefx.com/docs/houdini/vex/functions/normalize.html
■ 処理の流れまとめ
各ポイントに対して、以下の処理が実行されます:
ポイント @ptnum = 42 の場合
1. 所属する面を取得
int prim = pointprims(0, 42)[0];
→ prim = 7(7番目の面に属している)
2. その面の中心を取得
vector center = prim(0, "P", 7);
→ center = {0.5, 0.3, 0.2}
3. 中心→ポイント方向の法線を計算
@P = {0.8, 0.6, 0.4}(このポイントの位置)
@N = normalize({0.8, 0.6, 0.4} - {0.5, 0.3, 0.2})
@N = normalize({0.3, 0.3, 0.2})
@N = {0.62, 0.62, 0.41}(正規化された法線)
この処理が 全てのポイント に対して実行され、放射状の法線 が生成されます。
5. uvtexture1(UV Texture)
メッシュに UV 座標を付与するノードです。
設定のポイント
今回は Face モード を使用します。
Face モードにすると各面に対して個別に UV が割り当てられます。
公式ドキュメント
リンク
https://www.sidefx.com/docs/houdini/nodes/sop/texture.html
6. transform1(Transform)
メッシュ全体の位置・回転・スケールを調整します。
たぶんこの記事をよんでいる人はご存じだと思われますので詳細は省きます。
設定のポイント
Unreal Engine 向けの場合、Uniform Scale: 100にしておくといい感じです。
(いつもそれを忘れて10か100か迷う)
公式ドキュメント
リンク
https://www.sidefx.com/docs/houdini/nodes/sop/xform.html
7. rop_fbx1(FBX Export)
おなじみの。
設定画面

出力パスの設定
Output File に以下のように設定します:
$HIP/$HIPNAME.fbx
$HIP/$HIPNAME.fbx とは?
これは Houdini の自動パス(環境変数)で、
FBX の書き出し先とファイル名を、現在開いている .hip ファイルに合わせて自動生成する仕組みです。
hogehoge_123.hip を作業している状態で $HIP/$HIPNAME.fbx を指定して書き出すと、
hipがある場所と同じ個所に自動で hogehoge_123.fbx が生成されます。
多くの人は、プロジェクトの命名規則で .hip ファイルを保存していると思います。
そのため書き出すメッシュも、その名前と揃っていてほしいですよね?
シンプルですが、地味に便利な設定です。
Houdini 変数の解説
| 変数 | 意味 | 例 |
|---|---|---|
$HIP | 現在の .hip ファイルがあるフォルダ | /home/user/project/ |
$HIPNAME | .hip ファイル名(拡張子なし) | Hoge_Normal |
結果:/home/user/project/ に出力Hoge_Normal.fbx
公式ドキュメント
リンク
https://www.sidefx.com/ja/docs/houdini/render/expressions.html
■ 応用:このテクニックの発展
放射状法線の基本を理解したら、以下のような応用が可能です。
1. velocityに入れてみる
// 法線方向に速度を設定
@v = @N * ch("speed");
2. 法線の調整
// 上方向にバイアスをかける↑↑↑
@N = normalize(@N + {0, 0.5, 0});
3. 法線に基づくカラーリング
// 法線を色として可視化
@Cd = @N * 0.5 + 0.5;
■ トラブルシューティング
法線が正しく表示されない
- Facet の Unique Points が ON になっているか確認
- Wrangle の Run Over が Points になっているか確認
- Display Options で法線表示を ON にしているか確認
法線の向きが逆になる
// 法線を反転する場合
@N = -normalize(@P - center);
■ まとめ
今回は Houdini を使って、プリミティブ中心から外へ向かう放射状法線を生成する方法を解説しました。
学んだこと
| トピック | 内容 |
|---|---|
| 法線の基礎 | 面の向きを表すベクトル、長さは1が基本 |
| プリミティブとポイント | Houdiniのジオメトリ構造の理解 |
| Facet(Unique Points) | ポイント共有を解除して、1対1の関係を作る |
| VEX の基本 | pointprims(), prim(), normalize() の使い方 |
コードおさらい
// 1. このポイントが属する面を取得
int prim = pointprims(0, @ptnum)[0];
// 2. その面の中心位置を取得
vector center = prim(0, "P", prim);
// 3. 中心→ポイント方向の法線を設定
@N = normalize(@P - center);
わずか 3行 の VEX ですが、以下の重要な概念が詰まっています:
- prim と point の関係性
- 重心(centroid)の取得方法
- ベクトルの引き算と方向計算
- normalize の重要性
VEX 入門としても非常に良い題材ですので、ぜひ実際に手を動かして試してみてください!
■ 参考リンク
記事修正履歴
2025/12/16 読みやすさ向上のため改行や一部文章の言い換えを追加

