UnityのTextureで線が繋がるお絵描きをする

miso-engine.hatenablog.com
oFでお絵描きしたら、今度はUnityでもやってみる。先人たちが結構やっているので、まず参考リンクから。
Unityでテクスチャにお絵描きしよう - おもちゃラボ

点を打つのではなく線を引く

上記の実装だと点なので、oFでもやったように線にしよう。Unityではテクスチャに対する描画系のメソッドが一切ないからよりプリミティブな実装が必要になる。「ブレゼンハムのアルゴリズム」というものがあるので、それを利用した。
ブレゼンハムのアルゴリズム - Wikipedia

実際のコードはこちらのQiitaの記事が参考になったというかほぼ同じになったので掲載しない。Qiitaを参照して欲しい。
DXライブラリとブレゼンハムのアルゴリズムを使って線分を描画 - Qiita

線にする条件 マジックナンバー0.12

oFのときは線にするかどうかをマウスイベントで判断していたが、マウスイベントを渡す実装がちょっと手間だったので描画メソッド呼び出しの間隔で判断するようにした。つまり前回の描画メソッドの呼び出しからの秒数によって前回打った点と今回の点との間で、線を描くかどうかを決めるという仕組みだ。これは非常にうまくいった。今回のプロジェクトでは0.12秒以内だといい感じだった。最初は0.5秒にしていたが、そうすると例えばひらがなを書くと指を離したはずなのに繋がってしまっていた。というわけでマジックナンバー0.12、オススメです。マウスイベントによって分岐させるよりもシンプルに書けるし。

消しゴム

消しゴムは色をColor.clearにすることで簡単に実現できる。UI的にはペンよりもブラシの幅を太くするとしっくりくる。

openFrameworksでマウス入力からある程度滑らかな線を引く

お絵描き機能の実現のためにタイトルのことをしたいときのソリューションをまとめておく。本気で滑らかにしたいならよりよいアルゴリズムがあるはずなので、他の場所で記事を探して欲しい。

基本

一番単純な実装は、マウスが押されている時の各フレームごとのマウス位置で点を打つ方法だ。しかし、これ少し早くマウスを動かすだけで点線になってしまう。そこで現在のマウス位置と直前のフレームでのマウス位置の間でofDrawLineで線を引く。こうすると必ず線はつながる。線を引くだけだとつなぎ目が見えてしまうことがあるが、そんなときは線の終端に線の太さと同じ円を描くと滑らかになる。

shaderを使うとき

上記のやり方でやっていくとshaderを使ったときに、ofDrawLineで描く線がofSetLineWidthの値に関わらず細くなる現象に遭遇する。shader側に渡すことで解決出来るらしいのだが、それもちょっとなと思って描き方を変えた。線の角度と長さを計算し、ofVertexを利用して四角形を描くのだ。メソッドにまとめたのが以下のコードになる。

void Test::drawLine(ofPoint startPos, ofPoint stopPos, float lineWidth){
    ofPoint vec = (stopPos - startPos).normalize() * lineWidth/2;
    vec.rotate(90, ofPoint(0, 0, 1));
    
    ofPoint first = startPos + vec;
    ofPoint second = stopPos + vec;
    ofPoint third = stopPos - vec;
    ofPoint fourth = startPos - vec;
    ofBeginShape();
    ofVertex(first);
    ofVertex(second);
    ofVertex(third);
    ofVertex(fourth);
    ofEndShape();
    
    ofDrawCircle(startPos, lineWidth/2);
    ofDrawCircle(stopPos, lineWidth/2);
}

UnityのClothをスクリプトから操る

最近仕事でめっちゃCloth使ったのだが、Clothはスクリプトからcoefficientsを利用することで操れて楽しい。以下、簡単にClothについて箇条書き。

  • Clothがついてると拡大縮小が出来ないが、ClothをDestroyすると可能になる
    • Cloth、実は実行中でもDestroyとAddComponetが可能だったりする
    • パラメータをどこかに保存しておけば布の状態を復元できる
  • Clothの制約、coefficientsはコードから変更できる
    • 通常GUIから変更すると書いてあるが実際はコードから変更可能
    • 普通に左上が原点のマトリクスだった(下にPlaneのcofficientsを変更するコードを貼っておく)
    • これにより動的に布の動き方を制御することが可能
      • 固定点を上部にした状態と、下部にした状態を使い分けたり、布に片方がだけはためくなどの状態を作り出せる
const int meshWidth = 11;
const int meshHeight = 11;
const int meshNum = meshWidth * meshHeight;	

ClothSkinningCoefficient[] frontSkin = new ClothSkinningCoefficient[meshNum];
ClothSkinningCoefficient[] backSkin = new ClothSkinningCoefficient[meshNum];

void Start(){
	for (int i = 0; i < meshNum; i++) {
		frontSkin [i].maxDistance = float.MaxValue;
		frontSkin [i].collisionSphereDistance = float.MaxValue;
		backSkin [i].maxDistance = float.MaxValue;
		backSkin [i].collisionSphereDistance = float.MaxValue;
	}
	for (int i = 0; i < 11; i++) {
		frontSkin [i ].maxDistance = 1.5f;
		frontSkin [i + 11*10].maxDistance = 0;

		backSkin [i + 110].maxDistance = 1.5f;
		backSkin [i].maxDistance = 0;
	}
}

public void ChangeFront(){
	GetComponent<Cloth> ().coefficients = frontSkin;
}