生成されたジオメトリの表面法線を計算する方法


12

呼び出し元コードからの入力に基づいて3D形状を生成するクラスがあります。入力は、長さ、深さ、弧などです。私のコードはジオメトリを完全に生成しますが、表面の法線を計算するときに問題が発生します。点灯すると、計算中の不正確な表面法線により、私の形状に非常に奇妙な色/テクスチャがあります。私のすべての研究から、数学は正しいと信じています。私の技術や方法に何か問題があるようです。

高レベルでは、生成された形状の表面法線をプログラムでどのように計算しますか?私は自分のコードにiOSでSwift / SceneKitを使用していますが、一般的な答えは結構です。

形状を表す2つの配列があります。1つは、形状を構成する頂点を表す3Dポイントの配列です。もう1つの配列は、頂点を三角形にマッピングする最初の配列のインデックスのリストです。そのデータを取得して、形状の照明を支援する表面法線のセットである3番目の配列を生成する必要があります。(SCNGeometrySourceSemanticNormalSceneKitを参照)

頂点とインデックスのリストは、クラスへの入力に応じて常に異なるため、サーフェス法線を事前に計算したりハードコードしたりすることはできません。


もっとコンテキストが必要です。パラメトリックサーフェスの解析法線を計算しようとしていますか?暗黙の表面?または、一般的な三角形メッシュから法線を計算しますか?または、他の何か?
ネイサンリード

おかげで、詳細を追加しました。あなたの質問に答えるには、一般的な三角形メッシュから法線を計算する必要があります。メッシュは入力によって異なることは明らかですが。私の形は3D矢印です。ここの例は、2つの異なる形式(放射状と線形)のスクリーンショットです。このクラスは、要求に応じてメッシュの幅、深さ、長さ、円弧、および半径を変更します。cl.ly/image/3O0P3X3N3d1dこれを解決しようとする私の貧弱な試みで私が得ている奇妙な照明を見ることができます。
macinjosh

3
短いバージョンは、各頂点の法線を、それに接するすべての三角形の法線の正規化された合計として計算します。ただし、これによりすべてがスムーズになりますが、この形状には望ましくない場合があります。後で完全な回答に拡大してみます。
ネイサンリード

スムーズが私が目指しているものです!
macinjosh

4
ほとんどの場合、頂点の位置を分析的に計算すると、法線も分析的に計算できます。パラメトリックサーフェスの場合、法線は2つの勾配ベクトルの外積です。三角形の法線の平均を計算するのは単なる近似であり、多くの場合、視覚的にはるかに品質が低下します。私は答えを投稿しますが、SO(stackoverflow.com/questions/27233820/…)に詳細な例を既に投稿しているので、ここでコンテンツを複製するかどうかはわかりません。
レトコラディ

回答:


9

あなたは単に完全に滑らかな結果を望んではいけません。Nathan Reedがコメントした方法は「各頂点を法線方向に計算し、それらを合計し、合計を正規化する」が、一般的にはうまく機能しますが、ときどき見事に失敗します。しかし、ここでは重要ではありません。拒否句を追加することで、この方法を使用できます。

この場合、特定の部分を他の特定の部分に対して平滑化しないようにするだけです。選択的なハードエッジが必要です。したがって、たとえば、平らな上部と底部は、それぞれの平らな領域と同様に、側面の三角形ストリップから分離されています。

後の画像

画像1:希望する結果。

実際には、曲線領域の頂点のみを平均化し、他のすべての頂点は、三角形のみから得られる法線を使用できます。したがって、メッシュは、他の領域なしで処理される9つの別個の領域と考える方が適切です。

メッシュと法線の表示]

画像2:メッシュ構造と法線を示す画像。

プライマリ頂点の法線から特定の角度の外側にある法線を含めないことで、これを自動的に推測できます。擬似コード:

For vertex in faceVertex:
    normal = vertex.normal
    For adjVertex in adjacentVertices:
        if anglebetween(vertex.normal, adjVertex.normal )  < treshold:
            normal += adjVertex.normal
    normal = normalize(normal)

それは機能しますが、別々のプレーンの動作が異なることを理解しているため、作成時にこのすべてを単純に回避できます。したがって、通常の方向のマージが必要なのは湾曲した側面のみです。実際、基礎となる数学的形状から直接計算することができます。


9

主に、生成された形状の法線を計算する3つの方法があります。

分析法線

場合によっては、法線を生成するのに十分な表面情報があります。たとえば、球上の任意の点の法線を計算するのは簡単です。簡単に言えば、関数の導関数を知っているときは、法線も知っています。

分析法線を使用できるほどケースが狭い場合、おそらく精度の点で最高の結果が得られます。ただし、この手法はあまりうまくスケーリングしません。分析法線を使用できない場合も処理する必要がある場合、一般的な場合を処理する手法を維持し、分析的なものを完全に削除する方が簡単かもしれません。

頂点法線

2つのベクトルの外積は、それらが属する平面に垂直なベクトルを与えます。したがって、三角形の法線を取得するのは簡単です。

vec3 computeNormal(vec3 a, vec3 b, vec3 c)
{
    return normalize(crossProduct(b - a, c - a));
}

さらに、上記の例では、外積の長さはabc内の面積に比例します。したがって、複数の三角形で共有される頂点の平滑化された法線は、外積を合計し、最後のステップとして正規化することで計算できます。

vec3 computeNormal(vertex a)
{
    vec3 sum = vec3(0, 0, 0);
    list<vertex> adjacentVertices = getAdjacentVertices(a);
    for (int i = 1; i < adjacentVertices; ++i)
    {
        vec3 b = adjacentVertices[i - 1];
        vec3 c = adjacentVertices[i];
        sum += crossProduct(b - a, c - a);
    }
    if (norm(sum) == 0)
    {
        // Degenerate case
        return sum;
    }
    return normalize(sum);
}

クワッドを使用している場合、使用できる素晴らしいトリックがあります。クワッドabcdcrossProduct(c - a, d - b)は、クアッドが実際に三角形である場合にうまく処理できます。

イニゴquilezトピックに関するいくつかの短い記事を書いた:巧妙なメッシュの正規化、および通常の面積のn辺のポリゴンを

偏微分からの法線

法線は、フラグメントシェーダーで偏微分から計算できます。背後の計算は同じですが、今回は画面スペースで行われます。Angelo Pesceによるこの記事では、テクニック:Normals without normalsについて説明しています。


1
4番目の方法は、アーティストが法線を供給したことです;)
joojaa

@joojaa:ノーマルマップを参照していると思いますか?そうでなければ、手動で作成された法線について聞いたことがありません。
ジュリアンゲルトー

1
いいえ、手動で作成された法線。プログラマモデルよりも法線がどのように振る舞うべきかについて、アーティストがよりよく知っていることが時々起こります。計算エンジンが法線が基礎となる計算に由来すると想定する場合、計算エンジンに少し問題がある場合があります。しかし、確かにそれが起こり、数学的モデリングで多くの時間を節約できます。
-joojaa

1
これらは、「明示的な法線」(3ds maxおよびmaya用語)と呼ばれることもあります。
ドゥサンボスニャック「ペールヘッド」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.