様々な関数の使い方をおさらいし、作例コードを書くことで、自身の知識の定着を図っています。
今回は 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
);
}
}