万華鏡を作ってみよう(グラフィックスのコピー)

12月 12th, 2011

Processing Advent Calendar 2011(12日目)

万華鏡を作ってみよう



アプレットはこちら

今回は万華鏡の作成に挑戦します。

三角形に切り取る

万華鏡に存在する全ての三角形は「基本となる三角形」が反転しながら無限に繋がっています。そこで、PGraphicsを利用して「基本となる三角形」を作成し、それをテクスチャとして使用することで万華鏡を表現することにしました。

まずは、テクスチャを三角形できりぬく方法です。
これは、PGraphicsをテクスチャとして、vertex()を利用してポリゴンを描く際に、テクスチャ座標も三角形としてくりぬくことで実現できます。

左側がテクスチャ自体を描画したもの。そして、右側が、それをテクスチャにして、vertex()で三角形を描いたものです。

アプレットはこちら

なお、三角形をbeginShape()で描く際に、テクスチャ座標を反転(右側を0,左側を1)することで、左右反転を実現しています。

PGraphics pg;
final int EDGE_LENGTH = 400;  //  四角形の長さ
final int OBJECT_SIZE = 50;     //  画面内に描画する物体のサイズ
float positionX = 0;  //  テクスチャ内の物体のX座標
 
void setup(){
  size(EDGE_LENGTH * 2, EDGE_LENGTH, P2D);
  pg = createGraphics(EDGE_LENGTH, EDGE_LENGTH, P2D);
 
  smooth();
  textureMode(NORMALIZED);
}
 
void draw(){
  drawTexture(pg);
 
  image(pg, 0, 0);
 
  beginShape(TRIANGLE_STRIP);
  texture(pg);
  vertex(EDGE_LENGTH + EDGE_LENGTH / 2, 0, 0.5, 0);
  vertex(EDGE_LENGTH, EDGE_LENGTH, 1, 1);
  vertex(EDGE_LENGTH + EDGE_LENGTH, EDGE_LENGTH, 0, 1);
  endShape();
}
 
//  テクスチャ用画像の描画
void drawTexture(PGraphics pg){
  positionX += 1;
  if(positionX > pg.width){
    positionX = 0;
  }
 
  pg.beginDraw();
  pg.background(0, 0, 255);
  pg.smooth();
  pg.fill(128, 128, 255);
  pg.rect(positionX, 100, OBJECT_SIZE, OBJECT_SIZE);
  pg.ellipse(positionX, 300, OBJECT_SIZE, OBJECT_SIZE);
  pg.endDraw();
}

万華鏡を作成する

続いて万華鏡を作成します。
万華鏡は、三角形が無限に繋がっています。線を描くと下記のようになります。

これを、横一列を1セットとしてbeginShape(TRIANGLE_STRIP)で描画していきます。

また、三角形の頂点にそれぞれ1,2,3と番号を割り振ると、次のようになっています。
TRIANGLE_STRIPで描画するので、1,2,3,1,2,3…と順番にすることで実現できます。

よって、beginShape(TRIANGLE_STRIP)で描画する際にもその順序で座標をセットするようにします。
(ソース中ではTEXTURE_POINT_XとTEXTURE_POINT_Yにそれぞれテクスチャ座標を配列で持っていて、texturePointIndexを増やしていくことで実現しています。)

こうして、万華鏡が完成します。

アプレットはこちら

なお、画面をマウスクリックすると、万華鏡用のテクスチャの内容を見ることができます。下記のように四角形が飛散しております

final float ROOT_3 = sqrt(3);  //  三角形の高さを求めるのに使用する√3
 
PGraphics pg;  //  万華鏡用のテクスチャ
final int MAX_RECT = 20;  //  万華鏡テクスチャに使用する四角形の個数
Rect[] rects = new Rect[MAX_RECT];  //  万華鏡テクスチャに使用する四角形クラス
 
