Processing で簡単モーフィング
CG と言えばモーフィング! Processing でもモーフィング出来るはず!
やってみましょう。
「ある物体から別の物体へと自然に変形する映像をみせる。これはオーバーラップを使った映像のすり替えとは異なり、変形していく間の映像をコンピュータによって補完して作成する。」
なにやら難しそうですが、ある条件であれば簡単に出来るんです。
この記事は全文無料でお読みいただけます。もしお気に召しましたら投げ銭お願いしますね。😉✨
簡単モーフィングの原理
例として、2つの図形 A と B があったとしましょう。
図形 A
図形 B
図形を A から B まで変化させたい。
図形 : A → B
変化には、ある程度の時間が必要になります。
その時間の変化を t で表すとして、 t が 1.0 から徐々に減って 0.0 になるまでに変化させると考えましょう。
時間 t : 1.0 → 0.0
図形 : A → B
すると図形は、
t が 1.0 のときは、完全に A
t が 0.0 のときは、完全に B
t が 0.9 のときは、だいぶ A 寄り
t が 0.5 のときは、 A と B の中間
t が 0.1 のときは、だいぶ B 寄り
って感じになるでしょう?
雰囲気で数式にするとこんな感じになるんじゃないでしょうか?
時間 t : 1.0 → 0.0
図形 : A * t + B * (1.0 - t);
この考えでやってみたら、こうなりました。
ね? 簡単でしょ?
この考えをコードで表せばいいってわけで、上の例だとこんな感じ。
float shapeAx = radius * cos(radian);
float shapeAy = radius * sin(radian);
float shapeBx = radius * cos(radian * 3.0);
float shapeBy = radius * sin(radian * 1.0);
float applyX = shapeAx * t + shapeBx * (1.0 - t);
float applyY = shapeAy * t + shapeBy * (1.0 - t);
ellipse(applyX, applyY, 10.0, 10.0);
applyX と applyY の計算のところがミソです。
超簡単!
「簡単」というだけあって…
この方法は、図形 A、B 共に数式で描けるものであればどんな複雑な形にでも使えます。
別の言い方をすると、座標を計算できるものならなんでも OK です。
ただし、図形 A、B を構成する点は 1:1 で対応している必要があります。
図形 A のある点を、図形 B では 2つの点に分けるということは出来ません。
図形 A では 2つの点の座標を同じにすることでそう見せかけることはできますけども。
座標を求められないものには使えないので、例えば 2枚の写真を渡されてモーフィングしろと言われてもこの方法ではできません。
同じ座標上の色、彩度、明度はこの方法で変化させられると思いますが、それだと先の Wikipedia の説明で出てきたような「オーバーラップ」のような効果しか得られない気がします。
さらに、『なんでそんな動きになるの?』という変化の仕方をすることもあります。
例えばこれ。
図形 A
図形 B
モーフィングはこう。
もっと素直にスッと行けないもんかね?と思いますが、変化後の図形 B、
これが実は点が重なっててわかりませんが、8の字が 2重になっているんです。
直前の形を見るとわかりやすいかな?
そのためにひねくれた動きをしているように見えるんですね。
自分の思ったような変化のさせ方はできないということでもあります。
楽しい実例
図形を形作る数式を変えれば動きも変わります。
移動や回転、縮小も同じ方法で実現できます。
でも、これは普通モーフィングとは言わないですね。
動きにイージングを施しても面白いですよ。
点の数を増やして数式を複雑にしたとしても考え方は変わりません。
面白い数式を選べばこんなこともできます。
当然 3D でも同じ理屈で可能です。
ああっ!3D のほうがモーフィングって感じがしますね!
うれしいサンプルコード
動画のサンプルでは行って戻ってになってますが、コードは行ったっきりのものにしてあります。
15fps x 6秒の動画が作れる分の静止画を書き出します。
// Processing で簡単モーフィング
// @author @deconbatch
// @version 0.1
// Processing 3.2.1
// 2018.11.24
void setup() {
size(400, 400);
colorMode(HSB, 360, 100, 100, 100);
smooth();
noLoop();
noStroke();
noFill();
}
void draw() {
float radius = width * 0.4;
int frameCntMax = 15 * 6;
translate(width / 2.0, height / 2.0);
for (int frameCnt = 0; frameCnt <= frameCntMax; ++frameCnt) {
float frameRatio = easing(map(frameCnt, 0, frameCntMax, 1.0, 0.0));
background(0.0, 0.0, 90.0, 100.0);
for (float dotCnt = 0.0; dotCnt < 1.0; dotCnt += 0.0001) {
float radian = TWO_PI * dotCnt;
float shapeAx = cos(radian);
float shapeAy = sin(radian);
float shapeBx = cos(radian) * pow(cos(radian * 3.0), 2);
float shapeBy = sin(radian) * pow(sin(radian * 3.0), 2);
float applyX = shapeAx * frameRatio + shapeBx * (1.0 - frameRatio);
float applyY = shapeAy * frameRatio + shapeBy * (1.0 - frameRatio);
float applyHue = 360 * frameRatio + 240 * (1.0 - frameRatio);
fill(applyHue, 40.0, 80.0, 100.0);
ellipse(applyX * radius, applyY * radius, 1.0, 1.0);
}
saveFrame("frames/" + String.format("%04d", frameCnt) + ".png");
}
exit();
}
private float easing(float t) {
// InOutCubic
t *= 2.0;
if (t < 1.0) {
return pow(t, 3) / 2.0;
}
t -= 2.0;
return (pow(t, 3) + 2.0) / 2.0;
}
/*
Copyright (C) 2018- deconbatch
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
GPL で公開しますので、自由にコピーして楽しんでみてください。
お誘い
こんな楽しいクリエイティブ・コーディングを一緒に楽しみませんか?
全くのプログラミング初心者の方に向けた記事書いてます。
読んでみてね!
ここでこの記事はおしまいです。もしこの記事がお気に召しましたら投げ銭お願いします。😉✨
ここから先は
¥ 100
この記事が面白かったらサポートしていただけませんか? ぜんざい好きな私に、ぜんざいをお腹いっぱい食べさせてほしい。あなたのことを想いながら食べるから、ぜんざいサポートお願いね 💕