うつ病になった話

うつ病になった

気分が落ち込むとか体が重いとかは続いてましたが、点数が見える形で以前より頭が回らないことを示されたのが病院受診のきっかけでした。
またこちらの記事も自分の背中をおしてくれました。
note.com


今の症状で明らかにおかしいものは集中力の低下です。
何をやっても続けられません。特に自分で手を動かして何かを生み出すようなことはやろうと手を動かし始めても途中で止まってしまいます。
よく言うやり始めさえすれば自身のモードが切り替わってできるようになる みたいなのも今の自分には効果を発揮してくれないです。

今学部四年なので、いずれにしろ今後のことを決めて行動していかなくてはいけない時期ですが、この症状が足を引っ張っているのは確かです。

通院しながら薬での治療を初めて一週間と少しです。
理想は自分が行きたいところに行くための試験がある10月までに回復して、試験の準備ができることですが鬱になったのは初めてで今後の経過のイメージは持てていません。



手を止めてしまっていることに対しての焦りに焼かれそうになりますが、動かすことをいったんあきらめるのがいいのか、無理にでも進めようとするべきなのかわかりません。
集中できないことにいら立つことは多々あり、日常の一部と化してます。


できないことの代わりに映画をよく見るようになりました。
映画っていいですね。

今の気持ちと自分の状況を整理する

今は在宅で就活と卒業研究を並行して進めています。最後に外に出たのはひと月以上前です。
これは日記です


就職活動の面接で言われたことが自分の頭をいっぱいにする悩みを解決する糸口のような気がしたので、振り返りながらまとめます。

今と昔の気持ち

作ったもので人を笑かすのが好きというのは昔からある自分の行動の芯の部分です。それは今も変わっていません。

自分がその時いる環境で手段はいろいろ変わってきました。
(ゲームなら仕様を調べたり試したりして面白そうなものを、会話ならお互いが共感できそうな出来事で面白そうなものをそれぞれストックしておく)
今思えば自分がVRChatでシェーダーかいたりモデル作ったりしてるのも、面白いものを作って笑わしてやろうというのが発端だったはず 多分。

昨年の夏が過ぎたころからだったか、作るものやることなすことと自分のやりたいことがずれていくように感じ始めていました。
よくよく振り返ってみると花火の団扇を作ったあとがきっかけだったように思います。


酔った勢いで思いついたものをちょちょっと作ったはずが過去最高に反響をもらい結構戸惑いました。
ああいうキレイ系は自分が稀に見せる冴えのようなものなので決して得意なものではないのですが、過去一の反響をもらったことでキレイ系のほうが喜ばれると思うようになります。
(アイデアが出しやすいという意味で得意不得意を使ってます)

得意じゃないことを自分の芯に置こうとしてそこから作り始めますが、だんだんアイデアが枯れていきます。
人を笑わすって明確な思いがあるのとは違い、キレイ系はどう思わせるかという目標が自分の中ではっきりしないのに、そのまま進んだのが原因だったんじゃないかな
それでもしばらく作ってこれたのは、知らないことを知るためにやるっていう面が大きかったからです。
ずれはだんだん大きくなって自分が苦しくなって今に至ります。

今の状況

自分の気持ちがやっていることから離れてきたので、ツイッターのTLに流れるアイデアを拾わないと手がうまく動かなくってました。

就職活動で使うポートフォリオには、ずれが出てきてから作ったものが並んでます。それを引っ提げて面接に臨むも今のところまるで手ごたえがなく全落ちしています。
ポートフォリオが弱いのかと思い色々追加して受けてって繰り返しやる中で言われたことが、技術のテストで作ったものばかりってことでした。

すぐには理解できませんでしたが一か月以上考え続けて、ようやく上の気持ちの移り変わりが原因で習作がならぶものになっていたと分かりました。


長く抑えつけていたので感性がマヒしたのか、前のように面白そうなことがぽんぽんとは出てこなくなってしまいました。
でも面白いものをまた作りたいなと思います。

おわり

shader入門から一年たったので、Shader周り濃い目で作ったものについてかく


