Experiment


8. 立体視プログラミング

立体視の機能をプログラムに実装するためのプログラミング方法について学習します。
課題1で作成した3次元CGプログラムをもとに、バーチャルリアリティで要求される機能を追加していきます。ここでは偏光方式による立体視の機能を追加するためのプログラミング方法について学びます。


8.1 ウインドウの生成

ここでは、立体視を行うための具体的なプログラミング方法について学習します。以下の手順で sample3 のサンプルプログラムをダウンロードして下さい。

  ・http://green.cc.tsukuba.ac.jp/tetsu/lecture/exp/ にアクセスし、
   sample3.tarをダウンロード
  ・% tar xvf sample3.tar によりファイルを展開

以下がsamaple3の実行結果です。


                 図8.1:ステレオ表示されたティーポット

偏光立体視を行うには、左右の視点映像を同時に出力することが必要になります。そのためここでは、2画面分のサイズでウインドウを開き、左半分には左目の視点映像を、右半分には右目の視点映像をレンダリングする方法を行います。
2画面分のウインドウサイズを開くには、glutInitWindowSize() で横に2倍の幅を取ったウインドウサイズを指定します。例えば、このサンプルプログラムでは、以下のようにウインドウの設定を行っています。例にならって各自のプログラムのウインドウ生成部分を書き換えてみましょう。

  int main(int argc, char **argv)
  {
  ..........
  /* open window */
  glutInitWindowPosition(0, 0);    /* double size window */
  glutInitWindowSize(1024, 384);
  glutCreateWindow(argv[0]);
  ..........
  }

上の例では、4:3の大きさのウインドウを横に2つ並べ、1024×384の大きさのウインドウとして、モニタの左上の端に配置しています。
ここでは分かりやすいようにモニタの大きさよりも小さいウインドウを開いていますが、実際にはフルサイズの映像をプロジェクタに出力するため、例えばモニタサイズが1024×768の場合、2倍の大きさの 2048×768 でウインドウを開きます。また1台の計算機によって2画面を出力するには、グラフィックスカードのデュアル出力の機能を使用することで、第1画面の出力からは左目映像、第2画面の出力からは右目の映像をフルサイズで出力することが可能になります。


 
8 .2 視差映像の生成

2倍の大きさのウインドウを開いたら、左右の半分ずつの領域にそれぞれ左目、右目の視点映像を描画します。そのため、プログラムでは、display() 関数内で同じモデルを視点を変えて2回ずつレンダリングする必要があります。以下は、サンプルプログラムのレンダリング部分を示したものです。
ウインドウ内で左右の視点映像を描画する領域を変えるには glViewport() の関数で、ビューポートを変更します。ここでは、左目用の映像には (0, 0) の座標点を起点とした左半分の領域を設定し、右目用の映像には (512, 0) を起点とした右半分の領域を設定しています。
また gluLookAt() 関数によって、左目用映像、右目用映像に対してそれぞれ視点位置の変更を行っています。

  void display(void)
  {
  .........
  /*** Left Eye ***/
  /* viewport transform */
  glViewport(0, 0, 512, 384);

  /* projection transform */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-0.079, 0.081, -0.08, 0.04, 0.05, 100.0);

  /* viewing transform */
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(-0.04, -1.2, 0.8, -0.04, 0.0, 0.8, 0.0, 0.0, 1.0);

  /* modering transform */
  draw_model();

  /*** Right Eye ***/
  /* viewport transform */
  glViewport(512, 0, 512, 384);

  /* projection transform */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-0.081, 0.079, -0.08, 0.04, 0.05, 100.0);

  /* viewing transform */
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(0.04, -1.2, 0.8, 0.04, 0.0, 0.8, 0.0, 0.0, 1.0);

  /* modering transform */
  draw_model();

  glFlush();
  }

ここでは、利用者が正面を向いた状態を想定し、左目、右目の視点位置に対してそれぞれx座標の値だけを変えて gluLookAt()、glFrustum() の設定を行っています。 インタラクティブな立体映像を描画するためには、実際の利用者の視点位置を計測して、gluLookAt() の視点位置の代入、glFrustum() のパラメータの計算を行います。

また、プログラムの中でモデルを描画する部分は、左目用の映像も右目用の映像も共通であるため、ここでは draw_model() として別の関数にすることでプログラムの記述を効率化しています。

  void draw_model(void)
  {
  GLfloat matDiff1[] = { 0.0, 0.0, 0.3, 1.0 };
  GLfloat matSpec1[] = { 0.0, 0.0, 0.2, 1.0 };
  GLfloat matAmbi1[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat matDiff2[] = { 0.6, 0.3, 0.0, 1.0 };
  GLfloat matSpec2[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat matAmbi2[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat matDiff3[] = { 0.0, 1.0, 0.0, 1.0 };
  GLfloat matSpec3[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat matAmbi3[] = { 0.0, 0.0, 0.0, 1.0 };

  /* floor */
  glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiff1);
  glMaterialfv(GL_FRONT, GL_SPECULAR, matSpec1);
  glMaterialfv(GL_FRONT, GL_AMBIENT, matAmbi1);
  glBegin(GL_POLYGON);
  glNormal3f(0.0, 0.0, 1.0);
  glVertex3f(-2.0, -2.0, 0.0);
  glVertex3f( 2.0, -2.0, 0.0);
  glVertex3f( 2.0, 2.0, 0.0);
  glVertex3f(-2.0, 2.0, 0.0);
  glEnd();

  /* table */
  glPushMatrix();
  glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiff2);
  glMaterialfv(GL_FRONT, GL_SPECULAR, matSpec2);
  glMaterialfv(GL_FRONT, GL_AMBIENT, matAmbi2);
  glTranslatef(0.0, 0.0, 0.60);
  glRotatef(90.0, 1.0, 0.0, 0.0);
  Cylinder(0.8, 0.02, 30);

  glTranslatef(0.5, -0.3, 0.5);
  Cylinder(0.02, 0.30, 30);
  glTranslatef(-1.0, 0.0, 0.0);
  Cylinder(0.02, 0.30, 30);
  glTranslatef(0.0, 0.0, -1.0);
  Cylinder(0.02, 0.30, 30);
  glTranslatef(1.0, 0.0, 0.0);
  Cylinder(0.02, 0.30, 30);

  /* teapot */
  glPopMatrix();
  glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiff3);
  glMaterialfv(GL_FRONT, GL_SPECULAR, matSpec3);
  glMaterialfv(GL_FRONT, GL_AMBIENT, matAmbi3);
  glTranslatef(0.0, 0.0, 0.76);
  glRotatef(90.0, 1.0, 0.0, 0.0);
  glutSolidTeapot(0.2);
  
  return;
  }


※演習:
上記の立体視の機能を各自が作成したプログラムに組み込み、2画面分の出力を行うように修正してみましょう。