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

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

【UnityShader】Shader自習メモ②

あらすじ

シェーダの中でもより楽そうなUnlitShaderを選び、さらにその中でも楽しそうなfrag関数でShader勉強することにした。
前回:
meganeunity.hateblo.jp


frag関数とは

3Dモデルの頂点の集まりでできた欠片(フラグメント)の関数。

fixed4 frag(v2f i) : SV_Target
{
	return ???;
}

なんのこっちゃって感じですね。
1つずつ確認していきましょう。

  • fixed4型の戻り値

fixed4というのは4つのf少数値が入る配列です。
特にここではfixed4を色情報(RGBA)の格納に使います。
float4も大体おなじっぽい?

  • v2f型の引数

頂点関数から受け取った。
入力を意味するinputからiと命名されてるのでは?(推測)
INという記述も結構見ます。

  • SV_Target

ピクセルのカラーがなんちゃら敵なセマンティクス
よくわからない。触らない方がよさそう。

  • return ???

ここで返す値(fixed4型)がフラグメントの色になる。

とりあえず
float4 frag(v2f i) : SV_Target
の形は崩さない方がベター。
{}の中身が最も重要。

値の返し方

まずは何が返せるのか把握してから・・・

とりあえず0とか1返す

なにをしたらいいのかわからないのでとりあえず0をreturnしてみる。
このシェーダを適当なマテリアルに割り当てて、Planeで確認。

float4 frag(v2f i) : SV_Target
{
	return 0;
}

f:id:monimoni114514:20190410080733p:plain
黒塗りのPlane
ウオ!
黒くなった・・・

じゃあ1にすると・・・

float4 frag(v2f i) : SV_Target
{
	return 1;
}

f:id:monimoni114514:20190410081416p:plain
ホワイトPlaneくん
白くなる・・・

色々試してみると、
・0以下は真っ黒になる
・1以上は白くなる
・0より大きく1未満の値はグレースケール
という結果になりました。
1つの値をreturnするとグレースケールで値が表現されるっぽい。

f:id:monimoni114514:20190410082155p:plain
グレースケール

float4で返してみる。

そもそもfloat4型を戻り値としているんだからfloat4で返してもみる。
RGBA値を使うから赤でやってみる。
赤はRGBA値で R(赤色)が1,A(透明度)が1,それ以外が0なので、 (1,0,0,1)と表現します。

float4 frag(v2f i) : SV_Target
{
	float4 c = float4(1,0,0,1);
	return c;
}

f:id:monimoni114514:20190410082945p:plain
真っ赤に染まったPlane

設定したRGBA値で表現される色がそのまま塗られるらしい。
ただ、A(透明度)はなにを設定しても変化がない・・・?とりあえず1にしておけばよさそう。

float2は・・・?

floatとfloat4でできるならfloat2でもやってみる。

float4 frag(v2f i) : SV_Target
{
	float2 c = float2(1,1);
	return c;
}

f:id:monimoni114514:20190410084328p:plain
ピンクに壊れたPlane
あ、ダメだこれ。
ピンクだし。エラー出てるし。
(マイクラのMOD作ってるとき思い出した。)

値の返し方まとめ

・float可
 ・0~1↑の値
・float4可
 ・Aは適用外・・・?
・それ以外の型は不可
 ・ピンクになっちゃう

使えそうな値

キーワード的な変数があるらしい。
Unity公式リファレンスのやつ。
ビルトインのシェーダー変数 - Unity マニュアル

よくわかんないのが多いけど_Timeとかは使いやすそう。

使えそうな関数

あらかじめ用意された関数がいくつかある。
HLSLの関数まとめのやつ。
組み込み関数 (DirectX HLSL) | Microsoft Docs

distance(x, y)・・・xとyの距離を返す
step(a,x)・・・グラフのステップ xがaより大きければ1、a未満なら0を返す
abs(x)・・・xの絶対値を返す
三角関数・・・いっぱいある。いっぱいつかう
frac(x)・・・xの小数点部分だけ返す
floor(x)・・・x以下かつ最大の整数を返す

ほかにもいっぱいある。
こういうのを組み合わせてShaderを作るらしい。

distance関数やってみる

2点間の距離を求める関数。
とりあえず0と1いれる

float4 frag(v2f i) : SV_Target
{
	float dis = distance(0, 1);
	return dis;
}

f:id:monimoni114514:20190410081416p:plain
そらそうだろPlane
0地点と1地点の距離の長さって、そりゃいつでも1だから白が描画されるわ・・・。

モデル上の場所によって色を変えたいから、引数の値を代入してみる。
引数 i はfloatではないので i 内部の uv を1の代わりに使う。

float4 frag(v2f i) : SV_Target
{
	float dis = distance(0, i.uv);
	return dis;
}

f:id:monimoni114514:20190410102707p:plain
左下からグラデーションされたPlane

fragが返した値が0 👉 黒くなる
fragが返した値が中間👉グレー
fragが返した値が1 👉 白くなる
この画像だと
・左下が黒い👉左下で返した値が0
・右上、右下、左上が白い👉3点で返した値が1

ということは・・・・・?

i.uvはモデル上の座標(?)を表しているらしい。

i.uvのパラメータxとyが何を表しているか確かめるため、RGB値のRedとBlueにそれぞれ割り振ります。