2020年1月28日でshader入門して一年が経ちました
f:id:tonoshake:20200130224311p:plain


3Dモデリングの話もしたいので、そちらも含めて辿っていきます。
作ったものの話を進めるうえで紹介不可欠な人は、こういう記事でどう扱えばいいか分からないため匿名での紹介にとどめます。

12月末~年明け

自作アバターがVRCで使えるということを知ったのと、当時友達がBlenderで遊んでいたという二つの要因が合わさって、Blenderで3Dモデルづくりを始めました。
一番最初に作ったのは、別のゲームで作成したキャラクターです。

大学の図書館で見つけたこの本を見ながら操作を覚えました。

Blender 3Dキャラクター メイキング・テクニック
僕が見たものは、ちょっとバージョンが古かったのとボーン構造がVRC用と違っていたことで苦労しました。

出来たものに納得がいかなかったのでアバターワールドから持ってきたアバターを使って、喋るタイヤとしてVRCパブリックを渡り歩いていました。
日本人とは会えてなかったですが、不思議空間に魅了されていたので気にならなかったです。

確かこれ作った少し後ぐらいに友達から中古で買ったHTC VIVEが届いたはず

1月半ば~末

Blenderでポリゴンを張るのに疲れたのでSteamで販売していたKodonというVRソフトでスカルプトに挑戦しました。

1時間かからないくらいで出来るし、形を気にすれば自動ウェイトでなんとなく動くものになったので2~3日の間は毎日アバター作ってはアップロードしてました。

The Great Pugでとった写真が残ってました。(左の犬)
f:id:tonoshake:20200130233029p:plain

一人で彷徨う中たどり着いた坪倉家で、日本語を話すMさんに会いました。
色んなワールドを見せてもらう中で、Treehouse in the Shadeへ行きました。
そこで出会ったHさんとANさんと、連れてきてくれたMさんの三人の会話の中で"シェーダー"って語が出てきます。
更に聞いていると、Mさんが持っていた虹色のケミカルなサンドウィッチがどうやらそのシェーダーってものによって表現されていることが分かりました。

その時撮った写真 (1月25日)
f:id:tonoshake:20200130234541p:plain

シェーダーが気になったので数日こねくり回してましたがよくわからなかったので資料を購入します。ここが1月28日です。

そして色を時間で変えるシェーダーを作りました。

扇子持って踊りはじめたのもこのころ

二月

大学に向かうバスの中で作ったヒヨコを使って回転を試しました。
何で回したのか覚えてないですが、ポケモン的な混乱状態の表現がしたかったのかなと思います。
アニメーションが分からなかったので回転行列使って実装しました。


こんな感じの格好で扇子持って踊ったりマヨネーズ振り回したり色々してたらJapan TownでAWさんに出会います。
f:id:tonoshake:20200130235503p:plain
めちゃくちゃ面白いアバターを色々見せてもらって爆笑していたのを覚えています。


2月22日の夜に旅行先の鯖江のホテルでレイマーチングを試していました。
当時は距離をどれだけ進めるか決める手法を知らなかったのと、距離関数の扱い方が全然わかっていなかったので、正八面体を表示するために極小の固定長ですすめる方法で実装していました。
正直ぱっとしなかったので、これ以降しばらくレイマーチングから離れます。

3月

実物見ながらモデリングすれば楽にできるって気づいたので、持っていたベースのモデルを作ります。


頭おかしいくらい集中してたので二日で完成させられました。

このころAWさんやODさんに見せてもらったアバターから、ジオメトリーシェーダーというものの存在を知ります。


ちょっとやってみたもののこの時はよくわかりませんでした。


何となく竹を作りたくなります。
Blenderの配列複製モディファイアの利便性を知っていたのでガンガン使って伸ばしてましたが、長さ調整するためにBlenderを起動するのが面倒だと思います。
Unityで完結させるためにどうしたらいいかなって考えた時に、ポリゴン描画を増やせるジオメトリーシェーダーを思い出します。
出来たのがこれ

