VisualC++ と OpenGL を利用した仮想物理実験室
波動方程式のテスト
今後、マックスウェル方程式とシュレディンガー方程式のアニメーション化の準備のため、
簡単な波動方程式のテストソースをつくってみました。
参考ページ
■VisualC++ と OpenGL を利用した仮想物理実験室
仮想物理実験室の構築 (ver1.0)
■OpenGL(Akita National College of Technology Yamamoto's Laboratory )
VisualC++ + OpenGL のソース
//////////////////////////////////////////////////////////////////////////
// 波動方程式のテスト
//////////////////////////////////////////////////////////////////////////
#include <math.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <direct.h>
#include <time.h>
#include <GL/glut.h>
#include <GL/gl_material.h>
#include <GL/gl_screenshot.h>
using namespace std;
double PI = acos(-1.0);
//////////////////////////////////////////////////////////////////////////
// 変数の定義
//////////////////////////////////////////////////////////////////////////
#define _BITMAP 0//アニメーション作成用ビットマップの保存 0:しない 1:する
//ofstream file1( "ball1.data" );
//--------------------------------------------------------
// 仮想物理実験室変数の定義
//--------------------------------------------------------
double t = 0.0; //時刻
double dt= 0.1; //時間刻み
int tn = 0; //ステップ数
int skipBMP = 1; //BMP書出し回数の間引き数
int skipCal = 0; //描画回数の間引き
int skipDat = 1000;//データ書出し回数の間引き回数
const int N=51; //系の長さ
const double c=1.0; //波の速度
const double dd=2.0 ; //系の刻み幅
double z[3][N][N];
void SetUp(void){
}
void calculate();
//--------------------------------------------------------
// OpenGL用変数定義
//--------------------------------------------------------
//////////////////////////////////////////
// ウィンドウ生成用
int WindowPositionX = 200; //生成するウィンドウ位置のX座標
int WindowPositionY = 200; //生成するウィンドウ位置のY座標
int WindowWidth = 512; //生成するウィンドウの幅
int WindowHeight = 512; //生成するウィンドウの高さ
char WindowTitle[] = "仮想物理実験室(ver 1.2)"; //ウィンドウのタイトル
//////////////////////////////////////////
// アニメーション用 ビットマップ保存
gl_screenshot gs; //「gl_screenshot」クラスのインスタンス「gs」を宣言
static GLfloat LightPosition[4] = { 0, 0, 70, 1 }; //光源の位置
//////////////////////////////////////////
// マウスドラッグ用
int cx, cy; // ドラッグ開始位置
double sx, sy; // マウスの絶対位置→ウィンドウ内での相対位置の換算係数
double cq[4] = { 1.0, 0.0, 0.0, 0.0 }; // 回転の初期値 (クォータニオン)
double tq[4]; // ドラッグ中の回転 (クォータニオン)
double rt[16]; // 回転の変換行列
unsigned int listNumber;
float camera_z_pos =200.0;
// 文字描画 --------------------------------------------------------
int text_list;
char t_char[10];
char t_char2[10];
//string t_string;
void DRAW_STRING(int x, int y, char *string, void *font = GLUT_BITMAP_TIMES_ROMAN_24);
void DISPLAY_TEXT(int x, int y, char *string);
//--------------------------------------------------------
// 回転用
//--------------------------------------------------------
void qmul(double r[], const double p[], const double q[]);
void qrot(double r[], double q[]);
//--------------------------------------------------------
// 関数のプロトタイプ
//--------------------------------------------------------
//////////////////////////////////////////
// メイン関数用
void Initialize(void);
void Display(void);
void Resize(int w, int h);
void Idle(void);
void Keyboard(unsigned char key, int x, int y);
void mouse_motion(int x, int y);
void mouse_on(int button, int state, int x, int y);
void mouse_wheel(float);
// モードの選択 --------------------------------------------------------
int _Surface = 1; //表面の表示
int _Line = 0; //ラインの表示
//// 「_Surface」「_Line」はどちらかが「1」にする
int _Bitmap = 0; // ビットマップ保存(gifアニメーション作成用)
// 表面の描写用 --------------------------------------------------------
GLfloat ctlpoints[N][N][3];
GLfloat knots[2*N+8] = {0.0}; //ノット数 = 制御点数 + 次数 + 1(今回は3次) N=51 -> 110
GLUnurbsObj *theNurb;
//--------------------------------------------------------
// メイン関数
//--------------------------------------------------------
int main(int argc, char *argv[]){
SetUp();
srand((unsigned)time(NULL));
#if _BITMAP
_mkdir("bitmap"); //bmpファイル保存用のフォルダの作成
#endif
glutInit(&argc, argv); //環境の初期化
glutInitWindowPosition(WindowPositionX, WindowPositionY); //ウィンドウの位置の指定
glutInitWindowSize(WindowWidth, WindowHeight); //ウィンドウサイズの指定
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); //ディスプレイモードの指定
glutCreateWindow(WindowTitle); //ウィンドウの作成
glutDisplayFunc(Display); //描画時に呼び出される関数を指定する(関数名:Display)
glutReshapeFunc(Resize); //リサイズにより呼び出される関数
glutMouseFunc(mouse_on); //マウスクリック時に呼び出される関数
glutMotionFunc(mouse_motion); //マウスドラッグ解除時に呼び出される関数
glutKeyboardFunc(Keyboard); //キーボード入力時に呼び出される関数を指定する(関数名:Keyboard)
glutIdleFunc(Idle); //プログラムアイドル状態時に呼び出される関数
Initialize(); //初期設定の関数を呼び出す
glutMainLoop();
return 0;
}
//--------------------------------------------------------
// 初期設定の関数
//--------------------------------------------------------
void Initialize(void){
glClearColor(0.0, 0.0, 0.0, 1.0); //背景色
glEnable( GL_DEPTH_TEST ); //デプスバッファを使用:glutInitDisplayMode() で GLUT_DEPTH を指定する
glDepthFunc( GL_LEQUAL );
glClearDepth( 1.0 );
//// デプスバッファとは:描画対象の物体の座標を考慮して、ピクセルあたりの描画内容を決める
theNurb = gluNewNurbsRenderer();
gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, 25.0);
gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
for(int i=0; i<2*N+8 ; i++){
knots[i] = float(i); //ノットの定義
}
// マウスポインタ位置のウィンドウ内の相対的位置への換算用
sx = 1.0 / (double)WindowWidth;
sy = 1.0 / (double)WindowHeight;
// 回転行列の初期化
qrot(rt, cq);
//初期値---------------------------------------------
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
if(i>20 && i<30 && j>20 && j<30) {
z[0][i][j]=20.0;
}
else {
z[0][i][j]=0.0;
}
}
}
//t=dt*1---------------------------------------------
for(int i=1;i<N-1;i++) {
for(int j=1;j<N-1;j++) {
z[1][i][j]=z[0][i][j]+c*c/2.0*dt*dt/(dd*dd)*(z[0][i+1][j]+z[0][i-1][j]+z[0][i][j+1]+z[0][i][j-1]-4.0*z[0][i][j]);
}
}
for(int i=0;i<N;i++) {
z[1][i][0]=z[1][i][N-1]=z[1][0][i]=z[1][N-1][i]=0.0;//境界条件
}
//////////////////////////////////////////
//透視変換行列の設定
glMatrixMode(GL_PROJECTION);//行列モードの設定(GL_PROJECTION : 透視変換行列の設定、GL_MODELVIEW:モデルビュー変換行列)
glLoadIdentity();//行列の初期化
gluPerspective(30.0, (double)WindowWidth/(double)WindowHeight, 0.1, 1000.0); //透視投影法の視体積gluPerspactive(th, w/h, near, far);
}
//--------------------------------------------------------
// 描画の関数
//--------------------------------------------------------
void Display(void) {
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//////////////////////////////////////////
//モデルビュー変換行列の設定
glMatrixMode(GL_MODELVIEW);//行列モードの設定(GL_PROJECTION : 透視変換行列の設定、GL_MODELVIEW:モデルビュー変換行列)
glLoadIdentity();//行列の初期化
glViewport(0, 0, WindowWidth, WindowHeight);
//////////////////////////////////////////
//視点の設定
gluLookAt(
0.0, -90.0, 60.0, // 視点の位置x,y,z;
0.0, 0.0, 0.0, // 視界の中心位置の参照点座標x,y,z
0.0, 1.0, 0.0); //視界の上方向のベクトルx,y,z
//////////////////////////////////////////
//ステンシルバッファクリア値の設定
glClearStencil( 0 );
glCullFace( GL_BACK );
glEnable( GL_CULL_FACE );
glEnable( GL_AUTO_NORMAL );
glEnable( GL_NORMALIZE );
//////////////////////////////////////////
// 回転
glMultMatrixd(rt);
//////////////////////////////////////////
// 光源ON
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glLightfv( GL_LIGHT0,GL_POSITION,LightPosition );
//////////////////////////////////////////
// 描画
glPushMatrix();//階層化
calculate();
//zahyou();
glPopMatrix();
//////////////////////////////////////////
//文字の描画
glColor3d(0.0, 0.0, 0.0);
strcpy_s(t_char2, "t = ");
sprintf_s(t_char, "%5.2f", t);
strcat_s(t_char2, t_char);
DISPLAY_TEXT(5, 95, t_char2 );
//////////////////////////////////////////
//陰影OFF
glDisable(GL_AUTO_NORMAL);
glDisable(GL_NORMALIZE);
glDisable(GL_LIGHTING);
//////////////////////////////////////////
//ビットマップの保存
#if _BITMAP
if(tn%skipBMP == skipCal){
ostringstream fname;
int tt = tn/skipBMP +10000;
fname << "bitmap/" << tt << ".bmp" ;//出力ファイル名
string name = fname.str();
gs.screenshot(name.c_str(), 24);
cout << tn << " " << t << endl;
}
#endif
glutSwapBuffers(); //glutInitDisplayMode(GLUT_DOUBLE)でダブルバッファリングを利用可
}
//////////////////////////////////////////////////////////////////////////
// 計算+描画
//////////////////////////////////////////////////////////////////////////
void calculate(){
for(int i=1;i<N-1;i++) {
for(int j=1;j<N-1;j++) {
z[2][i][j]=2.0*z[1][i][j]-z[0][i][j]+c*c*dt*dt/(dd*dd)*(z[1][i+1][j]+z[1][i-1][j]+z[1][i][j+1]+z[1][i][j-1]-4.0*z[1][i][j]);
}
}
for(int i=0;i<N;i++) {
z[2][i][0]=z[2][i][N-1]=z[2][0][i]=z[2][N-1][i]=0.0; //境界条件
}
/*次の計算のために配列の数値を入れかえる。ここで過去の情報は失われる。*/
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
z[0][i][j]=z[1][i][j];
z[1][i][j]=z[2][i][j];
}
}
//// 描画 --------------------------------------------------------
for(int i=0;i<N-1;i++){
for(int j=0;j<N-1;j++){
if(_Surface){
ctlpoints[i][j][0] = float(i-(N-1)/2);
ctlpoints[i][j][1] = float(j-(N-1)/2);
ctlpoints[i][j][2] = z[2][i][j];
}
if(_Line){
glDisable(GL_LIGHTING);
glColor3d(1.0,1.0,1.0);
//glColor3d(abs(ez[i][j]), abs(ez[i][j]), abs(ez[i][j]));
glBegin(GL_LINES);
glVertex3d(i,j,z[2][i][j]);
glVertex3d(i,j+1,z[2][i][j+1]);
glEnd();
glBegin(GL_LINES);
glVertex3d(i,j,z[2][i][j]);
glVertex3d(i+1,j,z[2][i+1][j]);
glEnd();
glEnable(GL_LIGHTING);
}
}
}
if(_Surface){
// 表面表示時の効果 ----------------------------------------------------------
glMaterialfv(GL_FRONT, GL_AMBIENT, ms_ruby.ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, ms_ruby.diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, ms_ruby.specular);
glMaterialfv(GL_FRONT, GL_SHININESS, &ms_ruby.shininess);
gluBeginSurface(theNurb);
gluNurbsSurface(theNurb,
N+3, knots,
N+3, knots,
N * 3,
3,
&ctlpoints[0][0][0],
4, 4,
GL_MAP2_VERTEX_3);
gluEndSurface(theNurb);
}
tn++;
t = double(tn) * dt;
}
//--------------------------------------------------------
// アイドル時に呼び出される関数
//--------------------------------------------------------
void Idle(){
glutPostRedisplay(); //glutDisplayFunc()を1回実行する
}
//--------------------------------------------------------
// サイズ変更
//--------------------------------------------------------
void Resize(int x, int y){
WindowWidth = x;
WindowHeight = y;
glMatrixMode(GL_MODELVIEW);//行列モードの設定(GL_PROJECTION : 透視変換行列の設定、GL_MODELVIEW:モデルビュー変換行列)
glViewport(0, 0, WindowWidth, WindowHeight);
glMatrixMode(GL_PROJECTION); //行列モードの設定(GL_PROJECTION : 射影行列)
glLoadIdentity();//行列の初期化
gluPerspective(30.0, (double)WindowWidth/(double)WindowHeight, 0.1, 1000.0); //透視投影法の視体積gluPerspactive(th, w/h, near, far);
}
//--------------------------------------------------------
// 文字描画
//--------------------------------------------------------
void DISPLAY_TEXT(int x, int y, char *string){
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glPushAttrib(GL_ENABLE_BIT);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, 100, 0, 100);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glColor3f(1.0, 1.0, 1.0);
glCallList(text_list);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
glMatrixMode(GL_MODELVIEW);
text_list=glGenLists(1);
glNewList(text_list,GL_COMPILE);
DRAW_STRING(x, y, string , GLUT_BITMAP_TIMES_ROMAN_24);
glEndList();
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
}
void DRAW_STRING(int x, int y, char *string, void *font){
int len, i;
glRasterPos2f(x, y);
len = (int) strlen(string);
for (i = 0; i < len; i++){
glutBitmapCharacter(font, string[i]);
}
}
//--------------------------------------------------------
// マウスドラッグによる回転
//--------------------------------------------------------
//////////////////////////////////////////////
// クォータニオンの積 r <- p x q
static void qmul(double r[], const double p[], const double q[])
{
r[0] = p[0] * q[0] - p[1] * q[1] - p[2] * q[2] - p[3] * q[3];
r[1] = p[0] * q[1] + p[1] * q[0] + p[2] * q[3] - p[3] * q[2];
r[2] = p[0] * q[2] - p[1] * q[3] + p[2] * q[0] + p[3] * q[1];
r[3] = p[0] * q[3] + p[1] * q[2] - p[2] * q[1] + p[3] * q[0];
}
/////////////////////////////////////////////
// 回転の変換行列 r <- クォータニオン q
static void qrot(double r[], double q[]){
double x2 = q[1] * q[1] * 2.0;
double y2 = q[2] * q[2] * 2.0;
double z2 = q[3] * q[3] * 2.0;
double xy = q[1] * q[2] * 2.0;
double yz = q[2] * q[3] * 2.0;
double zx = q[3] * q[1] * 2.0;
double xw = q[1] * q[0] * 2.0;
double yw = q[2] * q[0] * 2.0;
double zw = q[3] * q[0] * 2.0;
r[ 0] = 1.0 - y2 - z2;
r[ 1] = xy + zw;
r[ 2] = zx - yw;
r[ 4] = xy - zw;
r[ 5] = 1.0 - z2 - x2;
r[ 6] = yz + xw;
r[ 8] = zx + yw;
r[ 9] = yz - xw;
r[10] = 1.0 - x2 - y2;
r[ 3] = r[ 7] = r[11] = r[12] = r[13] = r[14] = 0.0;
r[15] = 1.0;
}
//--------------------------------------------------------
// キーボード入力時に呼び出される関数
//--------------------------------------------------------
void Keyboard(unsigned char key, int x, int y){
switch ( key )
{
case 'i':
camera_z_pos -= 10.0;
break;
case 'o':
camera_z_pos += 10.0;
break;
default:
break;
}
cout << camera_z_pos << endl;
}
//--------------------------------------------------------
// マウスドラッグ時
//--------------------------------------------------------
void mouse_motion(int x, int y){
double dx, dy, a;
// マウスポインタの位置のドラッグ開始位置からの変位
dx = (x - cx) * sx;
dy = (y - cy) * sy;
// マウスポインタの位置のドラッグ開始位置からの距離
a = sqrt(dx * dx + dy * dy);
if( a != 0.0 )
{
// マウスのドラッグに伴う回転のクォータニオン dq を求める
double ar = a * 2.0 * PI * 0.5;
double as = sin(ar) / a;
double dq[4] = { cos(ar), dy * as, dx * as, 0.0 };
// 回転の初期値 cq に dq を掛けて回転を合成
qmul(tq, dq, cq);
// クォータニオンから回転の変換行列を求める
qrot(rt, tq);
}
}
//--------------------------------------------------------
// マウスクリック時
//--------------------------------------------------------
void mouse_on(int button, int state, int x, int y){
switch (button) {
case 0:
switch (state) {
case 0:
// ドラッグ開始点を記録
cx = x;
cy = y;
break;
case 1:
// 回転の保存
cq[0] = tq[0];
cq[1] = tq[1];
cq[2] = tq[2];
cq[3] = tq[3];
break;
default:
break;
}
break;
default:
break;
}
cout << x << " " << y<<endl;
}



