絵を表示させる



カーナビゲーションの実現させるためには、地図を表示させるという動作が必要になります。この演習で使用しているのはX Window System (X)と呼ばれるもので、複数のウインドウを開いて、その中に様々な表示をする事のできるシステムです。
この動作をするためのプログラムするための関数群(ライブラリ)が準備されています。これの扱い方を学びます

OpenGLを用いたグラフィック表示

OpenGLは、最近のグラフィック表示(特に3次元描画)を行うための標準的な手法となっています。

OpenGLとは

普通のグラフィック用関数は、実行すれば線を引くだとか、円を描く、四角を描くなどの機能を持った関数です。 一方、OpenGLの場合は、頂点位置の定義をおこない、その頂点を使って、ポリライン(頂点が線でつながれているが、閉じていない、言い換えると線分)、 ポリゴン(頂点が線つながれ、さらに閉じている、言い換えれば多角形、面)、ポリゴンやポリラインを組み合わせたオブジェクトという考え方です。 頂点位置は座標で与えられますが、この座標は3次元でも2次元でも扱うことができます。すなわち、3次元であれば立体、2次元であれば平面の絵を描く事が比較的簡単にできます。

さらに、絵が描かれている空間を様々な方向から見た状態を作り出せます。ある空間を斜め上からみれば立体的な絵に見えますが、 X-Y、Y-Z、Z-X平面にいずれかに垂直な方向から見ればそれは平面の絵にみえます。これらの機能や、見え方(空間に近づくと拡大、離れると縮小・見る位置を移動など)を簡単に制御することができます。

簡単な描画

まず、簡単な描画からやってみます。このサンプルプログラムを表示してtest4.cとして保存してください。

次に、このプログラムをコンパイルするには、今までの cc に比べてたくさんオプションが必要になり、

cc test4.c -g -O2 -Wall -lglfw -lGLU -lGL -lX11 -lXrandr -lm -o test4
としなければなりません。この手間を省くには2つの方法があります。

1つは、alias という短縮ダイアルのような機能を利用する方法です。 まず、ホームディレクトリに移動します。 (login 直後 or, cd コマンドだけで移動できる。)そこに、xcc.shというファイルを生成します。 そして中に以下の内容を記入します。

#! /bin/bash
gcc $1.c -g -O2 -Wall -lglfw -lGLU -lGL -lX11 -lXrandr -lm -o $1

$1はシェル変数と呼ばれ、この部分にコンパイルしたいファイル名が代入されます。 続いて、この.shファイルに権限を与えるため、ターミナルで次のコマンドを打ち込んでください。

chmod +x xcc.sh

そして、同じくホームディレクトリにある.bashrc というファイルに

alias xcc='/home/gakusei2017/xcc.sh'
という内容(行)を追加してください。 これは、別名定義と言って、ccに多数のオプションを付けた状態をxccという名前で使えるようにしています。
これは次回の端末起動から自動的に設定されますが、今回は
source ~/.bashrc
というコマンドを実行して下さい。正しく設定できていれば
xcc test4
だけでコンパイルできるようになります。

これまで実行するファイルはa.outという名前で出力されていましたが、今回の設定でコンパイルしたファイル名と同じ名前で出力されるようになります。 今回の場合はtest4になり、プログラムを実行するためには

./test4
としてください。

もう1つはmake というものを使う方法 です。ここでは説明しませんが、これからプログラムをガンガン行う人は、是非makeの使い方を覚えておいてください。

OpenGLの解説

解説ページへ


グラフを表示してみる

このプログラムをいろいろと改造してみてください。 わかる人はこれを参考にしてみてください。



今日の課題

ボールの運動

以下のプログラムでは、ウィンドウの端でバウンドしながらボールが動きます。 これを参考にして、下端からある角度で放り上げたボールが重力により放物線 を描いて落下し、ウィンドウの各端でバウンドを繰り返すプログラムを作成し てください。 その際、ウィンドウの上端にはぶつからないように初期条件を適切に設定してください。 反発係数は1として構いません。

/* ex5.c  */
/* compile: cc -o ex5 ex5.c -g -O2 -Wall -lglfw -lGLU -lGL -lX11 -lXrandr -lm */

#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <GL/glfw.h>



/* 円を描画する */
void circle(double x, double y, double r) {
  int const N = 24;             /* 円周を 24分割して線分で描画することにする */
  int i;

  glBegin(GL_LINE_LOOP);
  for (i = 0; i < N; i++)
    glVertex2d(x + cos(2 * M_PI * i / N) * r, y + sin(2 * M_PI * i / N) * r);
  glEnd();
}

