Processingでビルボードを描画する

久々の休みです。ここのところイベントづくしだったので、ちょっと放心状態です。

気を取り直して、今日はProcessingの小技の紹介です。標準では提供されていないビルボードの描画を行ってみます。

ビルボードとは

ビルボード(Billboard)とは、2Dの絵を3Dオブジェクトのように見せかける描画手法です。下の画像の草のように、三次元の座標上に大きさだけ合わせた二次元の絵をカメラ方向に向けて描画することで、平面の絵を立体的に錯覚させます。

f:id:tkitao:20141221161534j:plain

ビルボードは形状が複雑な植物や爆発エフェクトなどでよく利用されます。

ビルボードの実現方法

Processingでビルボードを実現するには、描画用の変換行列を取得し、位置とサイズを維持したまま、回転だけさせないように修正します。

  // 表示位置に平行移動
  translate(x, y, z);

  // 現時点の変換行列を取得
  PMatrix3D mat = (PMatrix3D)getMatrix();

  // 回転成分だけ無効にする
  mat.m00 = mat.m11 = mat.m22 = 1.0f;
  mat.m01 = mat.m02 = mat.m10 = mat.m12 = mat.m20 = mat.m21 = 0.0f;

  // 作成した変換行列を使って描画
  resetMatrix();
  applyMatrix(mat);

ちなみにProcessing.jsでは、残念ながらgetMatrix関数は使用できないため、この処理はブラウザ上で動作させることはできないのでご注意ください。

サンプルコード

今回は、次のようなパーティクル(細かい粒子)の画像をたくさん描画して、キラキラ光る画面を作ってみます。

f:id:tkitao:20141221143857p:plain

コード全文は以下のとおりです。billboard関数の部分だけコピーすれば、好きなスケッチで利用できます。

float cameraAngle = 0;
PImage particleImage;

class Particle {
  float x, y, z;

  Particle(float x, float y, float z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
}

Particle[] particles = new Particle[100];

void setup() {
  size(600, 400, OPENGL);

  particleImage = loadImage("http://cdn-ak.f.st-hatena.com/images/fotolife/t/tkitao/20141221/20141221143857.png");

  for (int i = 0; i < particles.length; i++) {
    particles[i] = new Particle(random(-500, 500), random(-500, 500), random(-500, 500));
  }
}

void draw() {
  background(0);

  camera(0, 0, -700, 0, 0, 0, 0, -1, 0);
  rotateY(cameraAngle);
  rotateZ(cameraAngle * 0.5f);
  cameraAngle += 0.01f;

  noStroke();

  blendMode(ADD);
  hint(DISABLE_DEPTH_TEST);

  for (Particle particle : particles) {
    billboard(particleImage, particle.x, particle.y, particle.z, 300, 300, 0);
  }
}

void billboard(PImage img, float x, float y, float z, float w, float h, float ang) {
  pushMatrix();

  translate(x, y, z);

  PMatrix3D mat = (PMatrix3D)getMatrix();
  mat.m00 = mat.m11 = mat.m22 = 1.0f;
  mat.m01 = mat.m02 = mat.m10 = mat.m12 = mat.m20 = mat.m21 = 0.0f;

  resetMatrix();
  applyMatrix(mat);

  if (ang != 0.0f) {
    rotateZ(ang);
  }

  float hW = w * 0.5f;
  float hH = h * 0.5f;

  beginShape(QUADS);
  texture(img);
  vertex(-hW, -hH, 0.0f, 0.0f, 0.0f);
  vertex(-hW, hH, 0.0f, 0.0f, img.height);
  vertex(hW, hH, 0.0f, img.width, img.height);
  vertex(hW, -hH, 0.0f, img.width, 0.0f);
  endShape();

  popMatrix();
}

ビルボードの処理とは直接関係ありませんが、光の表現を実現するため、blendMode(ADD)hint(DISABLE_DEPTH_TEST)に設定して、画像が重なり合った際に色が明るくなるようにしています。(ちなみにblendMode関数もProcessing.jsでは使用できません)

動作画面

実際の動作画面です。

f:id:tkitao:20141221153615g:plain

今回のサンプルはブラウザ上で直接動かすことができないので、Processing IDE上で動かした画面をGIFアニメにして貼り付けています。

終わりに

ビルボードをProcessingで実現してみましたが、ちょっと凝った処理をし始めるとProcessing.jsで動かせなくなるのがつらいですね。おまけに、久々に記事を更新したら筆の進む速度がものすごく遅くなっていたことにも驚きました。継続は大事ですね。