楽しくなり竹林そのものを好きに作りたくなって、竹林シェーダーというものを作ります。


今見ても結構いい感じだと思います。

このシェーダーを使ってVRChat上に初の自作ワールド「tonoTAKE」をあげました。

このころHさんに見せてもらったシェーダーの状態保持機構が印象的で、いつか使ってみたいなと思い続けるきっかけになりました。

AWさんにもじゃもじゃ毛の生えたアバターを見せてもらい、ファーシェーダーというものがあることを知ります。
楽しそうだなって思ったので、まず毛を生やすためのモデルを作りました。

そしてカビっぽくなったり毛根マップをもらったり色々あって、毛を生やすことに成功します。

得た知識で色々遊んでたら三月は終わりました。
記憶があいまいですが、ODさんの岩みたいなアバターを見せてもらったのもこのころだったかもしれないです。そこからテセレーションの存在を知りました。

4月

AWさんの呟きからパーリンノイズというものを知り、記事を色々見ながらそれっぽいものが作れます。

4月の残りはパーリンノイズを使って遊びます。
固定長レイマーチングで立体感を出してみたり、壁に張ったり、ファーシェーダーの毛根マップをノイズに変えたり。

tonosamaの作成は4月末の一週間で行いました。


作ったファーシェーダーと組み合わせて遊んでました。

5月

AWさんが見せてくれたベースをレンダリングしたものがかっこよかったので、Blender機能であるレンダリングを触り始めます。


このころにカサゴのモデルとこの画像を作りました。

カサゴのボーンをアニメーションさせる時に、ボーンをAdd propertyしていくのが苦痛だったので、Unityのスクリプトで自動化させました。



講義でこの動画を見て、光が回るタイプのレーダーが作りたくなります。

途中まで普通に作っていましたが脱線してこうなります。

このころAWさんから視界ジャックに使われるのはGrabPassって話を聞いて、存在を知ります。
画像処理の勉強でやった3x3空間フィルタをGrabPass使ってかけられるようにしたシェーダーと、ドット絵シェーダーを作ります。使い方次第なので同じシェーダーです。

ポスタリゼーションで色をいい感じにする部分でめちゃくちゃ凝りました。
VRChatに「Dotono」という名前のワールドとしてあげてあります。

このあと暫く空間フィルタ系シェーダーをぽつぽつと作ってました。

6月

映画「怪獣の子供」を見て深いプールが作りたくなり、波のシェーダーを作ります。
とりあえず波っぽいものを目指した結果ゲルストナー波にたどり着き、シェーダーに実装しました。


「Y180」って名前でVRCワールドとしてあげてあります。

シェーダー作りが迷走していたので、自分の落書きをもとにカエルの3Dモデルを作り始めます。

7月

出来たカエルがこれ


夏の目標にレイマーチングの習得を掲げて、スフィアトレーシングを使いだします。



8月

落選マーケットの締め切りが近かったので、ブースのモデルをつくりはじめます。


この時は形までできました。

盆でお酒を飲んだときに、団扇の花火が動いたら楽しいなって閃いたので、勢いで花火の団扇を作りました。


良い思い付きでした。

落選マーケットの締め切りまであと数日になったので急いでブースを完成させます。


色塗りが面倒だったので、全部シェーダーで色を塗りました。

僕の作業分が終わった後の待ち時間が暇だったので、紅葉を舞わせて遊んでました。


ひらひらさせるようなものはシェーダーで描いたことないなってだけで書いたので特にこだわりはありません。

9月

季節のシェーダーを描こうと思い、紅葉シェーダーを発展させました。


カメラを使った状態保持と物の検知を行って、積もったり掃除出来たりできるようにしました。
毎度毎度細かくメッシュを割りなおすためにBlender開くのがめんどくさかったので、テセレーションデビューしました。
「Autumn memory」って名前でVRChatにあげてあります。


9月末からレイマーチングをもりもりかき始めます。


帰りの電車とか昼休みとか暇を見つけては書いてたので、数日で大体空でかけるようになりました。

10月

