床井研究室

つゆ

島根県松江市のやすもと醤油は美味しいです。ただ、ぽん酢しょうゆとめんつゆの瓶が同じなので、先日子供がしらす丼にぽん酢しょうゆをかけようとして、間違えてめんつゆをかけてしまいました。しかし、食べてみると意外と美味しい! ということで、結局全部たいらげていました。まあ、そばつゆは天つゆとあんまり変わりませんから、組み合わせとして不自然ではないと思います。機会があれば、しらす丼にめんつゆをかけて食べてみてください。

バーテックスシェーダによる頂点座標の生成

solidSphere() では $(s, t)$ を使って頂点の3次元空間中の座標値 $(x, y, z)$ を求めています。$(s, t)$ はテクスチャ座標としてバーテックスシェーダに送っていますので、同じことはバーテックスシェーダでもできそうです。バーテックスシェーダで頂点の座標値を求めるようにすれば、バーテックスシェーダに頂点の座標値を送る必要もなくなります。$x = \cos 2\pi s \sin \pi t, y = \cos \pi t, z = \sin 2\pi s \sin \pi t$ ですから、これをバーテックスシェーダで計算します。attribute 変数 position を削除して、かわりに main() 内に vec3 型の変数 position を宣言します。

#version 120

// simple.vert

invariant gl_Position;
attribute vec2 texture;
//attribute vec3 position;
uniform mat4 projectionMatrix;
varying vec3 diffuseColor;
uniform vec3 lightDirection;
uniform vec3 lightColor;
varying vec2 t;

void main(void)
{
  float th = 6.283185 * texture.s;
  float ph = 3.141593 * texture.t;
  float r = sin(ph);
  vec3 position = vec3(r * cos(th), cos(ph), r * sin(th));

  t = texture * 16.0;

  diffuseColor = vec3(dot(lightDirection, position)) * lightColor;
  gl_Position = projectionMatrix * vec4(position, 1.0);
}

attribute 変数 position を削除したので、solidSphere() でこれに値を設定している部分を削除します。まず、Position 型の要素数を 5 から 2 に減らします。

...

/* 頂点バッファオブジェクトのメモリを参照するポインタのデータ型 */
typedef GLfloat Position[2];
typedef GLuint Edge[2];
typedef GLuint Face[3];

...

solidSphere() では、頂点位置を計算している部分を削除します。また $s$ と $t$ を、それぞれ (*position)[0](*position)[1] に代入します。

GLuint solidSphere(int slices, int stacks, const GLuint* buffer)
{
  ...

  /* 頂点の位置 */
  for (int j = 0; j <= stacks; ++j) {
    float t = (float)j / (float)stacks;
    //float ph = 3.141593f * t;
    //float y = cosf(ph);
    //float r = sinf(ph);

    for (int i = 0; i <= slices; ++i) {
      float s = (float)i / (float)slices;
      //float th = 2.0f * 3.141593f * s;
      //float x = r * cosf(th);
      //float z = r * sinf(th);

      //(*position)[0] = x;
      //(*position)[1] = y;
      //(*position)[2] = z;
      (*position)[0] = s;
      (*position)[1] = t;
      ++position;
    }
  }

  ...

attribute 変数 position がなくなったので、メインプログラムでも初期化を行う関数 init() において attribute 変数 positionindex に 0 を指定している部分を削除します。かわりに、attribute 変数 textureindex0 を指定します。

...

/*
** 初期化
*/
static void init(void)
{
  ...

  /* attribute 変数 position の index を 0 に指定する */
  //glBindAttribLocation(gl2Program, 0, "position");

  /* attribute 変数 texture の index を 0 に指定する */
  glBindAttribLocation(gl2Program, 0, "texture");

  ...

index が 1 の attribute 変数は使わなくなったので、これを指定している部分を削除します。また、頂点バッファオブジェクトには vec2 型 (GLfloat 2個組) のテクスチャ座標しか入っていないので、glVertexAttribPointer() の第2引数 size には 2 を指定し、第5引数 stride には 0 を指定します。

...

/*
** 画面表示
*/
static void display(void)
{
  ...

  /* 頂点バッファオブジェクトとして buffer[0] を指定する */
  glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);

  /* index が 0 の attribute 変数を有効にする */
  glEnableVertexAttribArray(0);

  /* index が 0 の attribute 変数に頂点バッファオブジェクトの場所と書式を設定する */
  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

  /* 頂点バッファオブジェクトの指標として buffer[1] を指定する */
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[1]);

  /* 図形を描く */
  glDrawElements(GL_TRIANGLES, points, GL_UNSIGNED_INT, 0);

  /* 頂点バッファオブジェクトを解放する */
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  glBindBuffer(GL_ARRAY_BUFFER, 0);

  /* index が 0 の attribute 変数を無効にする */
  glDisableVertexAttribArray(0);

  /* 固定機能に戻す*/
  glUseProgram(0);

  /* 隠面消去処理を無効にする */
  glDisable(GL_DEPTH_TEST);

  glFlush();
}

...

これでも前と同じ図形が描かれるはずです。なお、以下のサンプルファイルでは使っていない関数を削除するなど、若干整理しています。

バーテックスシェーダを切り替えて形を変える

バーテックスシェーダで頂点の座標値を求めてしまうと、solidSphere() はテクスチャ座標と頂点のつながり情報 (三角形のデータ) だけを生成することになり、球という形を決める処理を行いません。また、バーテックスシェーダでテクスチャ座標から頂点の座標値を求める方法を変更すれば、描画する図形の形を変えることができます。それではバーテックスシェーダを変更して、y 軸中心に半径 1, 上面の高さ $y = 1$, 底面の高さ $y = -1$ の円柱を描いてください。円柱では、球のように頂点位置をそのまま法線ベクトルに使うことができないことに注意してください。

バーテックスシェーダを変更して作成した円柱