もりもりゲーム制作ブログ

それほどもりもりしてません。Unityの忘備録的ブログ。

【UnityShader】UnityのShaderでOrbit(惑星軌道)

こういうの作ります(実際にはもっとなめらかにループしてます)

f:id:monimoni114514:20190510195752g:plain
OrbitShader
惑星軌道と惑星を表現したいとき(どんなときだよ)に使えます。

始める前に

この2つの記事(特に②)の内容を前提に書いていくので言葉がわかりにくいかもしれません。
【UnityShader】Shader自習メモ① - もりもりゲーム制作ブログ
【UnityShader】Shader自習メモ② - もりもりゲーム制作ブログ


用意するもの

   VisualStudio使いました。シンタックスハイライトが皆無に等しいので何使ってもいいと思います。
   噂ではVScodeでできるらしいっすよ(ものぐさ太郎)

  • Orbit.shader

   Projectウィンドウ内でCreate->UnlitShaderで名前を「Orbit」にする(別に何でもいい)

  • OrbitMaterial

   シェーダーをアタッチするマテリアル。なんでもいい。

考え方

"惑星軌道""惑星"を分解して考えます。

惑星軌道

ただの輪っかです。
黒い円の上に白い円を置けば黒いリングができます。

f:id:monimoni114514:20190511032221p:plain
リングができるまで

惑星

適当なサイズの黒い円を、さきほど作った惑星軌道に沿った円運動させればできます。

つまり

  • 黒い円
  • 白い円
  • 惑星用の黒い円
  • 円運動

があればOrbitが再現できます。

実装する

黒い円と白い円

まずは黒い円と白い円でリングを作ります。
frag関数の上にfloat ring()関数を記述して、この関数をfrag関数で呼び出し、黒いリングが描画されるようにします。

float4 ring(float2 st, float radius, float width)
{
	float d = distance(0.5, st);
	return -step(radius + width / 2, d) + step(radius - width / 2, d);
}

float dにあらかじめ中心からの距離を準備し、step関数で円を2つ描きます。
radius+width(半径+幅/2)の黒い円とradius-width(半径ー幅/2)の白い円です。
白い背景のピクセルで出力するのが1、
黒い円部分は白背景とあわせて0になればいいので-1、
白い円部分は白背景と黒背景とあわせて1になればいいので+1
みたいな出力をします。

ちなみになぜかこれを書いたころの自分は大きな円を白く、小さな円を黒くした上で、あとでその反転色を出力してます。なんで???

f:id:monimoni114514:20190512031346p:plain
出力する値とイメージ

黒い円と円運動

座標を入力するだけでそこに円を描画してくれる関数があれば、その関数に円運動の式を渡せばいい感じになるんじゃないか?という考えのもと作ります。
まずは入力した座標に円を描く関数circle()を作ります。

float circle(float2 st, float x, float y)
{
	float d = distance(float2(x, y), st);
	return step(0.1, d);
}

合体させる

先ほど作ったcircle関数を呼び出し円運動させて、ついでにさきほど作ったリングのサイズに合わせればいいわけです。
渡すx,yを式に表すと、
x=cos(t)*size
y=sin(t)*size

こうなります。
実際には、(0.5, 0.5)の座標を中心にしてほしいので0.5ずつ足すことに。

惑星と惑星軌道の準備ができたので、これをまとめて出力するorbit関数を作ります。

float orbit(float2 st, float speed, float size)
{
	float x = cos(_Time * speed) * size;
	float y = sin(_Time * speed) * size;

	return circle(st, x + 0.5, y + 0.5) * (1 - ring(st, size, 0.02));
}

先述したように当時ガバガバな記述をしていたのでring関数の出力を反転させるため1から引いてます。

呼び出す

呼び出します。はい。

fixed4 frag(v2f i) : SV_Target
{
	return orbit(i.uv, 50, 0.4);
}

だいたいこのくらいの値がちょうどいい感じになります。

完成!

完成したコードはこのURL先の
Assets->MyShaderの中にあるOrbit.shaderで確認できます。
GitHub - yoshikawaaan/WakuwakuShaderLearning: わくわくシェーダー勉強

たぶんもっといい感じに作る方法はいくらでもあると思うのでもうしばらく修行してきます・・・
つぎまでにセルラーノイズ、修行してきます。