【UnityShader】お絵かきシェーダー【4】 #56
前回の成果
歪みを利用してお絵かきした。
今回やること
引き続き、お絵かきシェーダーを学んでいきます。
事前準備
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 } } }
このソースのフラグメントシェーダー部分を変えていきます。
繰り返し
画面に複数同じ表現を繰り返し行っていこうと思います。
表現方法
繰り返しは、frac関数を使用することで表現することが出来ます。
frac関数は、
// 小数値の小数部分を返す frac(x); // 0.5が返ってくる frac(1.5); // 0.1が返ってくる frac(0.1);
となっています。
まずはuv値をそのまま描画します。
ソースコード
fixed4 frag (v2f i) : SV_Target { float2 repet = frac(i.uv); return float4(repet.x, repet.y, 0, 1); }
結果
uv値が描画されましたが、もちろん繰り返しはされていません。
そこでfrac関数に渡すuv値の値を2倍にしてみます。
ソースコード
fixed4 frag (v2f i) : SV_Target { // uv値を2倍にする int num = 2; float2 repet = frac(i.uv * num); return float4(repet.x, repet.y, 0, 1); }
結果
これで繰り返すことが出来ました。
これでなぜ繰り返されているかの理由を説明します。
まず、uv値をnum倍(今回の場合は2倍)しているので、uv座標は(0.0, 0.0)〜(2.0, 2.0)となります。
そこで小数部分に注目して頂きたいのですが、小数部分は0〜9を2週することになります。
ですので、2回繰り返されて描画されることになります。
イメージ
上記の場合は2倍なので2回繰り返されていますが、10倍にすると10回繰り返されます。
ソースコード
fixed4 frag (v2f i) : SV_Target { // uv値を10倍にする int num = 10; float2 repet = frac(i.uv * num); return float4(repet.x, repet.y, 0, 1); }
結果
では次にuv値で描画するのではなく、四角形を描画してみます。
四角形を描画するbox関数を使用して、繰り返し描画します。
ソースコード
// 四角形を描画する float box (float2 uv, float size) { size = 0.5 + size * 0.5; uv = step(uv, size) * step(1.0 - uv, size); return uv.x * uv.y; } fixed4 frag (v2f i) : SV_Target { int num = 10; float2 repeat = frac(i.uv * num); return box(repeat, 0.7); }
結果
これもfrac関数を使用することによって繰り返し描画することが出来ました。
次に、アニメーションさせてみます。
ソースコード
// 四角形を描画する float box (float2 uv, float size) { size = 0.5 + size * 0.5; uv = step(uv, size) * step(1.0 - uv, size); return uv.x * uv.y; } fixed4 frag (v2f i) : SV_Target { int num = 10; float2 repeat = frac(i.uv * num); // sinカーブでアニメーション return box(repeat, abs(sin(_Time.y * 3))); }
結果
アニメーションさせることが出来たのですが、このままですと全ての四角形が同じ動きをしてしまっています。
これを以下のようにアニメーションにさせるためにどうするのかを説明します。
個別にアニメーション
個別に制御
floor関数
各四角形を制御するためには、floor関数を使用することで表現することが出来ます。
floor関数は、
// 小数値の整数部分を返す floor(x); // 1が返ってくる floor(1.5); // 0が返ってくる floor(0.1);
となっています。
このfloor関数によってどのように各四角形を制御しているのかの説明に移ります。
ソースコード
fixed4 frag (v2f i) : SV_Target { float num = 2; float2 repFrac = frac(i.uv * num); float2 repFloor = floor(i.uv * num); return float4(repFrac.x, repFrac.y, 0, 1); }
repFloor変数を使用していないのは意図的です。
結果
上記の結果になることは、frac関数での繰り返しですでに学んでいます。
そして、各uv値は以下のようになることもfrac関数で学びました。
各uv値
ではfloor関数で何が出来るのかというと各四角形でのxとyの値を取得することができます。
現在uv値は0.0~2.0となっています。
これをfloor関数の引数としていれると整数部分のみ取得するので、0と1と2となります。
これにより各四角形でのxとyの値が取れます。
floorのイメージ
アニメーションの組み合わせ
こちらのgifのようにアニメーションさせるためには、素材を2つ作る必要があります。
1つは先程制作したbox関数です。
もう1つは、下記のgifのブロックでアニメーションしているようなものになります。
このブロックアニメーションの明るさをbox関数のサイズとして使用します。
ブロックアニメーション
box関数はすでに制作済ですので、ブロックアニメーションを制作します。
まずは、中心座標からsinカーブさせます。
ソースコード
float wave (float2 uv) { // (0.5, 0.5)を中心座標に float dist = distance(0.5, uv); return(1 + sin(dist * 3 - _Time.y * 3)) * 0.5; } fixed4 frag (v2f i) : SV_Target { return wave(i.uv); }
グラフ
結果
次に、今滑らかにアニメーションしているものをブロック状にします。
ブロック状にする理由は、アニメーションの明るさを四角形のサイズとして使用するため、滑らかだと四角形のサイズがおかしくなってしまいます。
滑らかなアニメーションでの四角形
ブロック状にするには、格子状に区切りその中心点の色を格子で出来た四角形の色としてあげれば良いです。
イメージ
ソースコードに起こしていきます。
ソースコード
float wave (float2 uv, float num) { // 格子状で区切った座標の中心を自身のuvとする uv = (floor(uv * num) + 0.5) / num; // (0.5, 0.5)を中心座標に float dist = distance(0.5, uv); return(1 + sin(dist * 3 - _Time.y * 3)) * 0.5; } fixed4 frag (v2f i) : SV_Target { return wave(i.uv, num); }
結果
最後に組み合わせます。
ソースコード
// 四角形を描画する float box (float2 uv, float size) { size = 0.5 + size * 0.5; uv = step(uv, size) * step(1.0 - uv, size); return uv.x * uv.y; } // ブロック状の波アニメーション float wave (float2 uv, float num) { // 格子状で区切った座標の中心を自身のuvとする uv = (floor(uv * num) + 0.5) / num; // (0.5, 0.5)を中心座標に float dist = distance(0.5, uv); return(1 + sin(dist * 3 - _Time.y * 3)) * 0.5; } fixed4 frag (v2f i) : SV_Target { float num = 10; float2 repFrac = frac(i.uv * num); float size = wave(i.uv, num); return box(repFrac, size); }
結果
これで各四角形を個別にアニメーションさせることができました。
他のアニメーション
色をつける
ソースコード
// ブロックの波アニメーション float boxWave (float2 uv, float num) { float2 repFrac = frac(uv * num); float size = wave(uv, num); return box(repFrac, size); } fixed4 frag (v2f i) : SV_Target { return float4(boxWave(i.uv, 9), boxWave(i.uv, 18), boxWave(i.uv, 36), 1); }
結果
疑似乱数
ソースコード
// ブロックの疑似乱数 float boxRand (float2 uv, float num) { // 格子状で区切った座標の中心を自身のuvとする uv = (floor(uv * num) + 0.5) / num; // 疑似乱数 float offset = frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453) * 5; return(1 + sin( _Time.y * 3 + offset)) * 0.5; } fixed4 frag (v2f i) : SV_Target { float num = 10; float2 repFrac = frac(i.uv * num); float size = boxRand(i.uv, num); return box(repFrac, size); }
結果
今回は以上となります。
ここまでご視聴ありがとうございました。