オブジェクトスペースレイマーチングに興味を持ち、少しずつ書きだします。
レイマーチングして出てきた物体の表面に、物体ごとに異なる絵を描く と、物体の位置関係に応じて画を変えるみたいなことをやろうとして頭抱えてました。


出来ました。
空間を駆使した片方の物体だけワールドスペースのものをシェーダーフェスに出展しました。


Gさんに紅葉シェーダーは結構需要ある って言われて、配布を始めました。


配布用に真剣に調整したのでめちゃくちゃ疲れました。


フォントがベジェによって構成されてるってことを知ったので、ベジェ曲線の制御点を抽出すればどこでも文字表示できるのではと思い、フォントから制御点を抜き出してシェーダーにするスクリプト をPythonでかきました。


「パーティクルライブとかで文字メッシュを毎度作らなくても、シェーダー内で文字指定すれば表示できる」というものが理想でしたが、思ったより重かったので服の模様にしました。
結構気に入ってます。

31日からSTYLY Avater Diorama Award 2019に向けて急ピッチでジオラマ制作を始めました。

11月

6日でジオラマを完成させ応募しました。


Audience賞をいただけました。


シェーダーの方向性を見失い、ふて寝してたらゴジラの背中のとげがふと浮かんで、体から水晶はえたら面白いなって思ったので、なんにでも水晶を生やせるシェーダーをかきました。


ジオメトリーシェーダーで四角の板以外を作るのは初めてだったので、下書きしてから実装してました。


セルラーノイズを纏ったアバターをODさんに見せてもらって以降、ずっと気になってたセルラーノイズを実装しました。


今までにない見た目がたのしかったのでこねくり回して遊んでました。

12月

セルラーノイズの生々しい感じに中てられて、生々しいレイマーチングの画を描きたくなりました。


良い具合の気持ち悪さが出て満足してました。


iq氏のHappy jumpingを解説したさやちゃんぐbotさんの記事を見て、ライティング意識したものを描きたくなっていろいろやってました。
sayachang-bot.hateblo.jp

shadowrayという考え方もここから知りました。


fovをいじることの楽しさをこのシェーダーで知りました。

ハートが描きたくて描いたシェーダーです。


レイマーチングのオブジェクト描画位置に移動させた座標を、一部軸成分だけ転用したことが自分のなかでは革新的でした。


人なんだけど細かく見ると不定形なアバターが作りたくて挑戦してました。


めちゃくちゃ重たいのと描画順の問題で没になりましたが、Unityの画面では最高です。


12月21日はxRTechNagoyaでShaderについての話で登壇しました。
人生初登壇でめちゃくちゃ緊張して記憶がほとんどないんですが、登壇者の凄さは理解できました。
docs.google.com
その時の資料です。

1月

序盤は自分の中で方向性がめちゃくちゃになってだいぶ苦しんでました。
自分が一番やばいと思うことをやろう って思い、水槽を作り始めました。


以上が一年ほどでやってきたことの主だったことです。

growCrystalsでやっていること[色つくり]

shader全文はこちらにあります
github.com

六角柱の色に関係したfragment shaderは以下の部分になります。

fixed4 frag (g2f i) : SV_Target
{
    clip(_Crystal-1);
    fixed4 mask = tex2D(_MaskTex,i.uv);
    float cl = (mask.g  < 0.5);
    clip(cl * -1);
    
    float mh = (i.maxh+_Sharpness);
    float ihr = (i.h)/mh;
    
    fixed3 col = lerp(float3(1,1,1) , _Color , saturate( (i.h+_White)/mh) );
    float3 specularCol = 1;
    col.rgb = reflection(i,col,specularCol,5);

    float alpha = 1-ihr;
    alpha = clamp( alpha,_AlphaMin,1);
    return float4(col,alpha);
}

わかりにくいので、色決定以外の部分を消して整理すると以下のものになります。
reflection関数は完全に光周りの話になるので、ここでは解説しません。
こちらを全部解説していきます。