final int EDGE_LENGTH = 75;  //  各三角形の辺の長さ
final float[] TEXTURE_POINT_X = {1, 0.5, 0};  //  三角形のテクスチャ座標Uを使用する順に並べた物
final float[] TEXTURE_POINT_Y = {1, 0, 1};    //  三角形のテクスチャ座標Vを使用する順に並べた物
 
 
void setup(){
  size(400, 400, P2D);
  pg = createGraphics(width, height, P2D);
 
  smooth();
  noStroke();
  textureMode(NORMALIZED);
 
  //  万華鏡テクスチャに使用する四角形を作成する
  for(int i = 0; i < rects.length; i++){
    rects[i] = new Rect();
  }
}
 
void draw(){
  background(0);
 
  //  万華鏡用のテクスチャを描画する
  drawKaleidTexture();
 
  //  マウス未クリック時は万華鏡、クリック時は万華鏡用テクスチャを表示
  if(!mousePressed){
 
    //  万華鏡内部の描画
    boolean initTopSideFlag = false;  //  1行の三角形の描画について、上側から描画し始めるかどうかのフラグ
    for(int y = 0; y <= height + EDGE_LENGTH; y += EDGE_LENGTH / 2 * ROOT_3){
      boolean topSideFlag = initTopSideFlag;  //  三角形の描画について、上側を描画するかどうかのフラグ
      int texturePointIndex = 0;  //  テクスチャ座標配列(TEXTURE_POINT_)について、使用する配列番号
 
      //  1行の描画
      beginShape(TRIANGLE_STRIP);
      texture(pg);
      for(int x = -EDGE_LENGTH; x <= width + EDGE_LENGTH; x += EDGE_LENGTH / 2){
 
        //  上側描画フラグに応じて、描画する高さを決める
        float pointY = topSideFlag ? y : y + EDGE_LENGTH / 2 * ROOT_3;
 
        vertex(x, pointY, TEXTURE_POINT_X[texturePointIndex], TEXTURE_POINT_Y[texturePointIndex]);
 
        //  フラグやテクスチャ配列番号の変更
        topSideFlag = !topSideFlag;
        texturePointIndex++;
        if(texturePointIndex >= TEXTURE_POINT_X.length){
          texturePointIndex = 0;
        }
      }
      endShape(CLOSE);
      initTopSideFlag = !initTopSideFlag;
    }
  }else{
    image(pg, 0, 0);
  }
}
 
//  万華鏡テクスチャに使用する四角形クラス(回転などを簡潔に記述するために使用)
class Rect{
  PVector pos;  //  座標
  PVector vec;  //  速度
  PVector size; //  大きさ
  color c;      //  色
  float spin;   //  回転角度
  float spinVec;//  回転速度
  boolean live; //  画面内にいるかどうかのフラグ
  Rect(){
    init();
  }
 
  //  初期化
  void init(){
    live = true;
    size = new PVector(random(1, 150), random(1, 150));
    pos = new PVector(-size.x, random(pg.height));
    vec = new PVector(random(1, 10), random(2) - 1);
    c = color(random(160, 222), random(160, 222), random(160, 222), 192);
    spin = random(360);
    spinVec = random(-10, 10);
  }
 
  //  描画
  void draw(PGraphics pg){
    pg.pushMatrix();
    pg.fill(c);
    pg.translate(pos.x, pos.y);
    pg.rotate(radians(spin));
    pg.rect(0, 0, size.x, size.y);
    pg.popMatrix();
  }
 
  //  移動
  void move(PGraphics pg){
    if(pos.x >= pg.width + size.x){
      live = false;
    }else{
      pos.add(vec);
      spin += spinVec;
    }
  }
 
  //  復活判定
  void revival(){
 
    //  画面外になった四角形は、1%の確率で復活する
    if(random(1) < 0.01){
      init();
    }
  }
} 
 
//  万華鏡用のテクスチャを描画する
void drawKaleidTexture(){
  pg.beginDraw();
  pg.smooth();
  pg.rectMode(CENTER);
  pg.background(0);
 
  for(int i = 0; i < rects.length; i++){
 
    //  四角形が画面内にあれば移動して描画。画面内になければ復活判定
    if(rects[i].live){
      rects[i].draw(pg);
      rects[i].move(pg);
    }else{
      rects[i].revival();
    }
  }
  pg.endDraw();
}}

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

Comments

Leave a Reply

 Comment Form