Unityシェーダーお絵描き入門
https://gyazo.com/8644661d3b9a6d0f62913131577b0ff8
2019/5/12~
◆ 目次
1. 関連リンク
2. ディスタンスフィールド
3. 極座標
4. 歪み
5. 繰り返し
6. 擬似乱数
https://gyazo.com/ea38e8d40023a4d62a667463b859b1d4
1. 参考リンク
https://gyazo.com/ea38e8d40023a4d62a667463b859b1d4
2. ディスタンスフィールド
◆ distance関数
aとbの距離を返す関数
code:shader.cs
distance(a, b)
使用例
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = distance(float2(0.5, 0.5), i.uv)
}
https://gyazo.com/06b128f49251af2f351a29d047067bdc
◆ step関数
xがa以上なら1, aより小さければ0を返す
code:shader.cs
step(a, x)
図示
y = step(0.5, x)
https://gyazo.com/30723eb361d7c3c383cbb5018e8e06c0
◆ 中心から円状に塗り分ける
▼ 結果
https://gyazo.com/e361ad8972e2d5fa89e3263165c2a319
▼ frag関数
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = distance(float2(0.5, 0.5), i.uv);
float a = 0.4; // 閾値
return step(a, d);
}
▼ シェーダー全体
code:shader.cs
Shader "Unlit/DistanceField"
{
Properties
{
_MainTex ("Sprite Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
CGPROGRAM
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f_img i) : SV_Target
{
float d = distance(float2(0.5, 0.5), i.uv);
float a = 0.1; // 閾値
return step(a, d);
}
ENDCG
}
}
}
◆ 時間経過でアニメーションさせる
https://gyazo.com/c7aefedb8e18151c0d520f5e49a28e37
◆ 円で遊ぶ
▼ 等間隔の円
https://gyazo.com/2cc273ed18ddfb11a8b1c502609e9e97
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = distance(float2(0.5, 0.5), i.uv);
d = abs(sin(d * 50));
return step(0.5, d);
}
▼ アニメーションさせる
その1
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = distance(float2(0.5, 0.5), i.uv);
d = abs(sin(d * 50 - _Time.y * 10));
return step(0.5, d);
}
https://gyazo.com/733b3eb6e1a5437b65db75f00bf4d75f
その2
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = distance(float2(0.5, 0.5), i.uv);
d = abs(sin(d * 50));
d = abs(sin(_Time.y)) * d;
return step(0.5, d);
}
https://gyazo.com/7e4fbe91cfb9850e7d5b3b9c72c30ae7
◆ ハート
▼ 数式
https://gyazo.com/10ffaf4d382ce9668ad948cd9bfee26f
$ x^2+(y−\sqrt{|x|})^2=1
▼ 通常ハート
表示
https://gyazo.com/a5a76b2d195676f628fc8d179e9f15b1
プログラム
code:shader.cs
float heart(float2 st)
{
// 大きさや位置の調整
st = (st - float2(0.5, 0.5)) * 10;
// pow(x, y)はxのy乗
return pow(st.x, 2) + pow(st.y-sqrt(abs(st.x)), 2);
}
fixed4 frag (v2f_img i) : SV_Target
{
return heart(i.uv);
}
大きさや位置の調整がなにやってるのかあんまりわからなかったなあ
調整前はこれ
https://gyazo.com/3a0efb3e8c9f134e5f32430f455537f1
原点の位置をfloat(0.5, 0.5)の中心に移動させてるのはわかったけど、そこに数をかけるとハートの範囲が小さくなるのはなんでかわからなかった
あとマイナスすると左移動すると思ったのだけど右移動してたのもなぞ
https://gyazo.com/7d7a9aa2e30220a934f32aeb8d4c55c2
▼ 境界線をはっきり
step関数を使う
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = heart(i.uv);
return step(0.5, d);
}
https://gyazo.com/a5e124141b7457dbb4df11dca3c5aeb2
▼ アニメーション
その1
https://gyazo.com/c17b0f371faf403a977050ec3ff6dc7b
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = heart(i.uv);
d = sin(abs(d - _Time.y * 10));
return step(0.5, d);
}
その2
https://gyazo.com/f3ef295c882558e179888afa137c7806
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float d = heart(i.uv);
return step(d, abs(pow(sin(_Time.y*5), 4) + 0.3));
}
その3
https://gyazo.com/41fd82d01eb236f6bec404a689171647
code:shader.cs
float heart(float2 st)
{
// 大きさや位置の調整
st = (st - float2(0.5, 0.38)) * 3;
// pow(x, y)はxのy乗
return pow(st.x, 2) + pow(st.y-sqrt(abs(st.x)), 2);
}
fixed4 frag (v2f_img i) : SV_Target
{
float d = heart(i.uv);
return step(d, abs(sin(d * 8 - _Time.w * 2)));
}
▼ 通常のテクスチャ上に書く
透過部分はBlendingとTagsで設定
黒い部分は黒のまま、白表示だった部分はテクスチャの色を出力する
code:shader.cs
Shader "Unlit/DistanceHeart"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags {"Queue"="Transparent" "RenderType"="Transparent"}
LOD 100
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
float heart(float2 st)
{
// 大きさや位置の調整
st = (st - float2(0.5, 0.3)) * 15;
// pow(x, y)はxのy乗
return pow(st.x, 2) + pow(st.y-sqrt(abs(st.x)), 2);
}
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float d = heart(i.uv);
d = step(abs(pow(sin(_Time.y*5), 4) + 0.3), d);
if(d >= 0.5) {
return col;
} else {
col.rgb = col * d;
return col;
}
}
ENDCG
}
}
}
https://gyazo.com/443422b2f8dba4f7bcf073fb0a4581c4
https://gyazo.com/ea38e8d40023a4d62a667463b859b1d4
3. 極座標
◆ 極座標とは
https://gyazo.com/a80b219917a6e9a782ae7b7004eaf517
https://gyazo.com/8c34e10e78c02bd3286ab7112851d2a0
https://gyazo.com/cba340df9bb508ede4c9a0dba744426c
https://gyazo.com/4b8489c31a764e650b982ba2e259ccf5
◆ 桜を書く
▼ 準備
原点を中心に持ってくる
uv座標は左下が原点なので、0.5ずつ
https://gyazo.com/5882c079115f7f95bca0ef6693cf3d6c
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 st = 0.5 - i.uv;
return distance(0, st);
}
アークタンジェント
回転するほど1に近づく
https://gyazo.com/dac990e42b5d800ea31b43bbeb5c2813
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 st = 0.5 - i.uv;
float a = atan2(st.y, st.x);
// atan2 の戻り値は -PI ~ PI の範囲なので
// 正規化して色として出力
return (a + PI) / (PI * 2);
}
▼ 桜を描く
数式をそのまま入れる
https://gyazo.com/fd207f5cd79c1bb4301e7f21005ae2b1
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 st = 0.5 - i.uv;
float a = atan2(st.y, st.x);
float d = min(abs(cos(a * 2.5)) + 0.4, abs(sin (a * 2.5)) + 1.1) * 0.32;
return d;
}
桜の花びらの境目が、原点からどのぐらいの距離にあるかっていうのを表してる値が返ってくる
https://gyazo.com/77a9f54c43e68d4104ccd33dcab2dbca
各ピクセルの原点からの距離を、この色の値を閾値として色を塗り分ける
https://gyazo.com/182741194d97addf987f32551ae5f115
lengthはベクトルの長さを表す組み込み関数
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 st = 0.5 - i.uv;
float a = atan2(st.y, st.x);
float d = min(abs(cos(a * 2.5)) + 0.4, abs(sin (a * 2.5)) + 1.1) * 0.32;
float r = length(st);
return step(r, d);
}
▼ 色々遊ぶ
回転
色を操作できるようにする
code:shader.cs
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
回転
ついでにサイズをちょっと小さめに
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 st = 0.5 - i.uv;
float a = atan2(st.y, st.x);
a = a + _Time.y;
float d = min(abs(cos(a * 2.5)) + 0.4, abs(sin(a * 2.5)) + 1.1) * 0.1;
float r = length(st);
float b = step(r, d);
if(b < 0.5) {
return col;
} else {
col.rgb = _Color;
return col;
}
}
https://gyazo.com/9e8cb5fa9de8c919b9ec9423e380506f
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 st = 0.5 - i.uv;
float a = atan2(st.y, st.x);
a = a + _Time.y;
float size = sin(_Time.y*2) * 0.1;
float d = min(abs(cos(a * 2.5)) + 0.4, abs(sin(a * 2.5)) + 1.1) * size;
float r = length(st);
float b = step(r, d);
if(b < 0.5) {
return col;
} else {
col.rgb = _Color;
return col;
}
}
https://gyazo.com/40d41a3e00910eea4721a8110f0143b7
https://gyazo.com/ea38e8d40023a4d62a667463b859b1d4
4. 歪み
◆ 歪みの作り方
▼ 通常の円
code:shader.cs
float circle(float2 st)
{
return step(0.3, distance(0.5, st));
}
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float d = circle(i.uv);
if(d > 0.5)
{
return col;
} else
{
return _Color;
}
}
https://gyazo.com/5556753345e1567e7873afc72ff59b97
▼ 歪んだ座標を使用
y 座標の値に基づいて、x 座標を左右にずらした座標を circle 関数に渡して絵を描く
y 座標を 20倍に拡大した値をサインカーブに変換し、
そのままだと歪みが大きすぎるので適当に 0.05 をかけて歪みを調整したものをx 座標にオフセットとして追加
結果左右に歪んだ円になる
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
i.uv.x += sin(i.uv.y*20)*0.05;
float d = circle(i.uv);
if(d > 0.5)
{
return col;
} else
{
return _Color;
}
}
https://gyazo.com/8e20413ef9ae1c2ed2a410168c24ea3f
▼ 歪みを動かす
code:shader.cs
float circle(float2 st)
{
return step(0.3, distance(0.5, st));
}
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
// 歪みの計算
float x = 2 * i.uv.y + sin(_Time.y * 5);
float distort = sin(_Time.y * 10) * 0.1 *
sin(5 * x) * (- (x - 1) * (x - 1) + 1);
// 座標を歪ませる
i.uv.x += distort;
// RGB ごとに少しずつ座標をずらす
if(circle(i.uv) >= 0.5) {
return col;
} else
{
return float4(
circle(i.uv - float2(0, distort) * 0.3),
circle(i.uv + float2(0, distort) * 0.3),
circle(i.uv + float2(distort, 0) * 0.3),
1
);
}
}
https://gyazo.com/59fa19551532be5f75527249fcc2ec04
https://gyazo.com/ea38e8d40023a4d62a667463b859b1d4
5. 繰り返し
◆ 繰り返しの作り方
xの少数部分を返す
code:shader.cs
frac(x)
// frac(3.14) → 0.14
// frac(float2(3.14, 6.28)) → float2(0.14, 0.28)
ただしこのまま使っても繰り返しにはならない
uv座標が0.0~1.0の間の数値なため
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float2 st = frac(i.uv);
return float4(st.x, st.y, 0, 1);
}
よって座標に数値をかけると(今回の場合は2)繰り返した絵になる
整数部分が増えると、少数部分は0からまた開始され繰り返されるため
nを大きくするほどマス目が増える
このマスごとに絵を描くことができる
code:shader.cs
fixed4 frag (v2f_img i) : SV_Target
{
float n = 2;
float2 st = frac(i.uv*n);
return float4(st.x, st.y, 0, 1);
}
https://gyazo.com/e36896f9d5ebd91c26a050dac5df1af4
▼ 指定したサイズのボックス
code:shader.cs
// 指定した大きさの四角を描く関数
float box(float2 st, float size)
{
size = 0.5 + size * 0.5;
// step(st, size)で左と上の辺を、step(1.0-st, size)で右と下の辺を書いている
st = step(st, size) * step(1.0 - st, size);
return st.x * st.y;
}
float4 frag(v2f_img i) : SV_Target
{
float n = 10;
float2 st = frac(i.uv * n);
return box(st, 0.8);
}
https://gyazo.com/06b4ceba7e59f0e2a6fda6dd44aab411
▼ アニメーション
その1
code:shader.cs
float box(float2 st, float size)
{
size = 0.5 + size * 0.5;
st = step(st, size) * step(1.0 - st, size);
return st.x * st.y;
}
float4 frag(v2f_img i) : SV_Target
{
float n = 10;
float2 st = frac(i.uv * n);
return box(st, abs(sin(_Time.y * 3)));
}
https://gyazo.com/71a0d871717288e121e54896ee5f0821
▼ 各マスを個別に制御
x以下の最大の整数を返す
code:shader.cs
floor(x)
//floor(3.14) → 3.0
//floor(float2(3.14, 6.28)) → float2(3.0, 6.0)
各マスを個別に制御することができる
code:shader.cs
float4 frag(v2f_img i) : SV_Target
{
float n = 2;
float2 fst = frac(i.uv * n);
float2 ist = floor(i.uv * n);
return float4(fst.x, fst.y, 0, 1);
}
https://gyazo.com/71472dcd634d8db834a6db6d3f74fce5
まずはシンプルなアニメーションを作る
code:shader.cs
float wave(float2 st)
{
float d = distance(0.5, st);
return (1 + sin(d * 3 -_Time.y * 3)) * 0.5;
}
float4 frag(v2f_img i) : SV_Target
{
return wave(i.uv);
}
https://gyazo.com/f069985579e3146158a1810222d59858
これを以下の式で10x10のモザイク状に色が分かれた絵にする
code:shader.cs
float n = 10;
float2 st = (floor(i.uv * n) + 0.5) / n;
https://gyazo.com/8608604b5b035391474dd30c70e091bf
シェーダに組み込む
code:shader.cs
float wave(float2 st, float n)
{
st = (floor(st * n) + 0.5) / n;
float d = distance(0.5, st);
return (1 + sin(d * 3 -_Time.y * 3)) * 0.5;
}
https://gyazo.com/a2082c485170a6295a64885932e47f85
このwave関数の値が四角形のサイズになるようにすれば完成
code:shader.cs
float box(float2 st, float size)
{
size = 0.5 + size * 0.5;
st = step(st, size) * step(1.0 - st, size);
return st.x * st.y;
}
float wave(float2 st, float n)
{
st = (floor(st * n) + 0.5) / n;
float d = distance(0.5, st);
return (1 + sin(d * 3 -_Time.y * 3)) * 0.5;
}
float4 frag(v2f_img i) : SV_Target
{
float n = 10;
float2 st = frac(i.uv * n);
float size = wave(i.uv, n);
return box(st, size);
}
https://gyazo.com/62524dab01840cc29066c1cfd14cfe75
RGBごとに箱のサイズを変える
code:shader.cs
float box_wave(float2 uv, float n)
{
float2 st = frac(uv * n);
float size = wave(uv, n);
return box(st, size);
}
float4 frag(v2f_img i) : SV_Target
{
return float4(box_wave(i.uv, 9), box_wave(i.uv, 18), box_wave(i.uv, 36), 1);
}
https://gyazo.com/05385402434118baf0a4ceddf6651d14
https://gyazo.com/8644661d3b9a6d0f62913131577b0ff8
https://gyazo.com/ea38e8d40023a4d62a667463b859b1d4
6. 擬似乱数
◆ シェーダ用擬似乱数
受け取った座標を元に良い感じの乱数を作ってくれるらしい
code:shader.cs
float rand(float2 st)
{
return frac(sin(dot(st, float2(12.9898, 78.233))) * 43758.5453);
}
◆ ランダム性なしに動かす
code:shader.cs
float box(float2 st, float size)
{
size = 0.5 + size * 0.5;
st = step(st, size) * step(1.0 - st, size);
return st.x * st.y;
}
float box_size(float2 st, float n)
{
st = (floor(st * n) + 0.5) / n;
return (1 + sin(_Time.y * 3)) * 0.5;
}
float4 frag(v2f_img i) : SV_Target
{
float n = 10;
float2 st = frac(i.uv * n);
float size = box_size(i.uv, n);
return box(st, size);
}
https://gyazo.com/92cb865f5f26e90aefe7ba58c893e47f
◆ ランダム性を追加
code:shader.cs
float rand(float2 st)
{
return frac(sin(dot(st, float2(12.9898, 78.233))) * 43758.5453);
}
float box(float2 st, float size)
{
size = 0.5 + size * 0.5;
st = step(st, size) * step(1.0 - st, size);
return st.x * st.y;
}
float box_size(float2 st, float n)
{
st = (floor(st * n) + 0.5) / n;
float offs = rand(st) * 5;
return (1 + sin(_Time.y * 3 + offs)) * 0.5;
}
float4 frag(v2f_img i) : SV_Target
{
float n = 10;
float2 st = frac(i.uv * n);
float size = box_size(i.uv, n);
return box(st, size);
}
https://gyazo.com/4b70dfdca1ca4728279e41dac705e458