fixed4 frag (g2f i) : SV_Target
{
    float mh = (i.maxh+_Sharpness);
    float ihr = (i.h)/mh;
    
    float3 col = lerp(float3(1,1,1) , _Color , saturate( (i.h+_White)/mh) );
    float3 specularCol = 1;
    col = reflection(i,col,specularCol,5);

    float alpha = 1-ihr;
    alpha = clamp( alpha,_AlphaMin,1);
    return float4(col,alpha);
}

変数の中身と処理の流れ

    float mh = (i.maxh+_Sharpness);
    float ihr = (i.h)/mh;

f:id:tonoshake:20191118122045p:plain
mh はクリスタルの長さを表しています。
i.maxhは六角柱部分の長さ、_Sharpnessは先端部分の長さが入っています。
i.h はfragment shaderで現在処理している場所が生え際からどれくらい離れているかが入っています。

そのためihrには現在処理している場所がクリスタル全体のどれくらいの位置にあるかが入ります(根元が0、先端が1)。

    float3 col = lerp(float3(1,1,1) , _Color , (i.h+_White)/mh);

f:id:tonoshake:20191120124910p:plain
colは白(float3(1,1,1))と_Color(クリスタルの色)を高さに応じて線形補完した色が入ります。
簡単に言えば、根元は白っぽく、先端は設定したクリスタルの色っぽくなります。

lerpは(A,B,どれくらいの割合)という引数をとる関数です。
例えば どれくらいの割合 というところにいれた値が0.5なら、AとBを半分ずつ足したものになります。


上で出てきたコードと違い、今処理している部分(i.h)に_Whiteという値が加算されています。
この_Whiteを大きくすると、(i.h+_White)が大きくなり、全体的により先端に近い場所とみなすことが出来ます。
これの何が嬉しいかというと、全体の高さを上げることで、全体の色をクリスタルに設定した色に近づけることができます。
つまり_Whiteのパラメータは、クリスタルの色の長さを調整するものになります。


saturateというのは、()の中が0より小さいなら0に、1より大きいなら1にするものです。

    float3 specularCol = 1;
    col = reflection(i,col,specularCol,5);

specularColは反射光の色です。
白にしています。
reflectionは反射光の計算用に自分で定義しました。
上で書いた通り、ここでは解説しません。
実装しているのはPhongの反射モデルです。


  float alpha = 1-ihr;
   alpha = clamp( alpha,_AlphaMin,1);
   return float4(col,alpha);
}

上で出した、クリスタル全体のどれくらいの位置にあるかを使って透明具合を決めています。
ここでは0が透明で1が不透明、0.5で半透明になります。
1-ihrとしている意味は、根元の方を不透明にして先端を透明にしたかったからです。
ihrは高さなので、根元が小さく先端が大きくなっています。最大値が1なので、1から引くことで大小を反転させることができます。
次のclamp(alpha,_AlphaMin,1)の部分では、透明度の下限値を設定しています。
clampは(なんらかの値,下限値,上限値)と引数を取り、下限値より下なら下限値に、上限値より上なら上限値に、それ以外は何らかの値を返してくれます。
下限値_AlphaMinは外から設定できる値なので、ここを調整すれば透明度を好きに決められるようになります。

以上のことが、growCrystalの色を決める流れです。

growCrystalsでやっていること[コード]

六角柱を作る方法に重きを置くので、ジオメトリーシェーダーの書き方については説明しません。ご了承ください。

growCrystalsでやっていることの要点のみ実装したシンプルなものをgithubに用意しました。
github.com
こちらのジオメトリーシェーダー部を上から全部解説します。

g2f v = (g2f)0;

[unroll]
for(int j = 0;j < 3;j++){
	v.vertex = UnityObjectToClipPos(ip[j].vertex);
	v.uv = ip[j].uv;
	v.normal = ip[j].normal;
	OutputStream.Append(v);
}
OutputStream.RestartStrip();

元のポリゴンを描画するだけです。


//六角柱の各頂点の位置を決める係数群
float3 needle = float3(1./3.,1./3.,1./3.);
float3 tri =  float3(2./4.,1./4.,1./4.);
float3 itri = float3(2./5.,2./5.,1./5.);

