【UnityShader】お絵かきシェーダー【2】 #54
前回の成果
お絵かきシェーダーを勉強した。
今回やること
引き続き、お絵かきシェーダーを学んでいきます。
事前準備
Scene上にQuadを配置します。
ソースコード
Shader "Unlit/Drawing_1" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" 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 i) : SV_Target { return float4(i.uv.x, i.uv.y, 0, 1); } ENDCG } } }
このソースのフラグメントシェーダー部分を変えていきます。
ハートを作る
ディスタンスフィールドの概念を利用し、ハートを作ってみようと思います。
ハートの数式
ハートの数式は以下となります。
ソースコードに反映
これを反映していきます。
fixed4 frag (v2f i) : SV_Target { float2 uv = i.uv; float heart; uv = (uv - float2(0.5, 0.38)) * float2(2.1, 2.8); heart = pow(uv.x, 2) + pow(uv.y - sqrt(abs(uv.x)), 2); heart = step(heart, 1); return heart; }
そうすると、ハートの形が描画されます。
ハートを動かす
次にハートを動かしていきます。
ソースコードを以下に変更します。
fixed4 frag (v2f i) : SV_Target { float2 uv = i.uv; float heart; uv = (uv - float2(0.5, 0.38)) * float2(2.1, 2.8); heart = pow(uv.x, 2) + pow(uv.y - sqrt(abs(uv.x)), 2); heart = step(heart, abs(sin(heart * 8 - _Time.w * 2))); return heart; }
stepをTimeで変化させることにより、ハートを動かすことができます。
step(heart, abs(sin(heart * 8 - _Time.w * 2)))
このstepでこのハートの動きになる理由は数式を分解して、グラフを見て頂けると理解しやすいです。
sin(x)のグラフ
sin(x * 8)のグラフ
abs(sin(x * 8))のグラフ
step(x, abs(sin(x * 8)))のグラフ
後はこの歯型のようなグラフがTimeによってこの形のまま移動するので、上のgifのような動きをします。
極座標
極座標とは、原点からの距離であると角度であるを使い点を表すものです。
普段よく使われるのような形は直交座標と呼ばれます。
極座標のグラフのイメージは以下となります。
極座標を使用するために、原点からの距離と角度を取得していきます。
花を描画する
極座標の考え方を利用して、花を描画します。
原点を変更する
極座標を使用して描画する際には、原点が中央にあると便利なので位置を変更します。
fixed4 frag (v2f i) : SV_Target { float2 uv = i.uv; uv = 0.5 - uv; float d = distance(0, uv); return d; }
0.5からuv値を減算することによって、原点を左下から中央へと変更させます。
これで、原点からの距離を取得できます。
結果
座標を角度に変換
角度を求めるためにatan2を使用します。
atan2は、点(0, 0)から点(x, y)までの半直線と、正のx軸の間の平面上での角度(ラジアン)を返すものです。
以下の画像を見ていただけるとイメージが掴みやすいと思います。
第一引数がyなのと、戻り値は~なので気をつけてください。
これを利用し、角度を求めます。
static const float PI = 3.14159265; fixed4 frag (v2f i) : SV_Target { float2 uv = i.uv; uv = 0.5 - uv; float a = atan2(uv.y, uv.x); a = (a + PI)/(PI * 2); return a; }
a = (a + PI)/(PI * 2);
上記の部分は、~を0~1に変換しています。
結果
花の数式を当てはめる
原点からの距離と角度を取得できたので数式を利用して花を描画していきます。
花の数式は以下となっています。
数式
y = abs(cos(x * 3))
グラフ
これを当てはめると以下のようになります。
ソースコード
fixed4 frag (v2f i) : SV_Target { float2 uv = i.uv; uv = 0.5 - uv; float a = atan2(uv.y, uv.x); float d = abs(cos(a * 3)); return d; }
結果
これは、それぞれの角度で原点からどれだけ離れているかを表しています。
つまり色が黒いと原点から近く、白いと原点から遠くなっています。
これを各ピクセルの原点からの距離をしきい値としてstepしてあげます。
fixed4 frag (v2f i) : SV_Target { float2 uv = i.uv; uv = 0.5 - uv; float a = atan2(uv.y, uv.x); // 花が大きく描画されすぎてしまうので*0.4で微調整 float d = abs(cos(a * 3)) * 0.4; d = step(length(uv), d); return d; }
結果
これで花の完成となります。
雪と桜の描画
極座標を利用し、花の他にも雪と桜が表現出来ます。
雪
数式
y = abs(cos(x * 12) * sin(x * 3)) * 0.4 + 0.1
グラフ
結果
雪
数式
y = min(abs(cos(x * 2.5)) + 0.4, abs(sin(x * 2.5)) + 1.1) * 0.32
グラフ
結果
最後に花と雪と桜のソースコードを添付しておきます。
ソースコード
Shader "Unlit/Drawing_5" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" 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 i) : SV_Target { // float2 uv = i.uv; // uv = 0.5 - uv; // float a = atan2(uv.y, uv.x); // float d = abs(cos(a * 3)) * 0.4; // d = step(length(uv), d); // return d; // } // 雪 // fixed4 frag (v2f i) : SV_Target { // float2 uv = i.uv; // uv = 0.5 - uv; // float a = atan2(uv.y, uv.x); // float d = abs(cos(a * 12) * sin(a * 3)) * 0.4 + 0.1; // d = step(length(uv), d); // return d; // } // 桜 fixed4 frag (v2f i) : SV_Target { float2 uv = i.uv; uv = 0.5 - uv; float a = atan2(uv.y, uv.x); float d = min(abs(cos(a * 2.5)) + 0.4, abs(sin(a * 2.5)) + 1.1) * 0.32; d = step(length(uv), d); return d; } ENDCG } } }
今回は以上となります。
ここまでご視聴ありがとうございました。