マルチスクリーンアプリケーション
[ Last updated : 2011-03-24 ]
MPI を利用して OpenGL で作成した 3D CG アプリケーションを複数の PC で分 割表示させた際の覚え書きです。 この仕組みを利用して、立体視装置やマルチスクリーンでの星空回転アプリケー ションなどを作成しました。
OpenGL での描画とマルチスクリーン化
OpenGL で 3D CG アプリケーションを作成する際には、 3次元空間へオブジェクトを配置し、 それを指定された視点・視線の方向・視野角で切り出した画像を表示する。 この際、 視点等を変更することによって別の方向から見た画像へと変更できる。 この仕組みを利用して、 各 PC で同じプログラムを実行しながら、 見ている方向を変更することによって マルチスクリーンで表示で来るアプリケーションが作成できる。 またこの際に、 並列計算に利用されている MPI を利用することで、 画像表示の同期を簡単に取ることがきる。
仕組み
複数のマシン間での描画の同期は次の手順で行なう。
画面の再描画に必要な最小限のデータ(例えば、画面の回転角や視点移動) をあらかじめマスターで計算
そのデータを各マシンへ配布
全マシンで同期
各マシンで描画
手順
OpenGL で3D CG アプリケーションを作成
この際に、PC 間での同期を取るために必要な変数をできるだけ少なくしておく。
MPI 関数の導入
main() にMPIの初期化及び終了処理に関する関数群を追加する。
int myid, numprocs;
int main(int argc, char** argv)
{
int namelen;
char processor_name[MPI_MAX_PROCESSOR_NAME];
char winname[256];
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Get_processor_name(processor_name, &namelen);
sprintf(winname,"%s [%d of %d on %s]\n", argv[0], myid, numprocs, processor_name);
glutInit(&argc, argv);
...
MPI_Finalize();
return 0;
}
視点、視線方向、視野角の指定
描画領域は、 サイズ(全プロセス数)とランク(起動したプロセスの番号)を利用して、 各マシンで計算することができる。 例えば y軸の周りに等分割して各ディスプレイで表示する場合には、 横方向の視野角 Δ θ (度) と i 番目のマシンの視線の角度 θi (度) は次のように なる。
Δ θ = 360/(全プロセス数)
θi = i ⋅ Δ θ
int myid, numprocs;
void reshape(int w, int h) /* Window サイズ変更に伴う書き直しと、その際の描画設定 */
{
GLdouble ratio, fovy;
double theta, edist;
GLdouble eye[]={0.0, 0.0, 0.0}, cent[]={0.0, 0.0, 0.0};
fovy = 360.0 / (double) numprocs; /* 横方向の視野角 */
theta = 2.0 * M_PI * (double) myid/(double) numprocs; /* 視線の角度 */
edist = (PERS_FAR - PERS_NEAR) * 0.5; /* 視線方向の点への距離 */
cent[0] = edist * cos(theta); /* 視線方向の点の座標 */
cent[2] = edist * sin(theta);
ratio = (GLdouble)w/(GLdouble)h; /* 画面の縦横比 */
fovy = ytheta0 /ratio; /* y 方向の視野角の設定 */
if (fovy > 180.0) fovy = 180.0;
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fovy, ratio, PERS_NEAR, PERS_FAR); /* 射影変換の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(eye[0], eye[1], eye[2], cent[0], cent[1], cent[2], 0.0, 1.0, 0.0);
/* 視点と視線方向の点の設定 (y 軸を上方)*/
}
MPI による同期
void mpiDisplay(void)
{
if(myid == MASTER)
{
/* 新しい座票 x[3] の計算 */
}
MPI_Bcast(&x, 3, MPI_DOUBLE, 0, MPI_COMM_WORLD); /* MASTER -> others */
MPI_Barrier(MPI_COMM_WORLD); /* マシン間で同期 */
glutPostRedisplay(); /* 画面描画 */
}
プログラム全体は次のようになる
#include "mpi.h"
#include <stdio.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#define MASTER 0
...
int myid, numprocs;
...
void display(void){ /* 画面の描画 */ }
void mpiDisplay(void){ /* 新しい座標の計算、データの送受信、マシン間の同期 */ }
void init(void){ /* 描画の初期設定 (質感、ライト等) */ }
void reshape(int w, int h){ /* 視点、視野角の設定 */ }
void mouse(int button, int state, int x, int y){ /* マウスイベント処理 */ }
int main(int argc, char** argv)
{
/* MPI 初期化 */
...
/* glut 初期化 */
...
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
if(myid == MASTER) glutMouseFunc(mouse); /* マウスは MASTER のみ使用 */
glutIdleFunc(mpiDisplay);
glutMainLoop();
MPI_Finalize();
return 0;
}
サンプルプログラム
OpenGL プログラミングガイドに掲載されている 主星の周りを回る衛星のアニメーションプログラム (planet.c) をもとに、 マウスで回転方向を変更できるの改良版を作成し、 これを2画面用に変更した。 画面を左右に分割するにあたり、 2枚の画面を次のように設定した。
視野角 : θ=60(度)
視点 : (x,y,z)=(0,0,5)
視線方向 : (x, y, z)=(± 5 tan(π θ/360), 0, 0)
ローレンツ方程式にしたがってボールが飛んでいく。 360度マルチスクリーンの中心に座って、カオスが体感できる? 尚、アトラクターの中心から眺めているように座標変換している。
注意点
OpenGLでの回転角度の単位は度(degree)だが、 math library はラジアンなので、混同しないようにする
mpdboot 実行時には返事をした順に PC が番号づけされていくので、 プログラム実行時には次のようにして明示的に指定する。 (ここでは mpd.hosts というファイルに PCのホスト名が書かれている)
$ mpiexec -n 5 -f mpd.hosts ./MPI-planet