2019年8月10日土曜日

Processing言語で3Dアニメーション ー 複数の描画パネルを使用する

Processing言語で,ウィンドウ上にこの記事の動画を,それぞれ複数の描画パネルに貼り付けてみました.通常の描画関数は実はPGraphics3Dクラスのメソッドになっているので,明示的にPGraphics3Dクラスのインスタンスを複数作成し,インスタンスを指定してメソッド呼び出しを行うと,複数の描画画面を使用することができます.

なお,PGraphics3Dクラスのインスタンスは実画面に繋がっていないgraphics contextのようで,最後にimage()関数で実ウィンドウにまとめて描画します.

ソースコードは以下のとおりです.重複するコードが多いので,Javaのサブラクスを作成すると,もっと簡潔になると思います.

// 2019/8/10
// 明示的にPGraphicsを用いて描画する
// 2つのPGraphicsを作って,別の描画をする

int loopCount; // draw()が呼ばれるたびにインクリメントされる変数
PGraphics3D pg1, pg2;
int panelWidth, panelHeight;

void setup() {
  size(950, 500, P3D); // 400x400のウィンドウ
  panelWidth = 400;
  panelHeight = 400;
  pg1 = (PGraphics3D)createGraphics(panelWidth, panelHeight, P3D);
  pg2 = (PGraphics3D)createGraphics(panelWidth, panelHeight, P3D);
  loopCount = 0;
  // 日本語フォントが効かない.サイズも反映されない.
  PFont font = createFont("MS Gothic", 48, true);
  pg1.textFont(font);
  pg1.textSize(24);
  pg1.hint(ENABLE_DEPTH_SORT); // z bufferが視点方向で正常に機能するように
  pg2.textFont(font);
  pg2.textSize(24);
  pg2.hint(ENABLE_DEPTH_SORT); // z bufferが視点方向で正常に機能するように
  frameRate(60);
}

void draw() {
  loopCount++;
  //
  pg1.beginDraw();
  pg1.background(192); // light grayの背景
  // ウィンドウの中心が(x, y)の原点になる
  pg1.translate(panelWidth/2, panelHeight/2, 0); // 原点を画面の中身にずらす
  // 座標軸を回転する(Processingは左手系)
  // x:右, y:下, z:手前 -> x:右, y:手前, z:上
  pg1.rotateX(PI/2);
  // この座標系で視点だけ回転させる
  // z軸が上の斜め上方向から原点を眺める
  pg1.camera(300.0*cos(radians(loopCount)),
          300.0*sin(radians(loopCount)),
          300.0,
          0.0, 0.0, 0.0,
          0.0, 0.0, -1.0);

  // 座標軸の描画
  pg1.fill(255, 0, 0); // 赤
  pg1.textAlign(CENTER); // x方向をセンタリング,y方向の座標はベースライン
  pg1.text("x軸", 150, 0, 0); // XY平面上に書く
  pg1.text("y軸", 0, 150, 0); // XY平面上に書く 
  pg1.text("z軸", 0, 0, 150); // Z=150のXY平面上に書く
  pg1.stroke(0, 0, 0); // 黒線
  pg1.line(0, 0, 0, 130, 0, 0); // X軸
  pg1.line(0, 0, 0, 0, 130, 0); // Y軸
  pg1.line(0, 0, 0, 0, 0, 145); // Z軸

  // 3Dオブジェクトの描画
  pg1.fill(0x7F0000FF); // 青の塗り潰し,透明度半分
  pg1.stroke(0, 255, 0); // 緑線
  pg1.box(100, 100, 100); // 原点中心,一辺のサイズ100のの立方体

  pg1.translate(-100, -100, 0); // 原点をずらす
  pg1.stroke(0, 0, 255); // 青線
  pg1.fill(0x7F00FFFF); // シアンの塗り潰し,透明度半分
  pg1.sphere(100); // 原点中心,半径100の球
  pg1.endDraw();
  //
  pg2.beginDraw();
  pg2.background(192); // light grayの背景
  // ウィンドウの中心が(x, y)の原点になる
  pg2.translate(panelWidth/2, panelHeight/2, 0); // 原点を画面の中身にずらす
  // 座標軸を回転する(Processingは左手系)
  // x:右, y:下, z:手前 -> x:右, y:手前, z:上
  pg2.rotateX(PI/2);
  // この座標系で視点だけ回転させる
  // z軸が上の斜め上方向から原点を眺める
  pg2.camera(300.0*cos(radians(-loopCount)),
          300.0*sin(radians(-loopCount)),
          300.0,
          0.0, 0.0, 0.0,
          0.0, 0.0, -1.0);

  // 座標軸の描画
  pg2.fill(255, 0, 0); // 赤
  pg2.textAlign(CENTER); // x方向をセンタリング,y方向の座標はベースライン
  pg2.text("x軸", 150, 0, 0); // XY平面上に書く
  pg2.text("y軸", 0, 150, 0); // XY平面上に書く 
  pg2.text("z軸", 0, 0, 150); // Z=150のXY平面上に書く
  pg2.stroke(0, 0, 0); // 黒線
  pg2.line(0, 0, 0, 130, 0, 0); // X軸
  pg2.line(0, 0, 0, 0, 130, 0); // Y軸
  pg2.line(0, 0, 0, 0, 0, 145); // Z軸

  // 3Dオブジェクトの描画
  pg2.fill(0x7FFF0000); // 赤の塗り潰し,透明度半分
  pg2.stroke(0, 255, 0); // 緑線
  pg2.box(100, 100, 100); // 原点中心,一辺のサイズ100のの立方体

  pg2.translate(-100, -100, 0); // 原点をずらす
  pg2.stroke(255, 0, 0); // 赤線
  pg2.fill(0x7FFF00FF); // マゼンタの塗り潰し,透明度半分
  pg2.sphere(100); // 原点中心,半径100の球
  pg2.endDraw();
  //
  image(pg1, 50, 50); // PGに描いた絵を表示
  image(pg2, 500, 50); // PGに描いた絵を表示
}


