PGraphicsをテクスチャとして使用する

12月 7th, 2011

Processing Advent Calendar 2011 (7日目)

PGraphicsをテクスチャとして使ってみよう


さらに引き続き、PGraphicsの記事を書きます。今回はPGraphicsをテクスチャとして使用する方法です。
PGraphicsをテクスチャとして使うことで、それを3Dのオブジェクトに張り付けたり、テクスチャの内容を動的に変えていくことができます。

ポリゴンにPGraphicsを貼る

まずは、単純なポリゴンに、テクスチャとしてPGraphicsを貼ってみます。
beginShape()からendShape()間でvertex()を利用することで、ポリゴンを描くことができます。
さらに、texture()も利用することで、ポリゴンにテクスチャを載せることができます。texutre()(Procesing公式リファレンス)
ここでtexture()にPImageではなく、PGraphicsを指定します。

四角形のポリゴンを描画して、そこにテクスチャとしてPGraphicsを貼りつけました。

なお、PGraphicsは以下の様になっています。

PGraphics pg;
final int RECT_SIZE = 20;  //  テクスチャで描画する四角形の個々の大きさ
void setup(){
  size(400, 400, P2D);
  background(0);
  pg = createGraphics(400, 400, P2D);
 
  pg.beginDraw();
  pg.background(0);
  for(int y = 0; y < pg.height; y += RECT_SIZE){
    for(int x = 0; x < pg.width; x += RECT_SIZE){
      pg.fill((float)x / pg.width * 255, (float)y / pg.height * 255, 0);
      pg.rect(x, y, RECT_SIZE, RECT_SIZE);
    }
  }
  pg.endDraw();
 
  beginShape();
  texture(pg);
  vertex(100, 100, 0, 0);
  vertex(300, 100, pg.width, 0);
  vertex(300, 300, pg.width, pg.height);
  vertex(100, 300, 0, pg.height);
  endShape(); 
}

このように、テクスチャには、Processingで動的に生成したPGraphicsを指定することもできるのです。

サイコロを作成する

先の技術を用いて、サイコロを作成しました。
まずPGraphicsに各目の画像を作成し、それをテクスチャとして立方体に貼りつけています。

アプレットはこちら


テクスチャは下記の様になっています。

PGraphics pg;
float rot = 0;  //  回転角度
 
final int EYE_SIZE_BLACK = 20;    //  サイコロの目の大きさ(黒丸)
final int EYE_SIZE_RED = 30;      //  サイコロの目の大きさ(赤丸)
final int DICE_EDGE_SIZE = 100;   //  サイコロテクスチャの辺の長さ
 
void setup() {
  size(400, 400, P3D);
  hint(ENABLE_DEPTH_SORT);
 
  pg = createDiceTexture();
}
 
void draw() {
  background(0, 0, 255);
 
  camera(0, 0, 500, 0, 0, 0, 0, 1, 0);
  rotateY(radians(rot));
  rotateX(radians(rot / 2));
  rot++;
 
  dice(100);
}
 
