p5.js 関数リファレンスと作例:createGraphics()

2023年3月16日木曜日

p5.js リファレンス

t f B! P L

様々な関数の使い方をおさらいし、作例コードを書くことで、自身の知識の定着を図っています。

今回は createGraphics() 関数の解説とサンプルコードを書いて、その使い方を学びます。誤りや、勘違いがあるかもしれません。何かあれば、ご指摘をいただけると嬉しいです。

p5.js は、バージョン 1.6.0 を使用しています。

 

createGraphics() 関数の説明と使い方

createGraphics() | p5.js 公式リファレンス

説明

p5.js の基本的描画環境である p5.Renderer オブジェクト(p5.Graphics)を生成して返す関数です。平たく言うと、通常のキャンバスとは別に、描画用のレイヤーを生成できる関数です。

createGraphics() で生成するレイヤーはオフスクリーンバッファーとも呼ばれ、このレイヤーに描画しただけでは画面には表示されません。画面に表示するには、image(レイヤー, 0, 0) などで、通常のキャンバスにレイヤーを重ねる必要があります。

 

書き方

createGraphics(w, h, [renderer])

パラメータ

w:レイヤーの幅を数値で指定

h:レイヤーの高さを数値で指定

renderer:2D の描画なら P2D、 3D を用いるなら WEBGL を文字列で指定。指定なしの場合は P2D の扱いになります。

 

返り値

p5.Graphics:レイヤー(オフスクリーンバッファー)

 

p5.js 公式リファレンスのコード例の補足



let pg;
function setup() {
  createCanvas(100, 100);
  pg = createGraphics(100, 100);
}

function draw() {
  background(200);
  pg.background(100);
  pg.noStroke();
  pg.ellipse(pg.width / 2, pg.height / 2, 50, 50);
  image(pg, 50, 50);
  image(pg, 0, 0, 50, 50);
}

100x100 サイズのレイヤーを生成し、レイヤー上に円を描きます。

そのレイヤーを、image() 関数でキャンバス上のそれぞれ別の位置に重ねています。2回めの image() 関数ではレイヤーの幅と高さをそれぞれ半分にして重ねています。

  • image(pg, 50, 50); // x=50, y=50 の位置に、幅=100, 高さ=100 のレイヤーを重ねる
  • image(pg, 0, 0, 50, 50); // x=0, y=0 の位置に、幅=50, 高さ=50 のレイヤーを重ねる

2個めの image() の前に pg.ellipse(0, 0, 100, 100); を入れてみると、image() 関数でレイヤーを重ねた後でもレイヤーを書き換えられることがわかります。


なぜ draw() の中でレイヤーの描画を行っているのか、その意図がよくわかりません。 setup() 中にまとめたほうがスッキリわかりやすい気がします。



let pg;
function setup() {
  createCanvas(100, 100);
  pg = createGraphics(100, 100);
  pg.background(100);
  pg.noStroke();
  pg.ellipse(pg.width / 2, pg.height / 2, 50, 50);
}

function draw() {
  background(200);
  image(pg, 50, 50);
  image(pg, 0, 0, 50, 50);
}

 

createGraphics() を使った作例

createGraphics() を使ったアニメーションの作例を作りました。背景を透明にしたレイヤーを重ねることで、向こう側が透けて見えるトリッキーな効果を出せます。


createGraphics() を使わなくても同様のアニメーションを作成することは可能ですが、createGraphics() を使うほうが楽だし、コードも何をしてるのかわかりやすくなるでしょう。


/** 
 * p5.js createGraphics() 関数の作例
 * 3つのレイヤーを重ねる
 * 
 * @author @deconbatch
 * @version 0.1
 * @license CC0
 * p5.js 1.6.0
 * created 2023.03.16
 */

const w = 600;
const h = w;
const frmRate = 24;
const cycleSec = 5;
const cycleFrm = frmRate * cycleSec;
let lFront, lMiddle, lBack;

function setup() {
  createCanvas(w, h);
  colorMode(HSB, 360, 100, 100, 100);
  frameRate(frmRate);

  // キャンバスの2倍の幅を持つレイヤを生成
  lBack = createGraphics(w * 2, h);
  lBack.colorMode(HSB, 360, 100, 100, 100);
  lBack.noFill();
  lBack.strokeWeight(100);

  // lFront レイヤを生成
  drawLayer(lBack);       // lBack レイヤに描画
  background(0, 0, 0, 0); // 一旦キャンバスをクリアして
  image(lBack, 0, 0);     // lBack レイヤをキャンバスに描画
  lFront = get();         // キャンバスを lFront レイヤとする

  // lMiddle レイヤを生成
  drawLayer(lBack);
  background(0, 0, 0, 0);
  image(lBack, 0, 0);
  lMiddle = get();

  // lBack レイヤに描画
  drawLayer(lBack);

}

function draw() {
  const cycle = sin(TWO_PI * (frameCount % cycleFrm) / cycleFrm);

  background(0, 80, 60, 100);
  translate(w * 0.5, h * 0.5);
  imageMode(CENTER);

  // 動かないレイヤ
  image(lBack, 0, 0);
  // 左右に動くレイヤ
  image(lMiddle, w * 0.5 * cycle, 0);
  image(lFront, -w * 0.5 * cycle, 0);
}

function drawLayer(pg) {
  pg.background(0, 0, 0, 0);
  // レイヤ上にランダムな四角形を 10個描く
  for (let i = 0; i < 10; i++) {
    pg.stroke(30 * (i % 6), 80, 60, 100); // 6色限定
    pg.rect(
      pg.width * floor(random(3)) / 3,
      pg.height * floor(random(3)) / 3,
      pg.width * floor(random(3)) / 3, 
      pg.height * floor(random(3)) / 3
    );
  }
}

 

QooQ