出来上がったGIFアニメーションは以下のとおりです.1つのウィンドウに2つの描画領域を作成して,それぞれの領域で別のアニメーションを描画しています.以下の不具合があります.

  • フォントの指定がうまくいかない
  • 半透明描画ができていない.


上記のGIFアニメーションを作成するために書き換えたソースコードは以下のとおりです.

// 2019/8/10
// 明示的にPGraphicsを用いて描画する
// 2つのPGraphicsを作って,別の描画をする

import gifAnimation.*;

GifMaker gifExport;
int loopCount; // draw()が呼ばれるたびにインクリメントされる変数
PGraphics3D pg1, pg2;
int panelWidth, panelHeight;

void setup() {
  size(950, 500, P3D); // 400x400のウィンドウ
  panelWidth = 400;
  panelHeight = 400;
  pg1 = (PGraphics3D)createGraphics(panelWidth, panelHeight, P3D);
  pg2 = (PGraphics3D)createGraphics(panelWidth, panelHeight, P3D);
  loopCount = 0;
  // 日本語フォントが効かない.サイズも反映されない.
  PFont font = createFont("MS Gothic", 48, true);
  pg1.textFont(font);
  pg1.textSize(24);
  pg1.hint(ENABLE_DEPTH_SORT); // z bufferが視点方向で正常に機能するように
  pg2.textFont(font);
  pg2.textSize(24);
  pg2.hint(ENABLE_DEPTH_SORT); // z bufferが視点方向で正常に機能するように
  frameRate(60);
  
  gifExport = new GifMaker(this, "export.gif");
  gifExport.setRepeat(0); // 無限ループ
  gifExport.setQuality(10); // default値
  gifExport.setDelay(30); // 30ms単位で1コマ(1秒33コマ)
  gifExport.setTransparent(0, 0, 0); // 黒は透過色
}

