床井研究室

忙しい

こんなこと書いてる場合ではないんですけど、自分用にメモしておきます。

矩形を描く

フラグメントシェーダで画像処理をするときとかは、クリッピング空間を覆う矩形ポリゴン、すなわち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);
}

フラグメントシェーダは同じです。

注意事項

いま書いてるプログラムから抜粋・変更しながら書いていてテストしてないので、間違ってる可能性は高いです。