CAVE Programming


4. 基本図形の描画

OpenGLを用いた基本的な3次元図形の描画方法について学習します。
前回は3次元モデルを描画するための座標変換の説明を行いました。今回は具体的に3次元モデルをプログラム内でどう記述するかについて学びます。


4.1 描画処理

コンピュータグラフィックスで描画される映像は、最終的にウインドウ内の各画素のRGB値として計算されます。各画素の値を格納するメモリ領域はフレームバッファと呼ばれ、画素ごとにR、G、B、αの値を格納する領域を持っています。
また3次元CGでは、物体の前後関係を正しく表現するため、視点から最も近い面だけを描画する隠面処理を行う必要があります。陰面処理は、画素ごとに視点から対象までの距離(z値)を計算し、z値を用いた判定を行います。各画素ごとにz値を格納するメモリ領域がzバッファです。

zバッファを使った図形の描画を行うためには、以下の各関数が使われます。

・glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH);
ディスプレイの表示モードの設定を行います。フレームバッファの利用方法を規定すると同時にzバッファのためのメモリ領域の確保を行います。

・void glClear(GLbitfield mask);
指定したバッファをクリアして初期化します。引数 mask は GL_COLOR_BUFFER_BIT がフレームバッファ、GL_DEPTH_BUFFER_BIT がzバッファを示し、論理和で指定することができます。

・glEnable(GL_DEPTH_TEST);
描画計算におけるzバッファを有効にします。

・glDisable(GL_DEPTH_TEST);
描画計算におけるzバッファを無効にします。

・glEnable(GL_COLOR_MATERIAL);
ライティング設定時に glColor3f() による色の指定を有効にし、描画するオブジェクトに質感を与えます。

・void glColor3f(GLfloat red, GLfloat green, GLfloat blue);
描画するオブジェクトの色を0.0〜1.0のRGB値で指定します。

・void glFlush(void);
OpenGLの描画コマンドを強制的に実行します。

以下は、sample1のプログラムを例に、zバッファを使用した3次元モデルの描画処理の流れを示したものです。

  void myinit(void)
  {
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_COLOR_MATERIAL);
  }

   void display(void)
  {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  ..........
  座標変換の設定.
  ..........
  /* modeling transform */
  glColor3f(0.0, 1.0, 0.0);
  glutSolidTeapot(1.0);

  glFlush();
  }

まず、zバッファを使用するためには、glutInitDisplayMode の関数の中で、ディスプレイモードとして GLUT_DEPTH を宣言をします。次に myinit() の初期設定を行う関数の中で glEnable(GL_DEPTH_TEST) でzバッファを有効にし、display() 関数の中では、glClear によってフレームバッファ、zバッファを初期化した後、CGモデルの記述を行い、glFrush によって描画コマンドを実行させています。


4.2 ポリゴン

実際に描画を行うモデルの定義は、display 関数内の”/* modeling transform */”の部分で行われます。ここで定義されるモデリング変換はモデルビュー変換行列に対して計算が行われますので、モデルの記述の前に glMatrixMode(GL_MODELVIEW) によってモデルビュー変換行列が指定されている必要があります。 
描画したい3次元モデルは、基本図形を用いて計算機内のワールド座標系に対して記述されます。OpenGLで用意されている基本図形としては、点、線、ポリゴン等があります。ここでは3次元オブジェクトを描画するための最も基本的な方法として、ポリゴンを用いた記述方法について説明します。
ポリゴンとは凸型の多角形で表される基本図形であり、多角形を構成する頂点座標を設定することで定義されます。
以下の手順でsample2のプログラムをダウンロードして下さい。

  ・http://lab.sdm.keio.ac.jp/ogi/vr/sample2.tar をダウンロード
  ・% tar xvf sample2.tar によりファイルを展開

sample2 は sample1を少し複雑にしたプログラムで、実行すると下図のような絵が描画されます。


     図4.1:テーブルの上のティーポット

以下は、sample2のプログラム中のポリゴンの記述部分を示したものです。ここでは1枚のポリゴンによって床面を描画しています。

  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();

