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

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

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

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

3DCG

コンピュータの演算により、3次元空間の仮想物体を、2次元平面上で表現する手法である。

GLSL

GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

Q&A

解決済

1回答

5869閲覧

OpenGLで法線ベクトルをバーテックスシェーダーに渡すには

fennec

総合スコア19

OpenGL

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

3DCG

コンピュータの演算により、3次元空間の仮想物体を、2次元平面上で表現する手法である。

GLSL

GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

0グッド

0クリップ

投稿2017/06/07 11:30

現在OpenGLを勉強中で、床井氏のサイトを参考に進めています。
そこで分からない部分がありまして、
http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20090914
このページでは、position(attribute変数)を頂点法線ベクトルとしてシェーダー内で扱っていますが、
positionではなく3Dモデルデータから取り出した頂点法線ベクトル・面法線ベクトルをシェーダー内で使うには
どのようにすればいいのでしょうか。
また、フラットシェーディングを行いたいのですが、その場合は面法線ベクトルを使用するのでしょうか。
ご教授ください。お願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

頂点単位で渡したいデータが複数ある場合は、必要な分だけattribute変数を足してやれば大丈夫です。
第13回 テクスチャ座標の生成まで読み進められますと、attributeを追加する丁度いい例が載っていましたので、ご参考になるかと思います。
例ではテクスチャ座標の追加ということで、追加した要素は2要素のvec2ですが、法線の場合でも要素数が3個になるだけで大差はありません。
肝になる部分は、

  • バーテックスシェーダーに法線用のattribute変数を追加する
  • シェーダーリンク時にglBindAttribLocationでシェーダー内の変数に一意な番号を付ける(または、シェーダーをリンクした後で、自動的に設定された番号をglGetAttribLocationで調べる)
  • その番号をglEnableVertexAttribArrayを使って使用可能にする(初期状態ではすべての番号は使用不能に設定されているので、明示的に使用可能にします)
  • glVertexAttribPointerで、その番号と対応する属性の要素数(floatやintなら1、vec2やivec2なら2...ここでは法線なので、vec3が適当でしょう。その場合は3です)、要素の成分のデータ型(vec3は3つのfloatからなるのでGL_FLOAT)、整数で与えられたデータを正規化するか(もし法線でなく、例えば色の情報を追加するとして、しかも与える側のデータが0〜255の整数...などという状況の場合、これを設定するとシェーダー側では0.0〜1.0に変換された値として取り出されるので便利)、ストライド(ある頂点の頂点データの先頭から、次の頂点の頂点データの先頭まで何バイト読み進めればいいか...今まで位置しか属性を使用していなかった場合は0を設定していたかもしれませんが、0の場合は自動的に「1つの属性だけが密に詰まっている」と解釈してくれるため正しく指定されますが、今回のように複数の属性を交互に詰める場合、読み進める量を正しく渡してやる必要があります)、オフセット(この属性が頂点データの先頭から何バイト先にあるか...今回の場合、法線データの前には位置データが入っていますので、float3つ分で4*3=12バイトのずれがあります)を指定する

といった事項でしょうか。これでシェーダーに位置に加えて法線も渡せるようになるでしょう。
頂点法線の場合は特に問題ないでしょうが、面法線の場合は、書き出されたデータ形式によっては、頂点の数と法線の数が一対一対応していないかも知れません。OpenGLは頂点の各属性が頂点の数と一対一対応していることを期待しているので、その場合は詰めるデータを加工して一対一対応させた上でVBOに送らなければならないかもしれません。
面法線を送り込むことができれば、あとは頂点法線の場合と同様に光源と法線で内積計算して色を決めれば、フラットシェーディングらしい描画結果が得られるでしょう。

投稿2017/06/07 14:53

編集2017/06/08 03:10
Bongo

総合スコア10807

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

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

fennec

2017/06/09 19:31 編集

