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

投稿者: | 2016/05/05

通常、特定の軸に沿った平面上への投影は、単にその軸の座標を均一にするだけで実現出来ます。
例えば、全てのX座標を0.0に揃えれば、それはX平面上に投影された事になります。

しかし、これが単純な軸平面上ではない場合、もしくは任意の移動方向に制限して投影する場合、話が一気に難しくなります。
今回はそういった高度な平面投影の計算方法を説明します。

投影関数

以下は指定した座標を、任意の平面上に任意の方向から投影する関数です。

PROJ_THRESHOLD = 0.0001  -- 1

fn projToPlane plnNrm plnOfs pos direc =
(
    local vecAng = dot plnNrm direc  -- 2
    if (abs vecAng) > PROJ_THRESHOLD then  -- 3
    (
        local closestDist = dot plnNrm (plnOfs - pos)  -- 4
        local dist = closestDist / vecAng  -- 5
        direc * dist + pos  -- 6
    )
    else
        pos  -- 7
)

引数説明

plnNrm
平面の法線です。
法線というのは面から垂直に伸びているベクトルの事で、例えば完全に上を向いている平面であれば、法線はZ軸の方向を向いているので [0.0, 0.0, 1.0] になります。
ベクトルは長さが1.0になるように、normalize関数で正規化する必要があります。
plnOfs
平面のオフセット値です。
平面上にある一点の座標を指定します。
この一点は平面上に有りさえすれば何処を指定していても問題ありません。
pos
投影前の頂点座標です。
direc
投影を行う方向のベクトルです。
このベクトルも正規化されている必要があります。
また、このベクトルが面と完全に平行だった場合、投影は行われません。

処理内容

  1. 面と投影方向が平行だった時、どの程度まで許容するかを設定します。
    イメージしてみると分かりますが、投影方向と面が完全に平行だった場合、頂点は面にぶつからず衝突距離は無限大になってしまいます。
    この値は1.0を完全に垂直とし、0.0以上の小さな値を設定します。

  2. 面と投影方向の平行度を計算しています。
    いわゆる内積です。

  3. 平行度が許容範囲内か判定しています。

  4. 詳細は省きますが、頂点と面の最接近距離を計算しています。

  5. 再接近距離を平行度で割る事で、実際の頂点の移動距離を計算します。
    平行度が0.0に近づくほど、実際の移動距離は大きくなっていきます。

  6. 最終的な移動先座標を計算しています。

  7. 平行度が許容範囲を超えていた場合、元の座標をそのまま返します。

実際の使用

以下は選択ポリゴンの全頂点を平面に投影しています。
投影面はZ座標10.0を通っており、ややX方向に傾いた上向きの面です。
投影方向は今回はZ方向としますが、実際にはどんな向きでも投影出来ます。

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)
    )
)

処理結果は以下のようになります。

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

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

コメントを残す

メールアドレスが公開されることはありません。