glVertex3f(x, y, z) は図形の頂点座標を定義する関数であり、glBegin と glEnd の間でポリゴンを構成する頂点群を列挙します。これらの頂点群に対してどのような図形表現を行うかは glBegin の引数で与えられ、ここでは glBegin(GL_POLYGON) によってポリゴンとしての定義を行っています。
ここで使われている各関数の意味は以下の通りです。

・void glBegin(GLenum mode);
描画するオブジェクトの頂点データの始まりを宣言します。ポリゴンを描画する場合は GL_POLYGON、ワイヤフレームで描画する場合は GL_LINES を指定します。

・void glEnd(void);
描画するオブジェクトの頂点データの終わりを宣言します。

・void glVertex3f(GLfloat x, GLfloat y, GLfloat z);
ポリゴンを構成する頂点座標を定義します。


 
4.3 プリミティブ

OpenGLで用意されている基本図形は、点、線、ポリゴンですが、これらを使用してより複雑な図形要素(プリミティブ)を作ることができます。GLUTでは、立方体、球、円錐、トーラス(ドーナツ)、正4面体、正8面体、正12面体、正20面体、ティーポット等の高次のプリミティブが用意されています。以下は、各プリミティブを描画するための関数です。

・void glutSolidCube(GLdouble size)
1辺の長さ size の立方体を描画します。

・void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
半径 radius、z軸のまわりの分割数(経度) slices、z軸に沿った分割数(緯度) stacks の球を描画します。

・void glutSolidCone(GLdouble radius, GLdouble height, GLint slices, GLint stacks)
底面の半径 radius、高さ height、z軸のまわりの分割数 slices、z軸に沿った分割数 stacks の円錐を描画します。

・void glutSolidTorus(GLdouble innerradius, GLdouble outerradius, GLint sides, GLint rings)
内部半径 innerradius、外部半径 outerradius、断面の円弧への分割数 sides、断面の個数 rings のトーラス(ドーナツ)を描画します。

・void glutSolidTetrahedron(void)
半径 √3 の正4面体を描画します。

・void glutSolidOctahedron(void)
半径が1.0の正8面体を描画します。

・void glutSolidDodecahedron(GLdouble radius)
半径 radius の正12面体を描画します。

・void glutSolidIcosahedron(void)
半径1.0の正20面体を描画します。

・void glutSolidTeapot(GLdouble size)
大きさ size のティーポットを描画します。
サンプルプログラムでは、この関数を使ってティーポットを描いています。


 
4.4 基本図形の定義

モデリングに利用する基本図形としては、OpenGL、GLU、GLUTで用意されているプリミティブだけではなく、繰り返し利用可能な図形要素を利用者が基本図形として定義する方法もあります。
以下は、sample2の中で定義されている円柱を描画する関数です。円柱の基本図形としては、半径 radius、長さ height、円弧の分割数 sides を引数として与えることで、原点を中心としてy軸に沿った円柱を描画します。sample2 では、この円柱の関数を用いて、テーブルの板と脚を描画しています。

  void Cylinder(double radius, double height, int sides)
  {
  int i;
  double step = 3.141592*2.0/(double)sides;
  double t;

  /* upper plane */
  glNormal3d(0.0, 1.0, 0.0);
  glBegin(GL_TRIANGLE_FAN);
  glVertex3d(0.0, height, 0.0);
  for(i=0; i<=sides; i++){
    t = step*(double)i;
    glVertex3d(radius*sin(t), height, radius*cos(t));
    }
  glEnd();

  /* lower plane */
  glNormal3d(0.0, -1.0, 0.0);
  glBegin(GL_TRIANGLE_FAN);
  glVertex3d(0.0, -height, 0.0);
  for(i=0; i<=sides; i++){
    t = step*(double)i;
    glVertex3d(radius*sin(-t), -height, radius*cos(-t));
    }
  glEnd();

  /* side */
  glBegin(GL_QUAD_STRIP);
  for(i=0; i<=sides; i++){
    double t = step*(double)i;
    double x = sin(t);
    double z = cos(t);
    glNormal3d(x, 0.0, z);
    glVertex3d(radius*x, height, radius*z);
    glVertex3d(radius*x, -height, radius*z);
    }
  glEnd();
  }


※練習:
sample2 のプログラムで、ポリゴンやプリミティブのパラメータをいろいろと変えてプログラムを実行し、映像がどう変化するか試してみましょう。