OpenGL で 3DCG
大江ゼミ3年 中川武憲
Agenda
・OpenGL の基礎知識
・x,y,z 軸の描画
・三角錐の描画
・W,A,S,D で移動
・マウスでカメラの向きを変更
・三角錐の自動回転
・デモ
OpenGL 基礎知識
・ビューイングパイプライン
・モデリング変換
・視野変換
・投射変換
・ビューポート変換
OpenGL 基礎知識
ビューイング・パイプライン
モデリング座標系 …モデルを基準にした座標系
(ローカル座標系)
↓                            ↓ モデリング変換
ワールド座標系 …仮想 3D 空間の基準となる座標系
↓                            ↓ 視野変換
ビュー座標系 …視点を基準とした (カメラから見た) 座標系
↓                            ↓ 投射変換
正規化デバイス座標系 … x, y, z がそれぞれ -1~1 の範囲となる座標系
(クリッピング座標系)
↓                            ↓ ビューポート変換
ウィンドウ座標系 …左上が原点、右下方向が x, y の正の方向となる2次元の座標系
(スクリーン座標系)
OpenGL 実践
x,y,z 軸の描画
GL_LINES で (0, 0, 0) から x,y,z それぞれ長さ 100 の線を引く。
各メソッドの解説
glLineWidth(GLfloat width): 線幅を指定
glColor4d: RGBA で色を指定 (GL_LIGHTING が disable の時のみ有効)
glBegin(GLenum mode): モードを指定して頂点の追加を開始
glVertex3d: XYZ で座標を指定
glEnd(void): glBegin と対になる頂点グループの終わりを示す
x,y,z 軸の描画
glLineWidth(1);
glDisable(GL_LIGHTING);
for (int i = 0; i < 3; i++) {
int length = 100;
glColor4d((i == 0), (i == 1), (i == 2), 1.0);
glBegin(GL_LINES);
glVertex3d(0, 0, 0);
glVertex3d((i == 0) * length, (i == 1) * length, (i == 2) * length);
glEnd();
}
glEnable(GL_LIGHTING);
三角錐の描画
三角錐は全ての面が三角形で構成される四面体である。
頂点は4つ、辺の数は6つである。
単純に考えると、 GL_TRIANGLES で4つの三角形を描けば良いが、同じ頂点を3度指定
する必要があり、合計で12個の頂点を打つことになり無駄である。
そこで、 GL_TRIANGLE_STRIP を使って合計6つの頂点で三角錐を描画した。
各メソッドの解説
glNormal3d: XYZ で法線ベクトルを指定する
三角錐の描画
glBegin(GL_TRIANGLE_STRIP);
glVertex3d(0, 0, 0); glNormal3d(0, 0, -1);
glVertex3d(4, 0, 0); glNormal3d(0, 0, -1);
glVertex3d(0, 0, 3); glNormal3d(0, 0, -1);
glVertex3d(0, 2, 0); glNormal3d(0, 0, -1);
glVertex3d(0, 0, 0); glNormal3d(0, 0, -1);
glVertex3d(4, 0, 0); glNormal3d(0, 0, -1);
glEnd();
コードでは伝わりにくい
図解
xz
y
(0, 2, 0)
(4, 0, 0)(0, 0, 3)
(0, 0, 0)
図解
xz
y
(0, 2, 0)
(4, 0, 0)(0, 0, 3)
(0, 0, 0)
図解
xz
y
(0, 2, 0)
(4, 0, 0)(0, 0, 3)
(0, 0, 0)
図解
xz
y
(0, 2, 0)
(4, 0, 0)(0, 0, 3)
(0, 0, 0)
図解
xz
y
(0, 2, 0)
(4, 0, 0)(0, 0, 3)
(0, 0, 0)
何故それで描画されるのか
三角錐の展開図
三角錐の展開図 (内側→外側)
c
bd
c
d
a
三角錐の展開図 (外側)
c
bd
c
d
a
GL_TRIANGLE_STRIP
W,A,S,D で移動
キーイベントに応じてワールド変換行列を操作する。
W,A,S,D で移動
void keyboard(unsigned char key, int x, int y)
{
printf( "Key Code : %d, Position : %d %dn", key, x, y );
// switch文を用いたキー処理の例(switch-caseを使う場合、breakの書き忘れに注意)
double size = 1.0;
switch(key) {
case 'a':
// x 軸正方向へ移動
wm[12] += size;
break;
case 'd':
// x 軸負方向へ移動
wm[12] -= size;
break;
case 'w':
// z 軸正方向へ移動
wm[14] += size;
break;
case 's':
// z 軸負方向へ移動
wm[14] -= size;
break;
default:
printf( "tKey: Other -> %cn", key );
}
glutPostRedisplay();
}
マウスでカメラの向きを変更
マウスイベントを拾い、ドラッグした大きさに応じてビュー変換行列を操作する。
マウスでカメラの向きを変更
void mousePressed(int button, int state, int x, int y)
{
printf( "Mouse Button: %d, State: %d, Position %d %dn", button, state, x, y );
// mouse down 時
if (! state) {
// 座標を記録しておく
mouse_pos.x = x;
mouse_pos.y = y;
}
}
マウスでカメラの向きを変更
void mouseDragged(int x, int y)
{
printf( "Mouse Drag Position %d %dn", x, y );
pos2d diff = {mouse_pos.x - x, mouse_pos.y - y};
mouse_pos.x = x;
mouse_pos.y = y;
y_rad += diff.x / window.w * M_PI / 5;
x_rad += diff.y / window.h * M_PI / 5;
// y 軸回転
vm_y[0] = cos(y_rad);
vm_y[2] = -sin(y_rad);
vm_y[8] = sin(y_rad);
vm_y[10] = cos(y_rad);
// x 軸回転
vm_x[5] = cos(x_rad);
vm_x[6] = sin(x_rad);
vm_x[9] = -sin(x_rad);
vm_x[10] = cos(x_rad);
// 視点の再計算
resize(window.w, window.h);
glutPostRedisplay(); // ウィンドウに再描画命令を送る(ポストする)
}
三角錐の自動回転
idle 関数内で定期的にワールド変換行列を操作する。
三角錐の自動回転
void idle()
{
// y 軸に対して毎度 1 度ずつ右ねじ回転
rad += M_PI / 180;
if (rad > M_PI * 2) {
rad = 0;
}
wm[0] = cos(rad);
wm[2] = -sin(rad);
wm[8] = sin(rad);
wm[10] = cos(rad);
glutPostRedisplay(); // ウィンドウに再描画命令を送る(ポストする)
}
デモ
http://git.io/NnYb
描画結果
法線ベクトルの指定
がおかしいので光の
反射が変。
参考資料
・OpenGL Documentation
https://www.opengl.org/sdk/docs/man2/xhtml/
・(Mac・iPhone)プリミティブについて - 強火で進め
http://d.hatena.ne.jp/nakamura001/20081231/1230719279
・OpenGL が世界を描画する仕組み - けんごのお屋敷
http://tkengo.github.io/blog/2014/12/27/opengl-es-2-2d-
knowledge-1/
・ビジュアル情報演習
http://www.wakayama-u.ac.jp/~wuhy/08_3DCGwithGLUT.html

OpenGL 3DCG