float4 frag(v2f i) : SV_Target
{
	float disX = distance(0, i.uv.x);
	float disY = distance(0, i.uv.y);
	return float4(disX,0,disY,1);
}

f:id:monimoni114514:20190410125358p:plain
RedとBlueでグラデーションされるPlane
RedにxをBlueにyをぞれぞれ割り振っているので、xが1に近い座標ほど赤く、yが1に近い座標ほど青くなってます。
f:id:monimoni114514:20190410125653p:plain
こういう感じ
左下が(0,0)で右上はどうも(1,1)だそうです。
0~1の値で表現されてるのは幅高も一緒。


そういえば、
なにげなくdistance(0,1)とかやってたけど、
勝手にdistance( float2(0,0), float(1,1) )に変換されてるみたいですね。

distanceの使い道
  • 二点間の距離をだすもの
  • グラデーションとしても使えそう。

step関数やってみる

xが変位する中で、aを超えたらf(x)が1になる関数をstepというらしい。

いや、stepってなんのことだよ・・・・・???
いろんなサイトでグラフを出して説明してるのが関係ありそう。

こういうの

f:id:monimoni114514:20190410100555p:plain
stepをグラフにしたやつ

たぶん、赤い線が段差になってるからステップなんだと思う。
いろんな方が言及してるけどif文より早いらしい。というかif文の代わり。

float4 frag(v2f i) : SV_Target
{
	float st = step(0.1, i.uv.x);
	return st;
}

f:id:monimoni114514:20190410101606p:plain
10%が0(黒)、90%が1(白)で分けられたPlane

stepの使い道
  • if文の代わり
  • 二階調にする

基礎中の基礎らしい。

四則演算して返してみる

distance関数とstep関数はなんとなくわかったので、つぎは四則演算したらどうなるのかやってみる。
distanceとstepをさきほどと同じ値で呼んで、四則演算したものをfrag関数の戻り値にする。

足し算を返す
fixed4 frag(v2f i) : SV_Target
{
	float dis = distance(0, i.uv);
	float st = step(0.1,i.uv.x)
	return dis + st;
}

f:id:monimoni114514:20190412071328p:plain
distance + step なPlane

はじっこだけグラデーションされた。

つまりどういうこと・・・・・?
計算してみましょう。

frag関数の戻り値と描画は
・白描画 → 1以上
・グレー描画 → 0~1
・黒描画 → 0以下
です。

distance関数の値は左下から
・値が0~1へと変化
step関数の値は、左から
・10%が値0
・90%が値1

となっています。
この二つを足した値から戻り値は
・10%が戻り値0+0~1👉0から1
・90%が戻り値1+0~1👉1以上
になるのでこんな描画になるみたいです。

f:id:monimoni114514:20190412073425p:plain
数値化

distanceやstepは必ずゼロか正の値を返すはずなので、
1+distansceのように戻り値を設定すると必ずすべてが白い描画になります。

引き算を返す

stepもdistanceも0~1の値なので、
1 - dinstanceのように値を返すと反転した描画になります。

f:id:monimoni114514:20190412080535p:plain
1 - distanceで反転Plane

distanceやstep自体にマイナス符号をつけるとー1~0の値になるので黒のみが描画される。

それぞれを引くと大体-1~1で描画されるので黒以外も描画される。
順番でだいぶ意味が変わってくる。

f:id:monimoni114514:20190412075344p:plain
distance - stepなPlane
f:id:monimoni114514:20190412075526p:plain
-distance + stepなPlane

1-( step - distance )を返してもまた変わってくるので難しい・・・。

掛け算を返す

これが一番使うことが増えそう?な気がする。

いろんな描画関数を自作したとき、その全部を同時に描画するときは単純に積でいいと思う。

たとえばOrbit(惑星とその軌道)を表現するときは
”黒い輪を描画する関数”

”ぐるぐる回る円を描画する関数”
の積を戻り値にすればいい。

f:id:monimoni114514:20190412082133p:plain
step * distance なPlane

割り算を返す

ん?割り算ってことはゼロ除算が起きてエラーになるんじゃ・・・

f:id:monimoni114514:20190412082759p:plain
distance / stepなPlane
f:id:monimoni114514:20190412082657p:plain
step / distance なPlane
オイオイオイ
できたわアイツ

いや、なんで?
調べたけどゼロ除算の数学的話しか出てこなかった・・・。

1/0とか1/distanceとか、あきらかにゼロ除算が発生するように戻り値を設定すると警告文は出てきました。

f:id:monimoni114514:20190412084132p:plain
怒られた。
さすがに許されないみたいです。

別のシェーダ作ってるときfunc( i.uv.x / 2 )でreturnしたらうまくいかなかったんですが、func( i.uv.x * 0.5 )でreturnするのは大丈夫だったので、今はなるべく割り算は使わない方がよさそうです。要チェックや!

これからがおもしろい!

disntanceとstepでながったらしく説明してきましたが、
次回からは三角関数などを使って動くシェーダを作っていきたいと思います。

次回:まだ

参考にさせていただいたサイト・スライド

・「楽しい!Unityシェーダー お絵描き入門! 」
@setchiさんのスライド
ガチガチの初心者が始めるべき順番、知識量を適切に教えてくれる。受け身で始めるならこっち。
docs.google.com


・おもちゃラボさん
調べたいことに対しての情報が良い感じに載っていてスゴイ。自発的に始めるならこっち。
nn-hokuson.hatenablog.com