ベジェ曲線を扱う時に曲線間の任意の点を取得したい時がありました。
bezier関数を使用しないベジェ曲線の作成方法について書いていこうと思います。
ベジェ曲線とは?
ベジェ曲線とはコンピューターで曲線を描いたりする時に使用する手法のことです。
3つの点(始点、終点、制御点)を使用して様々な形状の曲線を描くことが出来ます。
制御点を動かすことによって線の曲がり具合を調節できるイメージです。
図1
線形補間
ベジェ曲線は線形補間という概念が基となっています。
線形補間とは2つの点の間の値を一次関数に近似して求めることです。
図1の始点と終点を10等分の線形補間を行うと以下のようになります。
図2
//始点と終点
PVector startP = new PVector(5, width/2);
PVector endP = new PVector(width-5, height/2);
//始点と終点の線分
stroke(255);
strokeWeight(3);
line(startP.x, startP.y, endP.x, endP.y);
//線分の線形補間
stroke(255,0,0);
strokeWeight(15);
for (float i = 0; i<=10; i+=1) {
float x = lerp(startP.x, endP.x, i/10);
float y = lerp(startP.y, endP.y, i/10);
point(x, y);
}
ソースコードで使用しているlerp関数は第一、第二引数の間の値を第三引数(0.0~1.0)
の比率で取得します。
ソースコードではstartPのx,y座標とendPのx,y座標の間の値を比率0.1ずつ増加させて
取得しています。
ベジェ曲線の作成
ベジェ曲線の作成方法は以下のように言い表せます。
「始点と制御点、制御点と終点の線形補間の結果を再度線形補間してつなぎ合わせる」
それでは図1の3点(始点、終点、制御点)と線形補間を使用してベジェ曲線を作成していきます。
ベジェ曲線の作成は以下手順となります。
始点と制御点の線形補間
制御点と終点の線形補間
1、2の取得結果を線形補間
3の取得結果をつなぎ合わせる
//始点、終点、制御点
PVector startP = new PVector(5, width/2);
PVector endP = new PVector(width-5, height/2);
PVector p1 = new PVector(width/2, height-100);
//ベジェ曲線分割回数
int bezierSplitN = 10;
for (float i = 1; i<bezierSplitN; i+=1) {
//1.始点と制御点の線形補間
float x1 = lerp(startP.x, p1.x, i/bezierSplitN);
float y1 = lerp(startP.y, p1.y, i/bezierSplitN);
//2.制御点と終点の線形補間
float x2 = lerp(p1.x, endP.x, i/bezierSplitN);
float y2 = lerp(p1.y, endP.y, i/bezierSplitN);
//手順1,2の線形補間を白色の線で可視化
stroke(255);
strokeWeight(1);
line(x1, y1, x2, y2);
//3. 1、2の取得結果を線形補間
float bezX = lerp(x1, x2, i/bezierSplitN);
float bezY = lerp(y1, y2, i/bezierSplitN);
//黄色の点で可視化
strokeWeight(10);
stroke(255, 255, 0);
point(bezX, bezY);
//手順1,2の線形補間を赤色の点で可視化
stroke(255, 0, 0);
point(x1, y1);
point(x2, y2);
}
図3
図2の赤の点は始点と終点を線形補間した部分です。
黄色の点は手順1,2の結果を線形補間したもので、黄色の点を全てつなぎ合わせた線がベジェ曲線となります。
3次ベジェ曲線
3次ベジェ曲線は4点(始点、制御点2つ、終点)からなるベジェ曲線です。
手順は以下となります。
3点(始点、制御点a、制御点b)を使用し手順3 の結果を取得
3点(制御点a、制御点b、終点)を使用し手順3 の結果を取得
1,2の結果を線形補間
//ベジェ曲線の点を格納
ArrayList<PVector> bezierPlist = new ArrayList<PVector>();
void setup() {
size(850, 850);
pixelDensity(2);
background(0);
//始点、制御点2つ、終点
PVector startP = new PVector(5, width/2);
PVector p1 = new PVector(width/2-200, 200);
PVector p2 = new PVector(width/2+200, height-200);
PVector endP = new PVector(width-5, height/2);
//ベジェ曲線を構成する点をArrayListに格納
for (float i = 0; i<=100; i+=1) {
PVector v1 = quadratic(startP, p1, p2, i/100);
PVector v2 = quadratic(p1, p2, endP, i/100);
float vx = lerp(v1.x, v2.x, i/100);
float vy = lerp(v1.y, v2.y, i/100);
bezierPlist.add(new PVector(vx, vy));
}
//ベジェ曲線の点を描画
strokeWeight(6);
stroke(255, 255, 0);
for (PVector p : bezierPlist) {
point(p.x, p.y);
}
//始点、制御点2つ、終点の描画
stroke(255, 0, 0);
strokeWeight(10);
point(startP.x, startP.y);
point(p1.x, p1.y);
point(p2.x, p2.y);
point(endP.x, endP.y);
save("Vezier.jpg");
}
//引数のPVectorを線形補間しPVectorオブジェクトで返す
PVector quadratic(PVector p0, PVector p1, PVector p2, float t) {
float x = lerp(p0.x, p1.x, t);
float y = lerp(p0.y, p1.y, t);
float x2 = lerp(p1.x, p2.x, t);
float y2 = lerp(p1.y, p2.y, t);
float x3 = lerp(x, x2, t);
float y3 = lerp(y, y2, t);
PVector resultp = new PVector(x3, y3);
//各点の線形補間の結果を線で結ぶ
stroke(255);
strokeWeight(1);
line(x, y, x2, y2);
line(x2, y2, x3, y3);
return resultp;
}
図4
以上がベジェ曲線の作成方法となります。
今回記載したベジェ曲線の作成手順を使い倒して制作した絵がこちらになります。ゴンドアートという模様と動物を使ったアートをモチーフとしています。
布が中心に吸い込まれていくような雰囲気がありますよね。
今回は自分も分からない中できるだけ情報を噛み砕いて書いたので、もしかすると間違っている所があるかもしれないです。その際はコメントなどで教えていただけると嬉しいです。
最後まで記事を読んでいただきありがとうございます。
参考資料:
一から学ぶベジェ曲線 <https://postd.cc/bezier-curves/>
ダニエルシフマン先生 <https://www.youtube.com/watch?v=enNfb6p3j_g>
Commentaires