いつも丁寧な回答ありがとうございます。返信遅くなって申し訳ないです。 第11回で止まってました。第13回に例が載っているとは。。 無事、頂点法線ベクトルはシェーダに渡せたのですが、面法線ベクトルはglDrawElementsを使って描いていると複数のポリゴンで頂点が被る場合があるので、頂点属性として上手く渡せません。glDrawArraysを使うしかないのでしょうか。 また、面法線ベクトルの場合も、シェーダ内で頂点法線ベクトルと同様なライティング処理を行えば良いのでしょうか。フラットシェーディングについて調べたところ、in out変数の前にflatを付ければ出来ると知ったのですが、これは面法線ベクトルを使って行った場合と結果は同様なのでしょうか。 今一バーテックスシェーダーの役割というか、シェーダ内でやっていることと、処理の流れが掴めません。 質問ばかりで申し訳ないですが、よろしくお願いします。
Bongo

2017/06/08 19:52 編集

そこなんですよね...一つの頂点を複数の面が共有していると、位置は同じなのに法線が複数必要なため法線の数だけ頂点を重複させるよう加工しないとならなかったりします。flatを使う方法で見た目的に問題なさそうなら、そちらの方が手軽でしょう。 バーテックスシェーダは頂点の数だけ(10個の三角形を描画する場合30回)実行され、主な作業は元のデータを画面上の意図した位置へ配置することになります(モデル・ビュー・プロジェクション変換などと呼ばれるような作業をここで行うことになるでしょう)。 フラグメントシェーダはフラグメントの数だけ(ある三角形に注目すると、その三角形が最終的に画面内を占めるピクセルの数だけ)実行されます。主な作業は最終的なピクセルの色の決定になるでしょう。この時フラグメントシェーダーに送られるvarying変数(in変数)の値は、通常は三角形の各頂点についてバーテックスシェーダーで出力したvarying変数(out変数)が丁度よくブレンドされた値になります。flat指定すると、その変数についてはこの補間処理が行われなくなります。 WebGLの記事ですが、例えばhttps://sbfl.net/blog/2016/09/04/webgl2-tutorial-basics/などに描画の流れの図がありましたので、ご参考になるかと思います。 以前ご提示いただいた情報によるとGLSL 1.30が使用可能なようですので、シェーダーを1.30の書式に書き換えればflatを使えるようになるかと思います。法線用のバーテックス側out変数・フラグメント側in変数をflatにすると、フラグメントシェーダ側では補間なしの値が得られるので、フラットシェーディングらしい見た目にはなるかと思いますが、この補間なしの値というのは三角形の最初の頂点、または最後の頂点(glProvokingVertexで切り替えることができます)のものとなるのでご注意ください(もし、厳密に意図通りの描画にするには頂点の法線データ部分、三角形描画のインデックス指定順を工夫しないとならないかもしれません)。描画結果は、旧OpenGLでglShadeModel(GL_FLAT)を使った時のような見た目になると思います。 その他、別のアプローチとして、フラグメントシェーダでdFdxとdFdyをpositionに対して使用すると、画面水平方向と垂直方向のpositionの変化率...つまり面の傾きを得ることができます。この2つのベクトルの外積が面法線の向きとなりますので、フラグメントシェーダ中でこの面法線と光源の内積を取ってピクセルを塗ればフラットな見た目にできます。 同じくWebGLの記事で申し訳ないですが、https://wgld.org/d/webgl/w087.htmlにこの手法が紹介されていました。なお、サイト中でOES_standard_derivativesなどに触れられておりますが、通常のOpenGLには微分関数が標準搭載されていますので、特に気にされる必要はないかと思います。
fennec

2017/06/09 19:34 編集

返信ありがとうございます。 一つ一つ丁寧に教えてくださって本当に助かっています。シェーダーについてはほぼ理解できました。 頂点が被る問題についても、とりあえずglDrawElementsのままでいくことにしました。やはり法線と対になるよう頂点配列を揃えないといけないのですね。なにかもっとうまい方法がありそうな気がしますが、一通りの操作が出来るようになってから色々試していきたいと思います。 フラットシェーディングはコメントの最後の方で教えて下さったdFdx・dFdyを使う方法で実装出来ました。こちらで行ったほうが綺麗に陰影をつけることができました。flatを使う方法だとローポリの場合、面法線と頂点法線との差が大きいからか陰影が不自然になりました。 ある程度レンダリング出来るようになったので、次はテクスチャの貼り方や座標変換を学んでいこうと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問