float3 pp0 = (p0*needle.x  + p1*needle.y + p2*needle.z );
float3 pp1 = (p0*tri.x   + p1*tri.y  + p2*tri.z);
float3 pp2 = (p0*itri.x  + p1*itri.y + p2*itri.z );
float3 pp3 = (p0*tri.y   + p1*tri.x  + p2*tri.z );
float3 pp4 = (p0*itri.z  + p1*itri.x + p2*itri.y );
float3 pp5 = (p0*tri.z   + p1*tri.y  + p2*tri.x );
float3 pp6 = (p0*itri.y  + p1*itri.z + p2*itri.x );

考えたこと にあったように各頂点にどれだけ近いかを係数として与えて、それらをかけたものを座標にしています。
tri をfloat3(1,0,0)とすると元のポリゴンの頂点上に六角形の頂点が移動します。
一度float3にしているのはこういった調整がしやすいからです。


//中心から頂点へ向かう単位ベクトルを計算
float3 pv1 = normalize(pp1-pp0);
float3 pv2 = normalize(pp2-pp0);
float3 pv3 = normalize(pp3-pp0);
float3 pv4 = normalize(pp4-pp0);
float3 pv5 = normalize(pp5-pp0);
float3 pv6 = normalize(pp6-pp0);

コードの中のコメントにもありますが考えたことで出てきたことです。
六角柱の中心から六角柱の各頂点へ向いたベクトルを出し、正規化しています。
これに任意の数をかけることで、中心→頂点へ任意の数のぶんだけ移動させられる便利なものです。

float size1 = _CrystalSize1;
float size2 = _CrystalSize2;

//中心から見て頂点方向に伸ばす
pp1 += pv1 * size1;
pp2 += pv2 * size1;
pp3 += pv3 * size1;
pp4 += pv4 * size2;
pp5 += pv5 * size2;
pp6 += pv6 * size2;

ここで六角柱の太さを設定しています。
size1,size2の大きさだけ中心→頂点方向へ大きくなります。
大きさはInspectorから触れるようにしてあります。

float3 dir = getNormal(p0,p1,p2);
float len =_CrystalLength;

float3 pp0d = pp0 + dir * _Sharpness;
//法線の方向にlenの長さ分伸ばす
float3 pp1d = pp1 + dir * len;
float3 pp2d = pp2 + dir * len;
float3 pp3d = pp3 + dir * len;
float3 pp4d = pp4 + dir * len;
float3 pp5d = pp5 + dir * len;
float3 pp6d = pp6 + dir * len;

ここで六角柱の長さを設定しています。
dirに代入されているgetNormal()は、考えたこと にあった外積を用いて法線を出して返すものです。

pp0dには_Sharpnessがかけられています。
先端のとんがりの長さ≒鋭さとしてパラメータを分けています。

六角柱の作成

ここからいよいよ六角柱を作っていきます。
f:id:tonoshake:20191114214129p:plain
六角柱はこのように三角形のポリゴンが組み合わさってできています。
そのため、側面の四角い部分も三角形二つを組み合わせて作ることになります。

どうやって作るかをpp1,pp2,pp1d,pp2dを例にとって示します。
f:id:tonoshake:20191114215052p:plain
黄色の矢印でpp1→pp2→ppd1と辿って1つ目
白色の矢印でpp2→ppd1→ppd2と辿って2つ目になります。

実装部分のみ抜き出すと以下のコードになります。