void draw() {
  loopCount++;
  //
  pg1.beginDraw();
  pg1.background(192); // light grayの背景
  // ウィンドウの中心が(x, y)の原点になる
  pg1.translate(panelWidth/2, panelHeight/2, 0); // 原点を画面の中身にずらす
  // 座標軸を回転する(Processingは左手系)
  // x:右, y:下, z:手前 -> x:右, y:手前, z:上
  pg1.rotateX(PI/2);
  // この座標系で視点だけ回転させる
  // z軸が上の斜め上方向から原点を眺める
  pg1.camera(300.0*cos(radians(loopCount)),
          300.0*sin(radians(loopCount)),
          300.0,
          0.0, 0.0, 0.0,
          0.0, 0.0, -1.0);

  // 座標軸の描画
  pg1.fill(255, 0, 0); // 赤
  pg1.textAlign(CENTER); // x方向をセンタリング,y方向の座標はベースライン
  pg1.text("x軸", 150, 0, 0); // XY平面上に書く
  pg1.text("y軸", 0, 150, 0); // XY平面上に書く 
  pg1.text("z軸", 0, 0, 150); // Z=150のXY平面上に書く
  pg1.stroke(0, 0, 0); // 黒線
  pg1.line(0, 0, 0, 130, 0, 0); // X軸
  pg1.line(0, 0, 0, 0, 130, 0); // Y軸
  pg1.line(0, 0, 0, 0, 0, 145); // Z軸

  // 3Dオブジェクトの描画
  pg1.fill(0x7F0000FF); // 青の塗り潰し,透明度半分
  pg1.stroke(0, 255, 0); // 緑線
  pg1.box(100, 100, 100); // 原点中心,一辺のサイズ100のの立方体

  pg1.translate(-100, -100, 0); // 原点をずらす
  pg1.stroke(0, 0, 255); // 青線
  pg1.fill(0x7F00FFFF); // シアンの塗り潰し,透明度半分
  pg1.sphere(100); // 原点中心,半径100の球
  pg1.endDraw();
  //
  pg2.beginDraw();
  pg2.background(192); // light grayの背景
  // ウィンドウの中心が(x, y)の原点になる
  pg2.translate(panelWidth/2, panelHeight/2, 0); // 原点を画面の中身にずらす
  // 座標軸を回転する(Processingは左手系)
  // x:右, y:下, z:手前 -> x:右, y:手前, z:上
  pg2.rotateX(PI/2);
  // この座標系で視点だけ回転させる
  // z軸が上の斜め上方向から原点を眺める
  pg2.camera(300.0*cos(radians(-loopCount)),
          300.0*sin(radians(-loopCount)),
          300.0,
          0.0, 0.0, 0.0,
          0.0, 0.0, -1.0);

  // 座標軸の描画
  pg2.fill(255, 0, 0); // 赤
  pg2.textAlign(CENTER); // x方向をセンタリング,y方向の座標はベースライン
  pg2.text("x軸", 150, 0, 0); // XY平面上に書く
  pg2.text("y軸", 0, 150, 0); // XY平面上に書く 
  pg2.text("z軸", 0, 0, 150); // Z=150のXY平面上に書く
  pg2.stroke(0, 0, 0); // 黒線
  pg2.line(0, 0, 0, 130, 0, 0); // X軸
  pg2.line(0, 0, 0, 0, 130, 0); // Y軸
  pg2.line(0, 0, 0, 0, 0, 145); // Z軸

  // 3Dオブジェクトの描画
  pg2.fill(0x7FFF0000); // 赤の塗り潰し,透明度半分
  pg2.stroke(0, 255, 0); // 緑線
  pg2.box(100, 100, 100); // 原点中心,一辺のサイズ100のの立方体

  pg2.translate(-100, -100, 0); // 原点をずらす
  pg2.stroke(255, 0, 0); // 赤線
  pg2.fill(0x7FFF00FF); // マゼンタの塗り潰し,透明度半分
  pg2.sphere(100); // 原点中心,半径100の球
  pg2.endDraw();
  //
  image(pg1, 50, 50); // PGに描いた絵を表示
  image(pg2, 500, 50); // PGに描いた絵を表示
  
  gifExport.addFrame(); // gifアニメーションに現在のフレームを追加
  if (loopCount >= 360) {
    gifExport.finish(); // 1回転したらアニメーション終了
  }
}


1回転だけした時点で,無限繰り返し再生を行うアニメーション画像を書き出しています.

0 件のコメント:

コメントを投稿