Experiment


13. CAVEシステムへの拡張

作成中のプログラムを、CAVEシステムで動作させるための、拡張方法について学習します。
CAVE型ディスプレイでは、実寸の映像をスクリーン間で同期させて表示させるため、スクリーン構成に依存するパラメータの設定と、スクリーン間でのデータの受け渡しの機能を追加します。


13.1 各種パラメータ

CAVE用にプログラムを拡張するためには、ウインドウサイズ、視体積、センサ位置等の値をディスプレイに合わせて設定します。
~/sample/sample5/ にサンプルプログラムとして sample5-1、sample5-2、sample5-3 があります。それぞれCS Galleryの正面スクリーン、側面スクリーン、床面スクリーン用のプログラムです。ここでは、PCクラスタを使用したシステムを用いるため、各PCでそれぞれ対応する修正を行います。サンプルプログラムを参考にして、各自のプログラムの変更を行って下さい。

1) ウインドウサイズ
CAVEシステムではスクリーン全体を使用した映像表示を行うため、プログラムではフルサイズでウインドウを開いて映像の描画を行う必要があります。ここでは偏光方式による立体視映像の描画を行うため、1024x768 のフルスクリーンに対して横方向に2倍の大きさ 2048x768 のサイズのウインドウを開きます。左目用の映像はこのウインドウの左側半分の領域に、右目用の映像は右側半分の領域に描画します。また、画面全体を使用するためには、glutFullScreen() の関数を使用し、ウインドウシステムによって表示されるフレーム枠やメニューバー等を非表示に設定します。

・void glutFullScreen();
フルスクリーンモードでウインドウを開きます。

以下は、sample5 におけるプログラムの修正箇所を示したものです。
  void display(void)
  {
  .....
  /*** Left Eye ***/
  /* viewport transform */
  glViewport(0, 0, 1024, 768);
  .....
  /*** Right Eye ***/
  /* viewport transform */
  glViewport(1024, 0, 1024, 768);
  .....
  }

  int main(int argc, char **argv)
  {
  .....
  /* open window */
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
   glutInitWindowPosition(0, 0);
   glutInitWindowSize(2048, 768);
   glutCreateWindow(argv[0]);
   glutFullScreen();
  .....
  }

2) 視体積
次に実寸の大きさで映像を表示するために、スクリーンサイズに従った正しい視体積を設定する必要があります。CS Gallery では、2.63mx2.10m のスクリーンサイズになります。(側面スクリーンに関しては 2.10mx2.10m の大きさですが、投影のところでカットされるため、映像としては 2.63mx2.10m の大きさで作成して構いません。)サンプルプログラムでは screen_w, screen_h の変数でスクリーンサイズを与えているため、この変数の値を正しく設定します。
以下は、sample5 におけるプログラムの修正箇所を示したものです。

  void display(void)
  {
  float screen_w = 2.63;
  float screen_h = 2.10;
  float near = 0.05;
  float far = 100.0;
  ..........
  }

3 ) 視線方向
多面方のディスプレイでは、各スクリーンごとに視線方向が異なりますので、各計算機ごとに正しい視線方向を設定する必要があります。ウオークスルーの機能によって、仮想空間の中での利用者にとっての正面方向が変化します。正面スクリーンは利用者の正面方向と一致しますが、右面スクリーンは利用者の右方向、床面スクリーンは下方向となるため、以下の修正が必要になります。

sample5-2の修正箇所
  /* viewing transform */
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(viewpos[0][0]+walk[0], viewpos[0][1]+walk[1], viewpos[0][2],
         viewpos[0][0]+walk[0]+sin((turn[2]+90.0)/180.0*3.141592),
         viewpos[0][1]+walk[1]+cos((turn[2]+90.0)/180.0*3.141592),
         viewpos[0][2], 0.0, 0.0, 1.0);

  ..........
sample5-3の修正箇所
  /* viewing transform */
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(viewpos[0][0]+walk[0], viewpos[0][1]+walk[1], viewpos[0][2],
         viewpos[0][0]+walk[0], viewpos[0][1]+walk[1], viewpos[0][2]-1.0,
         sin(turn[2]/180.0*3.141592), cos(turn[2]/180.0*3.141592), 0.0);
  ..........


