質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

5857閲覧

法線ベクトルについて

windowsaa

総合スコア16

OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/11/13 09:38

編集2017/11/14 07:36

STLデータを表示するプログラムです。

いま法線ベクトルを設置したいと考えていますが方法がわからず詰まっています。

よろしくお願いします

[

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bongo

2017/11/13 10:36

以前のご質問の続きでしょうか?回答として十分でない気がしますのでこちらの欄にコメントしますが、元のデータがSTLの場合、先のご質問でmasaya_ohashiさんがおっしゃった「すべてのポリゴンごとに頂点がバラバラに定義されているデータ」になってしまうことは避けられないように思います。自前で全頂点の座標を比較して同じ座標のものを同じ頂点としてまとめていくしかないかもしれません。そちらの方針で進めてみてはどうでしょうか?
masaya_ohashi

2017/11/14 07:56

質問内容を削除するような編集はteratailでは原則不可とされています。編集履歴から内容を復元してください。https://teratail.com/help#delete-question また、そもそも回答してくれた人に失礼な行為です。
guest

回答2

0

masaya_ohashiさんの回答を拝見し、質問者さんがしたいことの想像がついたので少しコメントしてみます。

面法線ベクトルしか情報がないSTLからOpenGLで表示する際に「面法線を指定する」方法と「頂点法線をして基数」方法があると思います。

###面法線を指定する方法

これはSTLにある法線をそのまま指定すればよいと思います。こうすると当然ながらエッジ(メッシュの辺)は滑らかにはならず文字通りポリゴンが平面の集まりとして表示されます。

###頂点法線を指定する方法

三角メッシュ上の任意の点の法線は3つの頂点に対するその点の位置に従い、3つの頂点からのなんらかの加重平均的な手法で計算すると思います。こうするとスムーズな形状のシェーディングができるわけですが、隣り合ったメッシュのエッジがスムーズに見えるためには特定の頂点の頂点法線を、その頂点を含む全てのメッシュの面法線から計算する(※1)ことになると思います(masaya_ohashiさんの回答にある自力計算の方)。

ただ(※1)の具体的な方法は一種類ではなくいくつかの考え方があると思います。

(1) 単純に頂点を共有するメッシュの面法線を平均する
OpenGLが正規化をしてくれたような気もしますので単純に足せばいいのかも知れません。

(2) 加重平均をとる
これも一種類ではないですが、例えばメッシュの面積に応じた加重平均を取るなどとすると割合自然な形状に見える気がします。

「メッシュ 頂点法線 計算」で検索したところ下記のPDFに割合丁寧に加重平均の具体的手法の解説がありました。この解説では「メッシュの面積に応じた加重平均」が例として述べられています。

http://kanamori.cs.tsukuba.ac.jp/jikken/inner/triangle_mesh.pdf

投稿2017/11/14 02:25

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2017/11/14 02:27

あああ・・・前の質問をみました。すでに詳しく回答されているのですね・・・ 自分のコメントは蛇足だったと思います。orz
masaya_ohashi

2017/11/14 05:36

質問文に前の質問について書いてなかったので仕方のないことです(´・ω・`)
guest

0

ベストアンサー

STLファイルというのを知らなかったので調べてみましたが、面法線しか持たないデータ形式のようです。であれば、データから自ら頂点インデックス方式に変換する、またはなんらかのツールでSTLファイルを頂点インデックス対応の別の形式に変換するという二通りがあるかと思います。

自力で変換

  1. STLreader::countTrianglesでポリゴン数をカウント、必要な数のTriangleを用意する
  2. stlreader.readXXXで実際のポリゴンデータを取得する
  3. 頂点インデックス方式用の頂点の配列を1つ、それとインデックス用のintの配列、何個コピーしたかをカウントする変数を用意する
  4. 元データのTriangleの中の各頂点を、頂点インデックス方式用の頂点配列にコピー、インデックス用配列にインデックスを追加、コピーカウントを+1する。ただし、すでにコピーされている頂点の中にxyzが一致するものがある場合、頂点をコピーせず、カウントも増やさず、インデックスだけを追加する。
  5. 出来上がった頂点インデックス方式の頂点と、インデックスから頂点法線用のベクトルデータを計算する

データをツールで変換

これは自分で探すしかありません。無料のもので済むならそれでいいですし、有料のものが必要なら自分で買うひつようがあります。STL形式は面法線しか対応していませんが、OBJ形式やX形式等にツールを通して変換すれば、もしかしたら頂点インデックス形式に変換してくれるかもしれません。

投稿2017/11/14 00:51

masaya_ohashi

総合スコア9206

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2017/11/14 01:40

> 面法線しか持たないデータ形式 というわけでもないと思います。三角形のメッシュひとつづつ、3つの頂点とそのメッシュの法線ベクトルを並べた形式だと思います。法線はメッシュと垂直にするケースもあればシェーダーで特別な効果を得る(例えばより滑らかなシェーディングを狙って)メッシュと必ずしも垂直ではないベクトルを用いる場合もあるようです。 https://ja.wikipedia.org/wiki/Standard_Triangulated_Language
masaya_ohashi

2017/11/14 01:43

確かにWikipediaには「一部のソフト(例:SolidWorks)ではシェーディング効果に法線ベクトルを利用する。そのためこのようなソフトでは三角形の面に対する真の法線ベクトルとはならない。」とありますね。何れにせよ各頂点単位の法線データを持てる形式ではないようですので、自力で計算かデータ変換しか手はないですね。
KSwordOfHaste

2017/11/14 02:01

すみませんmasaya_ohashiさんの回答をきちんと把握できてませんでした。 自分はこの質問をみて「法線ベクトルを設置」の「法線ベクトル」が頂点の法線ベクトルということがわかってませんでした。OpenGLでメッシュを描画する場合はシェーダーへ頂点ベクトルを支持するのが必要という話をそういえば読んだことがあります。失礼しました。
masaya_ohashi

2017/11/14 02:16

あ、確かにこの質問には法線の平均化うんぬんという「前回の質問の内容」が含まれていないですね…すみません前回の質問の延長線で答えていたので、とくに説明もなく頂点インデックス方式の話を持ち出してしまいました。
windowsaa

2017/11/14 05:37

たくさんの回答ありがとうございます。 いまは、方針がかわり、面法線ベクトルを設定したいと考えています。 double vec1[3], vec2[3], norn[3]; //vec1 = stlreader.cross(&vec1[0], &vec2[0], &norn[0]); stlreader.normVec(&norn[0]); glNormal3dv(&norn[0]); このような書き方で会っていますか?
windowsaa

2017/11/14 05:37

あとはvecに値を格納するという感じです
masaya_ohashi

2017/11/14 05:44

面法線を使うなら、Triangleに入っているnornVecとなにが違うのでしょう?その計算いらなくないですか?
windowsaa

2017/11/14 05:46

nornVecの値を足し合わせた後に正規化ということですか?
masaya_ohashi

2017/11/14 05:47

いま気付いたのですがgetNormalVertの処理に問題があります。戻り値として一時変数を返しているのは危険な処理です。dotやcrossやnormVecのように、引数としてoutput用の変数を受け取って、そこに結果を格納する形にしないと正常に動作しない可能性があります。
masaya_ohashi

2017/11/14 05:48

> nornVecの値を足し合わせた後に正規化ということですか? 足し合わせるとはどういうことですか?なにとなにを足すつもりでしょう?
windowsaa

2017/11/14 05:55

すみません。よくわかっていなく変な質問をしてしまいました 面法線の設定の仕方をおしえてもらえませんか?
masaya_ohashi

2017/11/14 06:04

面法線というのはポリゴン(三角形)1枚に対して垂直な法線のことを指します。三角形の二辺の外積が面に対し鉛直なベクトル、つまり面法線となります。ここまでは理解されていますか? STLに入っている法線データはポリゴンに対する法線です。この法線は面に対して鉛直であることがほとんどですが、データを出力したソフトによってはなめらかなシェーディングになるために少しずれた法線になっていることもあります。なんにせよ、STLには最初から法線が入っています。わざわざ頂点から外積を計算しなくても、その法線を使えばよいだけです。設定の仕方と言われたら、あなたのコードはすでにTriangleのnormVecを使うようになっているのでなにもしなくてもそうなっています。 glNormal3dv(triangles[i].nornVec); //←これがSTLに入っている法線を使っている箇所 glVertex3dv(triangles[i].pnt1); glVertex3dv(triangles[i].pnt2); glVertex3dv(triangles[i].pnt3);
windowsaa

2017/11/14 06:23

丁寧な解説ありがとうございます!! +αなんですか、この法線ベクトルをつかうことにより陰影をつけるということは可能ですか?
windowsaa

2017/11/14 06:30

KSwordOfHasteさんの (1) 単純に頂点を共有するメッシュの面法線を平均する OpenGLが正規化をしてくれたような気もしますので単純に足せばいいのかも知れません。 ↑と個人的には思いました
masaya_ohashi

2017/11/14 06:36

> (1) 単純に頂点を共有するメッシュの面法線を平均する > OpenGLが正規化をしてくれたような気もしますので単純に足せばいいのかも知れません。 これは私が言っている頂点ごとの法線を計算した上で、法線を正規化しなくても勝手にGLが正規化してくれるモードがある、という話です。面法線から勝手に頂点法線を計算してくれるという話ではないです。 いまさらながら気付いたのですが、そもそもあなたのコードには「光源の設定」が一切ないです。これではglNormal3fv等で法線を指定したところで、陰影の計算が行われません。もしかして陰影が付かないことに悩んでいたのでしょうか? 光源の設定に関してはここのページを参考にどうぞ。 http://www.wakayama-u.ac.jp/~wuhy/GSS/06.html
KSwordOfHaste

2017/11/14 06:43 編集

OpenGLが正規化してくれるという「正規化」とは「長さが1のベクトルにする」ということしか意味しませんので、「滑らかになるような適切なベクトルを計算してくれる」ことは意味しません。そこを勘違いしないようにしてください。 重みづけを無視して単純な平均を取るというのは単なる例として挙げたのですがあまりよい方法とは言えないです。実際やってみると分かると思いますので、試しにトライしてみてもよいかも知れません。期待どおりにならなくても、知見を深めるという意味では良い経験になるかも知れません。
windowsaa

2017/11/14 06:45

KSwordOfHasteさん 単純に頂点を共有するメッシュの面法線を平均するというプログラムを書きたいと思っていました。 足し合わせる用の変数を用意した後の処理についておしえてもらえませんか? masaya_ohashiさん 光源は設定できました。ありがとうございます!
KSwordOfHaste

2017/11/14 06:49

自分はC++でOpenGLを書いたことはありません。ですので、クラスライブラリーを調べてメンバー関数などの仕様を調べてからでないと実際のコードは書けません。自分が取り組む場合はそうやって調べて書きますので質問者さんも同様にすることをお勧めします。不明な点があるならどこまでわかってどこがわからないのかを明確にして別途質問を挙げた方がよいでしょう。本来は回答コメントに「次々に質問する」ことはマナー的によくないと思います。聞きたいことは最初から全て質問に書くようにしてください。
masaya_ohashi

2017/11/14 06:52

光源が設定できたのなら、現状の3Dモデルがどんなふうに表示されているかよく見てください。三角形ごとに色が均一で、三角の境目がはっきり見える状態かと思います。これが面ごとの法線でシェーディングするということです。頂点単位の法線を計算した場合、境目が滑らかになり、角張った感じが和らげられます。 で、この状態で満足であればこれで終了です。なめらかな陰影にしたいのであれば、私のこの回答に書いてある通り、STLデータから頂点単位の法線を自力で計算するか、データをSTLからなんらかのデータに変換、読み込み処理を新しく作成する、のいずれかになります。
masaya_ohashi

2017/11/14 06:54

蛇足ですが、drawのたびにSTLファイルからポリゴン数と頂点を取り出すのは凄まじく無駄な処理です。glutMainLoopの呼び出しより前に済ましてしまって、いちいちループのたびにnew/deleteせずにずっとグローバル変数で持っておくべきです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問