Processingでシーングラフを実現する(2)

ついに、あれを買っちゃいました。これでなんとか花粉症をかわしつつ運動不足を解消する予定です。

さて、今回は前回作成したProcessing向けのシーングラフを使ってもう少し実践的なサンプルを作ってみたいと思います。

シーングラフを使った視点移動

シーングラフを使うことで個別の要素をまとめて移動させたり描画することが簡単にできます。その典型的な利用例に視点(カメラ)の移動があります。ルートとなるノードの位置や角度を変更するだけで、カメラの移動に相当する操作が実現できます。

視点移動のサンプル

では、早速視点移動のサンプルを作成してみます。ルートノードになる床と、その上で跳ねるボールのクラスを用意して、床を回転させることにより視点を動かします。

以下、シーングラフ部分も含めたコード全文です。

// シーングラフ用ノードの定義
class Node {
  PVector pos = new PVector();
  PVector rot = new PVector();
  PVector scl = new PVector(1.0f, 1.0f, 1.0f);
  ArrayList<Node> childNodes = new ArrayList<Node>();

  void draw() {}

  void doDraw() {
    pushMatrix();

    translate(pos.x, pos.y, pos.z);
    rotateX(rot.x);
    rotateY(rot.y);
    rotateZ(rot.z);
    scale(scl.x, scl.y, scl.z);

    for (Node node: childNodes) {
      node.doDraw();
    }

    draw();

    popMatrix();
  }
};

// 定数の定義
final float FLOOR_WIDTH = 300.0f;
final float FLOOR_HEIGHT = 5.0f;
final float BALL_RADIUS = 10.0f;
final float BORDER_POS = FLOOR_WIDTH / 2.0f - BALL_RADIUS;
final int BALL_NUM = 15;

// 床ノードの定義
class Floor extends Node {
  void draw() {
    noStroke();
    fill(0, 255, 0);
    translate(0.0f, FLOOR_HEIGHT, 0.0f);
    box(FLOOR_WIDTH, FLOOR_HEIGHT, FLOOR_WIDTH);
  }
}

// ボールノードの定義
class Ball extends Node {
  PVector speed = new PVector();

  Ball() {
    pos.x = random(-BORDER_POS, BORDER_POS);
    pos.y = random(-BORDER_POS, -BALL_RADIUS);
    pos.z = random(-BORDER_POS, BORDER_POS);

    speed.x = random(-2.0f, 2.0f);
    speed.y = random(-3.0f, 3.0f);
    speed.z = random(-2.0f, 2.0f);
  }

  void draw() {
    if (abs(pos.x) > BORDER_POS) { speed.x = -speed.x; }
    if (pos.y > -BALL_RADIUS) { speed.y = max(-speed.y, -6.0f); }
    if (abs(pos.z) > BORDER_POS) { speed.z = -speed.z; }

    pos.add(speed);
    speed.y += 0.2f;

    noStroke();
    fill(255);
    sphereDetail(8, 8);
    sphere(BALL_RADIUS);
  }
}

// シーンの初期化
Node floor = new Floor();

void setup() {
  size(300, 300, OPENGL);

  for (int i = 0; i < BALL_NUM; i++) {
    Ball ball = new Ball();
    floor.childNodes.add(ball);
  }

  floor.pos.set(width / 2.0f, height / 2.0f, 0.0f);
}

// シーンの描画と視点移動
void draw() {
  floor.rot.x = sin((float)frameCount / 80.0f) * 0.7f - 0.7f;
  floor.rot.y += 0.005f;

  background(0);
  lights();
  floor.doDraw();
}

動作確認

作成したサンプルを実際に動かしてみます。

視点が移動しながら床とボールが描画されています。

終わりに

今回シーングラフを作成してみて改めて気づきましたが、Processing.jsの3D描画は遅いですね…。ブラウザ上で気軽に3Dをやりたいのであれば、Three.jsを簡単に使えるように工夫する方が前向きかもしれません。