■ 2016年08月31日 [OpenGL][GLSL] 矩形の描き方
忙しい
こんなこと書いてる場合ではないんですけど,自分用にメモしておきます.
矩形を描く
フラグメントシェーダで画像処理をするときとかは,クリッピング空間を覆う矩形ポリゴン,すなわち2点 (-1. -1), (1, 1) を対角の頂点とする正方形を座標変換せずに描くというのが(自分的には)定石なんですが.でも,そのたった4つの頂点のために VBO を作ったり attribute 変数割り当てたりするのもなんだなぁっていつも思ってたので,試しに VBO を作らずに矩形を描く方法を考えてみました.
VAO を作る
VAO はどうしても必要なので作ります.でも,VBO は作りません.ですので,attribute 変数も割り当てません.
const GLuint vao([]() { GLuint vao; glGenVertexArrays(1, &vao); return vao; } ());
glDrawArrays() で描画する
それを描画します.四角形なので4点描画します.GL_TRIANGLE_STRIP で描くのがミソです.
// シェーダプログラムを選択する
glUseProgram(shader);
...
// 頂点配列オブジェクトを指定して描画する
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
バーテックスシェーダ
attribute 変数がないので,頂点座標値はバーテックスシェーダ内で生成する必要があります.バーテックスシェーダが処理している頂点の番号は組み込み変数 gl_VertexID で調べられますので,それを使います.
#version 150 core // テクスチャ座標 out vec2 texcoord; void main() { // テクスチャ座標を求める texcoord = vec2(gl_VertexID % 2, gl_VertexID / 2); // テクスチャ座標から頂点座標を求めて出力 gl_Position = vec4(texcoord * 2.0 - 1.0, 0.0, 1.0); }
この矩形の全面にテクスチャを貼ることを想定して,先にテクスチャ座標を求めます.gl_VertexID は 0, 1, 2, 3 と変化しますから,テクスチャ座標はそれぞれ (0, 0), (1, 0), (0, 1), (1, 1) になります.これを2倍して1引いて頂点座標を求めています.
フラグメントシェーダ
フラグメントシェーダは受け取ったテクスチャ座標でテクスチャをサンプリングするだけです.
#version 150 core #extension GL_ARB_explicit_attrib_location : enable // テクスチャ uniform sampler2D image; // ラスタライザから受け取る頂点属性の補間値 in vec2 texcoord; // テクスチャ座標 // フレームバッファに出力するデータ layout (location = 0) out vec4 fc; // フラグメントの色 void main(void) { fc = texture(image, texcoord); }
メッシュを描く
同様にして,前回の最後に示したようなメッシュは,次のようにして描くことができます.まず,メッシュの幅 slices と高さ stacks から,メッシュの1段分の GL_TRIANGLE_STRIP の頂点数 vertexCount と,その繰り返し回数(段数)primitiveCount を求めます.
const GLuint vao([]() { GLuint vao; glGenVertexArrays(1, &vao); return vao; } ()); const GLuint vertexCount(slices * 2 + 2); const GLuint primitiveCount(stacks);
glDrawArraysInstanced() で描画する
一つの GL_TRIANGLE_STRIP を段数分だけ繰り返し描くので,一つの基本図形の複数のインスタンスを描画する glDrawArraysInstanced() を使います.
// シェーダプログラムを選択する
glUseProgram(shader);
...
// 頂点配列オブジェクトを指定して描画する
glBindVertexArray(vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, vertexCount, primitiveCount);
glDrawArraysInstanced() で描画する場合のバーテックスシェーダ
現在どのインスタンスの頂点を処理しているかは gl_InstanceID で知ることができますので,これを使って座標値を求めます.一応,ポリゴンが左回りになるように気を付けます.slices と stacks は uniform 変数で渡すことにします.もちろん,これらはともに逆数にして vec2 の uniform 変数に格納し,除算を乗算に書き換えたいところです.
#version 150 core // メッシュのサイズ uniform int slices, stacks; // テクスチャ座標 out vec2 texcoord; void main() { // テクスチャ座標を求める texcoord = vec2(gl_VertexID / 2, 1 - gl_VertexID % 2 + gl_InstanceID) / vec2(slices, stacks); // テクスチャ座標から頂点座標を求めて出力 gl_Position = vec4(texcoord * 2.0 - 1.0, 0.0, 1.0); }
フラグメントシェーダは同じです.
注意事項
いま書いてるプログラムから抜粋・変更しながら書いていてテストしてないので,間違ってる可能性は高いです.
お疲れ様です。<br>最近からOpenGLの勉強を再開させていただきました。<br>この文章を読んでものすごく勉強になりました。ありがとうございます。<br>一つ質問させていただきたく。OpenGLで3D管形状の物体はモデリングできますか?(座標は既定座標を使用前提)<br>すみません、いきなり質問してしまって、どうぞよろしくお願いいたします。
ウさま、コメントありがとうございます。お返事が遅くなり、申し訳ありません。<br>OpenGL 自体は画面表示を行うことが目的ですので、モデリングの機能はありません。<br>管形状のものであれば、古いライブラリですが、<br>GLE http://www.linas.org/gle/<br>を組み合わせることで、表示することが可能かと思います。
東工大の脇田と申します。いつも大変、お世話になっております。このサイトなしには生きていけません。<br><br>偶然なんですが、昨日、似たようなコードを書いていました。VBO なしで三角形のパッチをひとつ送り込んでテッセレーションで多角形に展開して描画する例と粒子シミュレーションで粒子の座標は頂点シェーダで初期化する例です。後者は計算シェーダを使うべきなんですがその前段階の実験としてやりました。<br><br>以下のフォルダの kw2.{py, shaders} が前者、後者は rt1.{py, shaders} です。Python + Qt5 という環境で作っています。<br>https://github.com/wakita/pyqtgl/tree/kw/kw
脇田先生、コメントありがとうございます。<br>私もこういうことがやりたかったんです。<br>すごく参考になります。<br>ありがとうございます!!!