三角形のリストから頂点法線を計算する最良の方法


8

こんにちは、私はコンピュータグラフィックスの完全な初心者です。愚かな答えだとすみません。シンプルな3Dエンジンをゼロから作ろうとしています。実際の使用よりも教育目的のためです。

今のところ私は顔の法線のみを計算します。この方法では:

Triangleのリストの中にあるSurfaceオブジェクトがあります。Triangleクラス内の法線を次のように計算します。

triangle.computeFaceNormals() {
    Vec3D u = v1.sub(v3)
    Vec3D v = v1.sub(v2)
    Vec3D normal = Vec3D.cross(u,v)
    normal.normalized()
    this.n1 = this.n2 = this.n3 = normal
}

そして表面を構築するとき:

t = new Triangle(v1,v2,v3)
t.computeFaceNormals()
surface.addTriangle(t)

これが一番いい方法だと思います…。

さて、これでうまくいきます。軽いですが滑らかにはなりません。頂点法線も計算しようとしています。(私は自分のエンジンを細管表面でテストしているので、ほぼすべての頂点が複数の三角形と共有されています)

私はこの単純なアルゴリズムを見つけました:フリップコード頂点法線 ですが.. heiこのアルゴリズムは..指数関数的な複雑さですか?(私の記憶が私のコンピュータサイエンスの背景に失敗しない場合)

なにか提案を?


これは何語?三角形ではなく(何も返さない)のt結果であるように見えcomputeFaceNormalsます。

それは疑似コードです:)あなたは本当のコードで正しいです私はまた「これを返す」、申し訳ありませんが私は編集しました!
nkint

回答:


6

「最高」はかなり主観的です。つまり、アルゴリズム、入力、実行時の複雑さ、およびその他のプロパティに対して必要なものを比較検討する必要があります。

とは言っても、あなたのアプローチとリンクされたFlipCodeアプローチの両方は、使用可能な結果を​​生成するという点で合理的です(三角形間で実際の頂点インスタンスを共有していない場合は、はっきりとわかりません)あなたのコードから)。他の手法には、各面法線の寄与を、頂点によって共有される各面とのなす角度の大きさで重み付けすることが含まれます。

あなたは、書かれたようFlipCodeアプローチは、次善の表示されていることで正しいですが、それだけで不十分指定することができます。それはトラバース第二のループを提案するつもりかどうかちょっと不明だすべて共有するフェースの一握りに対して、モデルで顔を問題の頂点。2番目のループの検索スペースを削減するための隣接情報がある場合、それはあまり問題になりません。もちろん、その隣接情報を持っていない可能性があります-これは、アルゴリズムで利用できる入力を検討することで、私が意味することです。

面の法線を頂点にコピーしたくない場合、または頂点を共有していてそれらを分割したくない場合は、次のことができます。

foreach vertex:
   set vertex normal to (0,0,0)

foreach triangle:
   foreach vertex on that triangle:
      set vertex normal = normalize( vertex normal + face normal )

これは、各三角形がその頂点をコピーするのではなく、実際に各頂点を参照していることを前提としています。使用している言語がわからないので、そうであるかどうかはわかりません。


申し訳ありませんが、英語は私の最初の言語ではないので、私が望むことを悪い方法で説明した可能性があります。頂点の法線はまだありません!私は私の答えを少し編集しました、もっと明確になってほしい
nkint

大丈夫です、あなたは頂点法線を持っていなかったと理解しました。私が提供した疑似コードは、最初に各頂点の法線を(0,0,0)に設定し、次に、頂点が接触する各面の面法線を合計することで計算します(もちろん、再正規化します)。

5
このように法線を正規化しないでください。別のforeachですべての合計の後に正規化する必要があります。たとえば、同じ法線を持つ三角形が10個あるが、最後が異なる場合(結果の法線は、10個の三角形の法線とほぼ等しいはずです)、最後の三角形を合計すると、次のようになります:set vertex nromal = normalize(( 1,0,0)+(0,0,1))と(0.5,0,0.5)は間違っています。私はあなたを混乱させなかったことを望みます。
zacharmarz

7

ジョシュ・ペトリーは権利を持っています。頂点法線を計算する場合は、三角形の寄与を角度で重み付けする必要があります。しかし、単純な解を使用して、頂点の周りのすべての面からすべての法線を合計することができます。

したがって、すべての面法線を計算して正規化するだけです。次に、すべての頂点法線をゼロに設定します。次に、各面(三角形)に対して、その法線をすべての頂点に追加します。次に、すべての頂点法線を正規化します。これで完了です。

正確ではありませんが、それで十分かもしれません。

上記の顔の法線の計算-正しいですが、どちらの側の法線の頭に注意する必要があります。上または下に向かう可能性があります。それは外積に依存します-AxBはBxAと同じではありません-反対のベクトルを与えます。


クロス積についての良い点。

2

6つの長方形から作られた立方体があると仮定しましょう。立方体の頂点法線は、鋭いエッジがあるため、接続面の正規化された合計ではないことはすでにわかっています。頂点値のマップを作成し、それが立方体の異なる頂点法線である場合、各頂点に3つの法線が作成されます。

上記の点を念頭に置いて、これが頂点法線を計算する方法です(テストして使用しています)。

class Face{
    uint vert_indices[3];
    vec3 surface_normal;
    vec3 vertex_normal[3];
}

2つの面が頂点を共有している場合があるため、各面は使用する頂点法線を追跡しますが、頂点法線が各面で同じになるわけではありません。

face2をレンダリングするときに、vertの頂点法線を計算しようとしていると仮定します。

vertface0face1face2face3およびface4によって共有されます。上記のすべての面は順番に隣接しており、face0face4が接続してループを形成しています。

頂点の法線頂点は、face2に接続されたすべての隣接チェーンの合計を正規化したものです。隣接する2つの面の間の角度が0.8ラジアン(角度== arccos(crossProduct(faceA.surface_normal、faceB.surface_normal)))を超えると、チェーンが停止します。

間の角度場合face0face1はラジアン以上0.8との間の角度face3face4のための通常の頂点よりもより0.8ラジアンであるVERTレンダリングするときface2がある正規化surfnormal1 + surfnormal2 + surfnormal3)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.