v4o = float4(pp1,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

v4o = float4(pp2,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

v4o = float4(pp1d,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

OutputStream.RestartStrip();

v4o = float4(pp2,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

v4o = float4(pp1d,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

v4o = float4(pp2d,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

OutputStream.RestartStrip();

v4oに入れているfloat4のなかを見ると分かりますが、上の矢印で辿ったように頂点が入っています。

実装されているコードには法線情報を入れている部分がありますが、六角にするだけならこれでも十分です。
これを頂点を変えながら側面6つ分繰り返します。

六角柱の先端部分

f:id:tonoshake:20191114220612p:plain
六角中の先端は2つの頂点と真ん中の頂点を結んだ三角形を並べることで作られます。
pp1d,pp2d,pp0dを例にとって示します。

//top1
v4o = float4(pp1d,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

v4o = float4(pp2d,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

v4o = float4(pp0d,1);
o.vertex = UnityObjectToClipPos(v4o);
OutputStream.Append(o);

OutputStream.RestartStrip();

pp1d→pp2d→pp0dとなっているのが分かると思います。
これを先端の面の数である6回頂点を変えながら繰り返すことで六角柱を作ることが出来ます。

一通り解説しましたが、おかしなところやわからないところがあれば教えてください。

11/20
続編 色の付け方の解説を書きました
tonoshake.hateblo.jp

growCrystalsでやっていること[考えたこと]

 

 

どうやってクリスタルを生やしているか

f:id:tonoshake:20191114091545j:image

一枚の三角ポリゴンの上に頂点を6つ定義して、そこから六角柱を伸ばしています。

 

どうやって6つの頂点を定義しているか

f:id:tonoshake:20191114094040j:image

三角ポリゴンの各頂点からの距離を使って6つの点を導出しています。

点の座標をABCそれぞれにどの程度寄せるかを係数として与えています。

 

 

三角ポリゴンの頂点3つをABCとして具体的な式を示します。

p0=A/3 + B/3 + C/3

p1=A×1/2 +B×1/4+C×1/4

p2=A×2/5 +B×2/5+C×1/5

p3=A×1/4 +B×1/2+C×1/4

p4=A×2/5 +B×1/5+C×2/5

p5=A×1/4 +B×1/4+C×1/2

p6=A×2/5 +B×2/5+C×1/5

 

係数の合計値がどれも1になっているのがポイントです。

 

どうやって面からまっすぐに伸ばすか

面の法線を利用しています。

f:id:tonoshake:20191114145739j:image

そのままでは頂点ごとに別の法線を持っていて扱いにくいので、面の法線を求めます。

 

外積を用いることで簡単に求められます。

f:id:tonoshake:20191114150622j:image

C→A向きのベクトルとC→B向きのベクトルの外積をとることで、ABCに垂直なベクトルが取れます。

それを正規化することで、好きな数字をかければ面に垂直な方向へ大きくなる便利な単位ベクトルとして扱えます。

 

C→A向きのベクトルは、AからCを引いたものになります。

外積や正規化は、それぞれcross()とnormalize()という関数が用意されているので、手で実装しなくても大丈夫です。

 

どうやって先端を作るか

二つ前に出した六角形の中心点p0を使用します。

 

p0からp6まで法線方向に移動させたものを、それぞれpp0〜pp6とします。

 

f:id:tonoshake:20191114153209j:image

pp0、pp1、pp2を結べば三角形を作ることができます。

あとはこれをpp0 pp2 pp3のように隣り合った頂点とpp0を結んで行きます。

 

クリスタルの大きさの変え方

 

以上のものをコードにすれば六角柱を生やすことは出来ますが、太さの調整がしにくいため一工夫加えます。

 

形を崩さず太さを変えるには、六角の中心→六角の頂点(または六角の頂点→六角の中心)の方向に六角の頂点を移動させる必要があります。

先程法線を求める際AからCをひくことで、C→A向きのベクトルを求めました。

六角の中心→六角の頂点も同様にして求めることができます。中心→各頂点の向きベクトルをそれぞれpd1〜pd6としていくつか式として示します。

pd1=p1-p0

pd2=p2-p0

...

pd6=p6-p0

 

各pdを正規化すれば、好きな数字をかけると中心→頂点の方向に移動する便利なベクトルになります。

マイナスをかければ中心→頂点方向へ移動するのでサイズ調整はばっちりです。

 

以上が考え方の説明です。

次の記事では実際にコードに起こすためのあれこれを解説します。

 https://tonoshake.hateblo.jp/entry/2019/11/14/221842

 

こちらで配布してます

Grow Crystals + 水晶の置物△188 | さかなや https://booth.pm/ja/items/1679585