//  サイコロのテクスチャを作成する
PGraphics createDiceTexture(){
  PGraphics graphic;
  graphic = createGraphics(DICE_EDGE_SIZE * 6, DICE_EDGE_SIZE, P2D);
  graphic.smooth();
 
  //  さいころの各面を作る
  graphic.beginDraw();
  graphic.background(255);
  graphic.noStroke();
 
  //  1の面
  graphic.fill(255, 0, 0);
  graphic.ellipse(50, 50, EYE_SIZE_RED, EYE_SIZE_RED);
 
  //  2の面
  graphic.fill(0);
  graphic.ellipse(175, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(125, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
 
  //  3の面
  graphic.ellipse(275, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(250, 50, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(225, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
 
  //  4の面
  graphic.ellipse(325, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(375, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(325, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(375, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
 
  //  5の面
  graphic.ellipse(425, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(475, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(450, 50, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(425, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(475, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
 
  //  6の面
  graphic.ellipse(530, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(570, 25, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(530, 50, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(570, 50, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(530, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
  graphic.ellipse(570, 75, EYE_SIZE_BLACK, EYE_SIZE_BLACK);
 
  graphic.endDraw();
 
  return graphic;
}
 
 
//  サイコロの描画
void dice(float size) {
  float halfSize = size / 2;
 
  //  正面(1)
  beginShape(QUAD);
  texture(pg);
  vertex(-halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 0, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 0);
  vertex( halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 1);
  vertex(-halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 0, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  右側面(2)
  beginShape(QUAD);
  texture(pg);
  vertex( halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 0);
  vertex( halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 1);
  vertex( halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  背側面(6)
  beginShape(QUAD);
  texture(pg);
  vertex( halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 0);
  vertex(-halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 6, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 6, DICE_EDGE_SIZE * 1);
  vertex( halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  左側面(5)
  beginShape(QUAD);
  texture(pg);
  vertex(-halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 0);
  vertex(-halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 1);
  vertex(-halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  上面(3)
  beginShape(QUAD);
  texture(pg);
  vertex(-halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 1);
  vertex(-halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 1);
  endShape(CLOSE); 
 
  //  底面(4)
  beginShape(QUAD);
  texture(pg);
  vertex( halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 1);
  vertex( halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
}

setup()内のhint(ENABLE_DEPTH_SORT)が重要です。
どうも、PGraphicsのテクスチャを利用すると、Processingはポリゴンの前後関係を理解してくれないようなので、本来隠れて見えないはずのポリゴンが見えてしまったりします。そこで「ちゃんと前後関係を考慮して描画してね」というように明確にお願いをするのがhint(ENABLE_DEPTH_SORT)なのです。

今回はわかりやすさを重視したので、6つ面をそれぞれbeginShape(QUAD)で描いています。
しかし、恐らくbeginShape(TRIANGLE_STRIP)を使って1回で描いた方が描画速度の効率が良いと思います。(どうやって一回で描くかの順序を考える必要がありますが)

PGraphicsを更新し、動的にテクスチャを変化させる

PGraphicsを変化させれば、テクスチャの内容も当然、それにあわせて変わります。
今回は、サイコロの目をその面の中で適当に動かすようにしてみました。

アプレットはこちら

PGraphics pg;
float rot = 0;  //  回転角度
 
final int EYE_SIZE_BLACK = 20;    //  サイコロの目の大きさ(黒丸)
final int EYE_SIZE_RED = 30;      //  サイコロの目の大きさ(赤丸)
final int DICE_EDGE_SIZE = 100;   //  サイコロテクスチャの辺の長さ
 
DiceEye[] diceEye = new DiceEye[1 + 2 + 3 + 4 + 5 + 6];  //  動きまわるサイコロの目ボール
 
//  サイコロの目ボールのデータ{面番号, 初期X座標, 初期Y座標}
final int[][] DICE_EYE_DATA = {{0,  50, 50},
                               {1, 175, 25}, {1, 125, 75},
                               {2, 275, 50}, {2, 250, 50}, {2, 225, 75},
                               {3, 325, 75}, {3, 375, 25}, {3, 325, 75}, {3, 375, 75},
                               {4, 425, 50}, {4, 475, 50}, {4, 450, 50}, {4, 425, 50}, {4, 475, 50},
                               {5, 530, 25}, {5, 570, 25}, {5, 530, 50}, {5, 570, 50}, {5, 530, 75}, {5, 570, 75}
                           };
 
void setup() {
  size(400, 400, P3D);
  hint(ENABLE_DEPTH_SORT);
 
  pg = createGraphics(DICE_EDGE_SIZE * 6, DICE_EDGE_SIZE, P2D);
 
  //  サイコロの目ボールを作る
  int diceEyeCount = diceEye.length;
  for(int i = 0; i < diceEyeCount; i++){
    color c;
    int size;
    if(DICE_EYE_DATA[i][0] == 0){
      c = color(255, 0, 0);
      size = EYE_SIZE_RED;
    }else{
      c = color(0);
      size = EYE_SIZE_BLACK;
    }
    diceEye[i] = new DiceEye(new PVector(DICE_EYE_DATA[i][1], DICE_EYE_DATA[i][2]), c, size, 
    DICE_EDGE_SIZE * DICE_EYE_DATA[i][0], DICE_EDGE_SIZE * (DICE_EYE_DATA[i][0] + 1), 0, DICE_EDGE_SIZE);
  }
}
 
void draw() {
  background(0, 0, 255);
 
  //  サイコロテクスチャを描画する
  pg.beginDraw();
  pg.background(255);
  int diceEyeCount = diceEye.length;
  for(int i = 0; i < diceEyeCount; i++){
    diceEye[i].move();
    diceEye[i].draw(pg);
  }
  pg.endDraw();
 
  //  カメラを中心に向ける
  camera(0, 0, 500, 0, 0, 0, 0, 1, 0);
 
  //  サイコロを回転させる
  rotateY(radians(rot));
  rotateX(radians(rot / 2));
  rot++;
 
  dice(100);
}
 
 
//  サイコロの描画
void dice(float size) {
  float halfSize = size / 2;
 
  //  正面(1)
  beginShape(QUAD);
  texture(pg);
  vertex(-halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 0, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 0);
  vertex( halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 1);
  vertex(-halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 0, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  右側面(2)
  beginShape(QUAD);
  texture(pg);
  vertex( halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 0);
  vertex( halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 1);
  vertex( halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 1, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  背側面(6)
  beginShape(QUAD);
  texture(pg);
  vertex( halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 0);
  vertex(-halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 6, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 6, DICE_EDGE_SIZE * 1);
  vertex( halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  左側面(5)
  beginShape(QUAD);
  texture(pg);
  vertex(-halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 0);
  vertex(-halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 5, DICE_EDGE_SIZE * 1);
  vertex(-halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
 
  //  上面(3)
  beginShape(QUAD);
  texture(pg);
  vertex(-halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize, -halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 0);
  vertex( halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 1);
  vertex(-halfSize, -halfSize,  halfSize, DICE_EDGE_SIZE * 2, DICE_EDGE_SIZE * 1);
  endShape(CLOSE); 
 
  //  底面(4)
  beginShape(QUAD);
  texture(pg);
  vertex( halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize,  halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 0);
  vertex(-halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 4, DICE_EDGE_SIZE * 1);
  vertex( halfSize,  halfSize, -halfSize, DICE_EDGE_SIZE * 3, DICE_EDGE_SIZE * 1);
  endShape(CLOSE);
}
 
//  サイコロの目ボールクラス
class DiceEye{
  PVector vec;  //  移動量
  PVector pos;  //  位置
  color c;      //  色
  int size;     //  大きさ
  int borderTop;    //  ボールが動ける領域の上端
  int borderBottom; //  ボールが動ける領域の下端
  int borderLeft;   //  ボールが動ける領域の左端
  int borderRight;  //  ボールが動ける領域の右端
 
  DiceEye(PVector position, color c, int size, int borderLeft, int borderRight, int borderTop, int borderBottom){
    float angle = random(TWO_PI);
    this.vec = new PVector(cos(angle), sin(angle));
    this.pos = position;
    this.c = c;
    this.size = size;
    this.borderLeft = borderLeft;
    this.borderRight = borderRight;
    this.borderTop = borderTop;
    this.borderBottom = borderBottom;
  }
 
  //  ボールを動かす
  void move(){
    float halfSize = size / 2;
    PVector tmpPosition = PVector.add(pos, vec);  //  移動後の位置
 
    //  ボールの位置が境界を超えている場合は、その移動は無効とし、移動方向を変化させる。
    if((tmpPosition.x < borderLeft + halfSize || borderRight - halfSize < tmpPosition.x)
    || (tmpPosition.y < borderTop + halfSize || borderBottom - halfSize < tmpPosition.y)){
      float angle = random(TWO_PI);
      this.vec = new PVector(cos(angle), sin(angle));     
    }else{
      pos = tmpPosition;
    }
  }
 
  //  ボールを描画する
  void draw(PGraphics pg){
    pg.fill(c);
    pg.ellipse(pos.x, pos.y, size, size);
  }
}

Tags: , ,
Posted in Processing Advent Calendar2011 | No Comments »

Comments

Leave a Reply

 Comment Form