int main(void) {
  int width = 640, height = 400; /* ウィンドウのサイズ */
  double x = 200.0, y = 200.0;   /* ボールの中心位置 */
  double vx = 5.0, vy = 5.0;     /* ボールの移動速度 */
  double r = 10.0;               /* ボールの半径 */

  /* グラフィック環境を初期化して、ウィンドウを開く */
  glfwInit();
  glfwOpenWindow(width, height, 0, 0, 0, 0, 0, 0, GLFW_WINDOW);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();             /* 投影以外の座標変換は行わない */

  for (;;) {
    /* Esc が押されるかウィンドウが閉じられたらおしまい */
    if (glfwGetKey(GLFW_KEY_ESC) || !glfwGetWindowParam(GLFW_OPENED))
      break;

    glfwGetWindowSize(&width, &height); /* 現在のウィンドウサイズを取得する */
    glViewport(0, 0, width, height); /* ウィンドウ全面をビューポートにする */

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, 0, height, -1.0, 1.0); /* この範囲の空間をビューポートに投影する */

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT); /* バックバッファを黒で塗り潰す */

    /* ウィンドウのサイズが変更されても、ボールがウィンドウ外に出ていかないようにする(fminl 関数は 2 つの引数の小さい方の値を long double 型で返します) */
    x = fminl(x,  width - r);
    y = fminl(y,  height - r);

    /* ボールを移動 */
    x += vx;
    y += vy;

    /* ウィンドウの上下左右の端にぶつかったらバウンド */
    if (x <= r || x >= width - r)
      vx = -vx;
    if (y <= r || y >= height - r)
      vy = -vy;

    glColor3d(1.0, 1.0, 1.0);
    circle(x, y, r);            /* ボールを描く */



    glfwSwapBuffers();          /* フロントバッファとバックバッファを入れ替える */
    usleep(40 * 1000);          /* 40ミリ秒くらい時間待ち */
  }

  glfwTerminate();              /* ウィンドウを閉じる */

  return 0;
}

次に

  1. 配列をつかって5つくらい同時に跳ねさせる、もしくは
  2. 万有引力&ケプラーの法則の検証シミュレーションを行い、 さらに惑星を増やしてみる。

ヒント:(1)は特に工夫の余地なし。(2)では、質量固定で、中心(太陽)の 質量は十分大きいとすると、引力(加速度)は距離の自乗に反比例し、 かつ中心方向を向くことになります。

演習の補足 

放物運動

まず、もとのプログラム(ex5-1.c)で軌道をつくっている部分について考えます。 ここでは x, y, vx, vyという4つの変数が玉の運動を 示しています。ここでx, yは玉の位置、vx, vy はその増分、すなわち速度です。この for 文のループを 1回実行する時間で、玉の位置はベクトル(vx, vy)だけ移動するわけです。 dx, dyはちょっと使われておしまいの変数で、玉の 運動には直接関係がありません。 後半の if 文は、壁にあたったら、対応する速度の正負を変える、 という動作になります。(vx *= -1vx=vx*(-1) を意味します。別の書き方をすれば vx=-vx です)ここで、-1-0.8などにすると跳ね返り係数を0.8規定することになります。

さて、放物運動をさせるにはどうするか、ということですが、 放物運動は物理法則的には一定の加速度が一定の方向に作用した 状態です。地球上で言えば、重力加速度です。上の話によると、 「位置には速度を周期ごとに足す」なのですが、同じ考え方を して「速度には加速度を周期ごとに足す」することが答です。 式でいうと、適切な場所に「vy=vy+1;」などと書くだけで、じつは この課題はおしまいです。

しかしながら、それだけで実行すると最初の位置によっては、 跳ね返る度にだんだん減衰したり、加速したりする減少がみられます。 これは計算の精度が悪いことを意味します。 いろいろな計算方法を検討してみて下さい。

応用課題1:複数のボールを出す 

配列については来週詳しく説明しますが、余力のある人は複数の物体を グラフィックス表示させてみて下さい。

まず、複数のボールに別々の動きをさせるには、それぞれの運動を 示す数値 x, y, vx, vyを その数だけ確保しなければなりません。 そこで 配列を使うわけです。

そうしたら、あとはいままでx, y, vx, vyを使っていた場所を チェックして、x[i] などとして、i0から順に増やすよう、 for文のループをつくればいいわけです。1度作ってしまえば、 (無謀ですが)100個出すことも可能です。この、簡単に数を 増やすことができることこそ、配列の醍醐味なのです。

応用課題2:万有引力 

質量固定で、中心(太陽)の 質量は十分大きいとすると、引力(加速度)は距離の自乗に反比例し、 かつ中心方向を向くことになります。 あとは、上記の放物運動のプログラムを応用すればできると思います。


戻る