MaxScriptTip: 頂点を特定平面上に投影(整列)する

5月 05, 2016
今回は平面への投影処理について。 

通常、特定の軸(XYZ)平面上への投影は、単に頂点の値を揃えるだけで出来ます。
例えば、全ての頂点をYZ平面上に投影したければ、X座標を0.0に揃えるだけで整列できます。

しかし、これが単純な軸平面上ではない場合、もしくは任意の移動方向に制限して投影する場合、一気に話が難しくなります。

以下は頂点座標を、任意の平面上に任意の方向から投影する関数です。
PROJ_THRESHOLD = 0.0001  -- 1

fn projToPlane plnNrm plnOfs vert vec =
(
    local vecAng = dot plnNrm vec  -- 2

    if (abs vecAng) > PROJ_THRESHOLD then  -- 3
    (
        local closestDist = dot plnNrm (plnOfs - vert)  -- 4
        local dist = closestDist / vecAng  -- 5
        vec * dist + vert  -- 6
    )
    else
        vert  -- 7
)

引数説明

plnNrm
平面の法線です。予め正規化しておく必要があります。
法線というのは面から垂直に伸びている単位ベクトルの事で、例えばYZ平面であればZ軸の方向を向いているので[1,0,0]になります。
正規化するにはnormalize関数を使用します。
plnOfs
平面のオフセット値です。平面上にある一点の座標を指定します。
この一点は平面上に有りさえすれば、何処を指定していても問題ありません。
vert
投影前の頂点座標です。
vec
投影を行う方向ベクトルです。
このベクトルも正規化されている必要があります。
またこのベクトルが面と完全に平行だった場合、処理はキャンセルされます。(後述)


処理内容説明

1. PROJ_THRESHOLD = 0.0001
面とvecが平行だった時、どの程度までなら処理するかの許容値を設定しています。
1.0を完全に垂直、0.0を完全に平行とし、0.0以外の値を設定します。 イメージしてみると分かりますが、面と移動ベクトルが完全に平行だと、頂点は面にぶつからず処理不能となります。
2. local vecAng = dot plnNrm vec
plnNrmとvecがどの程度同じ向きを向いているかを計算しています。
全く同じ向きなら1.0、正反対を向いていれば-1.0、直角なら0.0を返します。
いわゆる内積演算です。
3. if (abs vecAng) > PROJ_THRESHOLD then
2の計算結果の絶対値がPROJ_THRESHOLDより小さければ、処理をキャンセルしています。
4. local closestDist = dot plnNrm (plnOfs - vert)
頂点と平面との再接近距離を計算しています。
具体的にはplnOfsからvertへ向かうベクトルを計算し、plnNrmと内積を取っています。
こうすることで面と頂点との距離を計算しています。不思議ですね。
5. local dist = closestDist / vecAng
再接近距離をvecAngで割る事で、vec方向に移動した時の衝突距離を計算しています。
そもそもvecAngは移動ベクトルと面法線の一致度である為、この2つが違えば違う程、小さな値(0に近い値)になっていきます。 結果、distはより大きな値になります。
ただし0.0の時(完全に平行な時)は結果が無限大になってしまう為、処理不可です。
6. vec * dist + vert
最終的な移動先座標を計算しています。
単位ベクトルであるvecと移動距離distを掛ける事によって、移動オフセットを計算する事が出来ます。
7. vert
vecが面と完全に平行だった時、引数vertをそのまま返しています。
ここは、必要に応じて例外処理等に変更しても良いと思います。

実際に使ってみます。
thePoly = selection[1]
plnNrm = normalize [0.5, 0, 1]
plnOfs = [0, 0, 10]
vec = z_axis

in coordsys #world
(
    for vi = 1 to (polyop.getNumVerts thePoly) do
    (
        local v = polyop.getVert thePoly vi
        polyop.setVert thePoly vi (projToPlane plnNrm plnOfs v vec)
    )
)
面の向きはXに傾いたZ方向、面のオフセットはZ+に10ずらしています。
また、移動ベクトルはZ軸に指定しています。



どうでしょうか?
傾いた面上に整列しているのが分かるかと思います。
また面は原点を通っておらず、Z方向にオフセットされている事も確認出来ます。



本来であればもっと画像を多用して解説すべき記事ですが、管理人の体力が限界なのでこの辺で。

Related Articles