By Jules Kris, Tristan Bunn
はじめに
ようこそ!この描画の章では、vertex()
とbezierVertex()
関数を使用して、直線と曲線のセグメントで構成されたカスタムシェイプの描画について紹介します。これらのテクニックをbeginShape()
とendShape()
と組み合わせて使用し、Color Gradientsチュートリアルのステッカーベースの写真装飾アプリ用のスパークルステッカーを作成します。
前提条件
このチュートリアルを進めるには、以下を完了している必要があります:
前章の「p5.jsの紹介」を完了し、理解していることが必要です。これにより、p5.jsライブラリの基本的な理解が得られます。
カラーグラデーションの作成チュートリアル:
この章はカラーグラデーションの作成の続きで、このスケッチの開始コードを提供し、線形グラデーション、放射状グラデーション、およびいくつかのブレンドモードテクニックを紹介します。
ステップ1 – カラーグラデーションの作成スケッチを開く
スパークルを追加するためにスケッチを準備しましょう。
- カラーグラデーションの作成チュートリアルからコードを開き、それを行っていないか新しいコピーが必要な場合は、このテンプレートコードから始めることができます。
- 夢のような夜空を模倣するために線形グラデーションの値を調整しましょう。
startColor
をcolor(200, 100, 100)
(明るく彩度の高い青)に、endColor
をcolor(300, 100, 100)
(明るく彩度の高いマゼンタ)に変更します。 - 今のところ、
mousePressed()
内でlensFlare()
関数を呼び出している部分をコメントアウトしてください。後でそれに戻ります。
コードは以下のようになるはずです:
let video;
let snapped = false;
function setup() {
createCanvas(640, 480);
colorMode(HSB, 360, 100, 100);
//VIDEOオブジェクトをインスタンス化し、画面の0, 0に描画する
video = createCapture(VIDEO);
video.position(0, 0);
//スナップボタンをクリックしたら、takeSnap関数を実行する
let snapButton = createButton('snap');
snapButton.mouseClicked(takeSnap);
blendMode(LIGHTEST);
noStroke();
}
function mousePressed() {
if (snapped === true) {
gradientFilter();
// lensFlare();
}
}
//写真をまだスナップしていない場合、ウェブカムのビデオフィードが表示される
//takeSnap関数を実行すると、snappedをtrueに設定し、ビデオフィードを削除して、撮影した静止画だけを残す
function takeSnap() {
if (snapped === false) {
image(video, 0, 0);
snapped = true;
video.remove();
}
}
//画面をクリックしたときに放射状グラデーションで塗りつぶされた円を描画する
//各円のサイズと色は、diameterとh変数に含まれるランダムな値
function lensFlare() {
let diameter = random(50, 200);
let h = random(150, 360);
for (let d = diameter; d > 0; d -= 1) {
fill(h, 90, 90);
circle(mouseX, mouseY, d);
h = (h + 1) % 360;
}
}
//for loopとlerpColorを使用して画面に線形グラデーションを描画する
function gradientFilter() {
let startColor = color(200, 100, 100);
let endColor = color(300, 100, 100);
for (let y = 0; y < height; y += 1) {
let amt = map(y, 0, height, 0, 1);
let gradColor = lerpColor(startColor, endColor, amt);
stroke(gradColor);
line(0, y, width, y);
}
}
コードを実行してください。p5.jsエディタがウェブカムにアクセスする許可を求める必要があるかもしれません。
「snap」ボタンをクリックし、その後新しい写真をクリックしてカラーグラデーション効果を適用します。結果は以下のようになるはずです:
試してみよう!
設定したい雰囲気について考えてください。ここでは夜空を選んでいますが、他のものを望むかもしれません!第三のlerpColorで変数を作成し、グラデーションが2色ではなく3色の間で変化するようにしてみてください。
ステップ2 – vertex()
関数を使用してスパークルを作成する
vertex()
関数を使用して、直線で接続された頂点で作られた角張ったスパークルを作成します。頂点(複数形:vertices)は、x-y座標上の点で、線が交わって角度を形成し、三角形、四角形、そしてもちろん星形などのさまざまな多角形を定義するのに使用できます。
- 新しい関数を定義し、
sparkle()
と名付けます sparkle()
関数の中で:- 前の章でレンズフレアを貼り付けたように、スパークルをステッカーとして貼り付けられるようにしたいです。sparkle関数の始めに
push()
を、終わりにpop()
を追加します。そして、translate(mouseX, mouseY)
を書きます。translate関数は、シェイプの頂点の値をシフトして、マウスをクリックした場所に移動させます。push()
とpop()
関数は、translate
関数がsparkle()
関数内でのみ実行され、スケッチの他の要素を誤って変換しないようにします。 vertex()
関数の動作を示すために、まず白い線を描きます。stroke(255)
を書いて、線を白くします。vertex()
関数を使用して定義されたシェイプの場合、beginShape()
関数から始めます。vertex(100, 100)
を追加して、x-y座標(100, 100)に最初の点を作成します。vertex関数はx値、y値、そしてオプションのz値を取ります。これは2Dスケッチなので、3次元座標用のz値は必要ありません。vertex(200, 200)
を書いて、直線を形成する2番目の頂点を追加します。- 頂点を追加し続けると、それらは直線で互いに接続されます。今のところ、
endShape(CLOSE)
関数でシェイプを終了します。
- 前の章でレンズフレアを貼り付けたように、スパークルをステッカーとして貼り付けられるようにしたいです。sparkle関数の始めに
mousePressed()
関数内のif文の中にsparkle()
を追加すると、スケッチを再生してキャンバスをクリックしたときに、作成した線が表示されます:
次に、vertex()
関数にランダム性を追加して、サイズを変化させます。
innerRadius
という変数を作成し、random(3, 5)
に設定します。次に、outerRadius
という2番目の変数を作成し、random(10, 50)
に設定します。これにより、作成する各スパークルのサイズと形状に変化を加えることができます。vertex(100, 100)
を削除し、vertex(200, 200)
をvertex(-innerRadius, innerRadius);
に置き換えます。4点のスパークルを描くには、8つの対称的な頂点を作成する必要があります。
innerRadius
とouterRadius
変数を使用することで、これを簡単にしています。以下がスパークルの描き方です:vertex(-innerRadius, innerRadius); vertex(0, outerRadius); vertex(innerRadius, innerRadius); vertex(outerRadius, 0); vertex(innerRadius, -innerRadius); vertex(0, -outerRadius); vertex(-innerRadius, -innerRadius); vertex(-outerRadius, 0);
ここで、この反転がどのように4点のスパークルを作成するかを、各頂点の座標が表示されているこの図で見ることができます:
- この例では、innerRadiusは5、outerRadiusは50です。
- 2つおきの頂点が他の頂点の反転になっていることに注目してください。外側の点は垂直/水平に互いに鏡像になっており、内側の点は対角線上に互いに鏡像になっています。これにより、スパークルの形状の対称性が維持されます。
mousePressed()
関数内のgradientFilter()
の直下の行にsparkle()
関数を呼び出します。
コードは以下のようになるはずです:
function sparkle() {
push();
// マウスの位置に座標空間を移動させ、(0, 0)がマウス座標と一致するようにします。
translate(mouseX, mouseY);
// 各星の内側と外側の半径をランダムに定義します。
let innerRadius = random(3, 5);
let outerRadius = random(10, 50);
// 星の形を描きます。
beginShape();
vertex(-innerRadius, innerRadius);
vertex(0, outerRadius);
vertex(innerRadius, innerRadius);
vertex(outerRadius, 0);
vertex(innerRadius, -innerRadius);
vertex(0, -outerRadius);
vertex(-innerRadius, -innerRadius);
vertex(-outerRadius, 0);
endShape(CLOSE);
pop();
}
再生ボタンを押すと、修正された青からピンクへのグラデーションフィルターと、キャンバスをクリックするたびに様々なサイズの4辺のスパークルステッカーが表示されます。
試してみよう!
私たちは4つの点から始めました。頂点について学んだことを使って、これらの輝きにもっと点を追加してみましょう!偶数と奇数の輝きで、対称性を達成するための計算が異なることに注意してください。
ステップ3 – ベジェ曲線の間奏
これでvertex()
関数を使って角張った輝きを描くことに成功しました!曲線を使ってより面白い形を描くこともできます。ベジェ曲線はそのための良い方法の1つです。ベジェ曲線は、アンカーポイントのペア(下の灰色の円)と制御点のペア(下の赤い点)を使って曲線を定義します。以下がその様子です。曲線自体は黒で、灰色の線は各点が最終的な曲線にどのように関係しているかを示しています。
bezier()
関数を使ってそのベジェ曲線を描く方法は以下の通りです。この曲線はbezier(100, 150, 50, 50, 250, 50, 200, 150)
を呼び出すことで描かれます:
次に、bezierVertex()
を使ってベジェ曲線を接続して形を作ります。
試してみよう!
bezier関数内の3番目、4番目、5番目、6番目のパラメータの数値を変更して、制御点を変更し、それが形状にどのように影響するかを確認してみてください!
ステップ4 – bezierVertex()
関数を使って丸みを帯びた輝きを作成する
bezierVertex()
関数は、先端に1つのアンカーポイントから始まります。これは、尖った輝きの時と同様にvertex()
関数を使って呼び出します。しかし今回は、形を作るために別のvertex()
関数を呼び出す代わりに、bezierVertex()
関数を呼び出します。Adobe Illustratorや類似のベクターベースのイラストソフトウェアを使ったことがあれば、おそらくベジェ曲線を使ったことがあるでしょう!bezierVertex()
は、ベクターグラフィックソフトウェアでベジェ曲線を操作するのに使用されるハンドルに似た、目に見えない制御点を作成することで機能します。
- 今のところ、
mousePressed()
内でsparkle()
関数を呼び出している箇所をコメントアウトしてください。後でまた戻ってきます。 curvedSparkle()
という新しい関数を定義してください。- 以前の
sparkle()
関数と同様に、push()
とpop()
関数を追加し、その間にtranslate(mouseX, mouseY)
を入れてください。
曲線を作成するには、bezierVertex()
関数を使用する必要があります。
- 尖った輝きの時と同様に、
beginShape()
で形を始め、endShape()
で終わります。 bezierVertex()
関数を持つ形は、必ずvertex()
関数から始める必要があります。まずvertex(x, y)
を呼び出します。これが形の最初の点となります。x
とy
は、星の最初の頂点のx-y座標を表します。
- 次に、
bezierVertex()
関数を呼び出します。2Dスケッチで作業しているため、この関数は以下の順序で6つのパラメータを取ります:- 最初の制御点のx座標
- 最初の制御点のy座標
- 2番目の制御点のx座標
- 2番目の制御点のy座標
- アンカーポイントのx座標
- アンカーポイントのy座標
これで文脈が分かったので、形を作り始めましょう!
// 曲線の星形を描く
beginShape();
// 上部の元のアンカー
vertex(0, -100); // アンカーポイント1
// 右上の曲線
bezierVertex(0, -50, // 制御点1のx-y座標
50, 0, // 制御点2のx-y座標
100, 0 // アンカーポイント2
);
endShape();
2点とその間の線しかないように見えるため、制御点がどこにあるのか疑問に思うかもしれません。制御点は目に見えない形で線の曲線を制御しています!以下がその仕組みです:
stroke('orange');
line(0, -100, 0, -50);
line(50, 0, 100, 0);
circle(0, -50, 5);
ここでは、制御点が円として可視化され、アンカーポイントに線で接続されているのが分かります:
メインのスケッチに戻って、曲線の輝き形状を作り続けましょう!以前と同じ変数を使用しますが、それらを負にしたり、比例した数を加減したりして変更します。
形を完成させましょう:
// 曲線の星形を描く
beginShape();
// 上部の元のアンカー
vertex(0, -100);
// 右上の曲線
bezierVertex(0, -50, 50, 0, 100, 0);
// 右下の曲線
bezierVertex(50, 0, 0, 50, 0, 100);
// 左下の曲線
bezierVertex( 0, 50, -50, 0, -100, 0);
// 左上の曲線
bezierVertex(-50, 0, 0,-50, 0,-100);
endShape();
クリックするたびに曲線の輝きがランダムに0.1から0.5(元のサイズの10%から50%)の間でスケーリングされるように、
random()
関数をパラメータとしてscale()
関数を追加しましょう。scale()
はtranslate()
と同様に変換関数ですが、その下に書かれた座標を移動する代わりに、形状のサイズを大きくしたり小さくしたりします。let starScale = random(0.1, 0.5); scale(starScale);
曲線の輝きの塗りつぶしを
fill(0, 0, 100)
に設定して、明るい白色にします。
curvedSparkle()
関数は以下のようになります:
function curvedSparkle() {
push();
// マウスの位置に移動
translate(mouseX, mouseY);
// 座標系をスケーリング
let starScale = random(0.1, 0.5);
scale(starScale);
// 塗りつぶし色を設定
fill(0, 0, 100);
// 曲線の星形を描く
beginShape();
// 上部の元のアンカー
vertex(0, -100);
// 右上の曲線
bezierVertex(0, -50, 50, 0, 100, 0);
// 右下の曲線
bezierVertex(50, 0, 0, 50, 0, 100);
// 左下の曲線
bezierVertex( 0, 50, -50, 0, -100, 0);
// 左上の曲線
bezierVertex(-50, 0, 0,-50, 0,-100);
endShape();
pop();
}
mousePressed
内のgradientFilter()
関数を呼び出す箇所の下に、curvedSparkle()
関数を呼び出してください。
再生ボタンを押すと、修正された青からピンクのグラデーションフィルターと、キャンバス上でクリックした場所に現れる様々なサイズの4点曲線輝きステッカーが表示されます。
試してみよう!
制御点の値を変更して、輝きの曲線がどのように変化するか試してみてください!
ヒント: bezierVertex()
関数の最初の4つのパラメータの値を変更することで、これを実現できます。
ステップ5 – クリックごとに3種類のステッカーを切り替える
レンズフレア、角張った輝き、曲線の輝きステッカーを切り替えるための条件文を作成しましょう。
sparkleCounter
というグローバル変数を作成し、0に設定します。このカウンターはステッカーを配置するたびに増加し、次のクリックで3つのステッカーのうちどれを貼り付けるかを追跡するのに役立ちます。- mousePressed()関数内の
snapped === true
ブロック内に、一連の条件文を作成します:
if (sparkleCounter % 3 === 0) {
sparkle();
} else if (sparkleCounter % 3 === 1) {
curvedSparkle();
} else {
lensFlare();
}
sparkleCounter += 1;
- このif文が何をしているのか、分解して説明しましょう!これはキャンバスをクリックするたびに実行されます。モジュロ(
%
)を使用してsparkleCounterを3(ステッカー関数の数)で割り、その余りを計算します。余りが0、1、またはそれ以外かによって関数を実行します。この計算を行うたびに、sparkleCounterに1を加えます。これにより、キャンバスをクリックするたびに3つのステッカー関数を順番に実行できます。 - 各ステッカー関数の先頭に
push()
があり、各関数の最後にpop()
があることを確認してください。これにより、それらの関数内で適用する様々な変換が関数外の何にも影響を与えないようになります。
最終的なコードは以下のようになります:
let video;
let snapped = false;
let sparkleCounter = 0;
function setup() {
createCanvas(640, 480);
colorMode(HSB, 360, 100, 100);
noStroke();
//VIDEOオブジェクトをインスタンス化し、画面の0, 0に描画する
video = createCapture(VIDEO);
video.position(0, 0);
//snapボタンをクリックしたときにtakeSnap関数を実行する
let snapButton = createButton("snap");
snapButton.mouseClicked(takeSnap);
blendMode(LIGHTEST);
}
//マウスを押すたびに3つのステッカーを順番に切り替える
function mousePressed() {
if (snapped === true) {
gradientFilter();
if (sparkleCounter % 3 === 0) {
sparkle();
} else if (sparkleCounter % 3 === 1) {
curvedSparkle();
} else {
lensFlare();
}
sparkleCounter += 1;
}
}
//まだ写真を撮っていない場合は、ウェブカメラのビデオフィードが表示される
//takeSnap関数を実行すると、snappedをtrueに設定し、ビデオフィードを削除して、撮影した静止画だけを残す
function takeSnap() {
if (snapped === false) {
image(video, 0, 0);
snapped = true;
video.remove();
}
}
function sparkle() {
push();
// マウスの位置に移動する
translate(mouseX, mouseY);
// 形状の頂点を設定する
let vertex1 = random(3, 5);
let vertex2 = random(10, 50);
// 星形を描く
beginShape();
vertex(-vertex1, vertex1);
vertex(0, vertex2);
vertex(vertex1, vertex1);
vertex(vertex2, 0);
vertex(vertex1, -vertex1);
vertex(0, -vertex2);
vertex(-vertex1, -vertex1);
vertex(-vertex2, 0);
endShape(CLOSE);
pop();
}
function curvedSparkle() {
push();
// マウスの位置に移動する
translate(mouseX, mouseY);
// 座標系をスケーリングする
let starScale = random(0.1, 0.5);
scale(starScale);
// 塗りつぶし色を設定する
fill(0, 0, 100);
// 曲線の星形を描く
beginShape();
// 上部の元のアンカー
vertex(0, -100);
// 右上の曲線
bezierVertex(0, -50, 50, 0, 100, 0);
// 右下の曲線
bezierVertex(50, 0, 0, 50, 0, 100);
// 左下の曲線
bezierVertex( 0, 50, -50, 0, -100, 0);
// 左上の曲線
bezierVertex(-50, 0, 0,-50, 0,-100);
endShape();
pop();
}
//画面をクリックしたときに放射状グラデーションで塗りつぶされた円を描く
//各円のサイズと色はdiameterとh変数に含まれるランダムな値
function lensFlare() {
push();
let diameter = random(50, 200);
let h = random(150, 360);
for (let d = diameter; d > 0; d -= 1) {
fill(h, 90, 90);
circle(mouseX, mouseY, d);
h = (h + 1) % 360;
}
pop();
}
//forループとlerpColorを使用して画面に線形グラデーションを描く
function gradientFilter() {
push();
let startColor = color(200, 100, 100);
let endColor = color(300, 100, 100);
for (let y = 0; y < height; y += 1) {
let amt = map(y, 0, height, 0, 1);
let gradColor = lerpColor(startColor, endColor, amt);
stroke(gradColor);
line(0, y, width, y);
}
pop();
}
再生ボタンを押し、写真を撮影し、キャンバスをクリックすると、最終結果を見ることができます!
ステッカーを追加し続けると、画面全体をステッカーで覆うことができ、LIGHTEST
ブレンドモードがキャンバスのすべてのレイヤーとどのように相互作用するかを見ることができます。
こちらが完成したスケッチです。参考にしてください。
試してみよう!
これまでに学んだことを使って、前のレッスンの内容を含む4つ目のステッカー関数を作成してください。そして、それをmousePressed内の既存の条件文に追加し、キャンバスをクリックするたびに4つの形状関数すべてを順番に実行するようにしてください。
まとめ
このチュートリアルでは、vertex()
とbezierVertex()
を使用して角張った輝きと曲線の輝きステッカーを作成する方法を学びました。また、translate()
を使用してマウスポインタの下にカスタム形状を配置する方法も学びました。最後に、キャンバスをクリックするたびに3つの異なる関数を繰り返し実行するカウンターの作成方法を学びました。おめでとうございます!このスケッチ内の座標、サイズ、色の値を変更したり、学んだことを使って独自のカスタム形状を導入したりしてみてください!
次のステップ
以下の他のチュートリアルを試してみてください:
- HTMLの作成とスタイリング(Webデザインの章)
- アブラカダブラ:手で話す(ml5.jsとp5.js)
- シンプルなメロディーアプリ(Node.jsとp5.js)