4 ) センサ位置
センサで計測されるディスプレイ空間内の位置と仮想空間内の位置の対応付けを行うためには、ワールド座標系とセンサ座標系の関係を正しく定義する必要があります。CS Galleryでは、ワールド座標系の(-1.54, 0.00, 1.06)の位置にトランスミッタが置かれ、ここがセンサ座標系の原点となります。サンプルプログラムでは、source_x, source_y, source_z の変数でワールド座標系に対するトランスミッタの位置を設定しているため、この変数の値を正しく設定します。また、トランスミッタの置き方によって座標系の方向が異なり、視点位置の計算方法も変更されますので、11章を確認して下さい。
以下は、sample5 におけるプログラムの修正箇所を示したものです。

  #include <GL/glut.h>
  #include <sys/shm.h>

  #define SHMKEY 61
  int shmid;

  float source_x = -1.54; /* transmitter position */
  float source_y = 0.00;
  float source_z = 1.06;


 
13.2 スクリーン間の同期

各スクリーン面ごとに以上のプログラムの修正を行い、各PCでプログラムを実行することで、各スクリーン面に対応した立体視映像の描画が行われます。次に、CS Gallery等の多面型ディスプレイを使用する場合の問題として、スクリーン間の同期の問題があります。

この実験課題では、静的な世界の構築に限定しているため、利用者の視点移動、ウオークスルーの情報をスクリーン間で正しく共有することで、描画における同期を取ります。CS Galleryでは、CAVE1(正面スクリーン用)の計算機から、CAVE2(右面スクリーン用)、CAVE3(床面スクリーン用)の計算機に共有すべきデータを送信する方法を取ります。
CAVE1で、~/sync/にある ssend、CAVE2, CAVE3で ~/sync/ の srecv を実行して下さい。このプログラムは、以下の構造体で指定されているデータをUDPによって、CAVE1からCAVE2, CAVE3へ送信し続けます。この構造体では、視点データでは、センサから送られてくる視点位置、視点角度のデータの他に、ウオークスルーで得られる移動位置、移動方向のデータを含んでいます。

そのため、CAVE1では、ウオークスルー機能で得られる walk[3], turn[3] のデータをこの構造体に書き込み、CAVE2, CAVE3では、この構造体からwalk[3], tuen[3] のデータを読み込みます。

以下は、CAVE1での修正部分を示します。
  struct FOB
  {
  float pos[3];
  float ang[3];

  float walk[3];
  float turn[3];
  int ianim;
  };
  struct FOB *fob_data;

   .....

  void keyboard(unsigned char ckey, int x, int y)
  {
   switch(ckey){
   case 'a':
    walk[1]=walk[1]+0.01*cos(turn[2]/180.0*3.141592);
    walk[0]=walk[0]+0.01*sin(turn[2]/180.0*3.141592);
    fob_data->walk[0]=walk[0];
    fob_data->walk[1]=walk[1];

    break;
   case 'b':
    walk[1]=walk[1]-0.01*cos(turn[2]/180.0*3.141592);
    walk[0]=walk[0]-0.01*sin(turn[2]/180.0*3.141592);
    fob_data->walk[0]=walk[0];
    fob_data->walk[1]=walk[1];

    break;
   case 'c':
    turn[2]=turn[2]+1.0;
    fob_data->turn[2]=turn[2];
    break;
   case 'd':
    turn[2]=turn[2]-1.0;
    fob_data->turn[2]=turn[2];
    break;
   case 'z':
    walk[0]=walk[1]=walk[2]=0.0;
    turn[0]=turn[1]=turn[2]=0.0;
    fob_data->walk[0]=walk[0];
    fob_data->walk[1]=walk[1];

    fob_data->walk[2]=walk[2];
    fob_data->turn[0]=turn[0];
    fob_data->turn[1]=turn[1];
    fob_data->turn[2]=turn[2];
    break;
   }
  }
  

また、以下はCAVE2, CAVE3での修正部分を示します。

  struct FOB
  {
  float pos[3];
  float ang[3];

  float walk[3];
  float turn[3];
  int ianim;
  };
  struct FOB *fob_data;

  .....

  display(void){
  .....
  walk[0]=fob_data->walk[0];
  walk[1]=fob_data->walk[1];
  walk[2]=fob_data->walk[2];
  turn[0]=fob_data->turn[0];
  turn[1]=fob_data->turn[1];
  turn[2]=fob_data->turn[2];

  .....
  }


Contact: Tetsuro Ogi