【UnityShader】Imageのアウトラインを動かす 【2】#51
前回の成果
アウトラインの元となる部分を回転させた。
今回やること
Imageに合わせたアウトラインを回転させていきます。
事前準備
Scene上にImageを配置します。
Imageに画像と今回制作するマテリアルをアタッチします。
画像はなるべく単純な形のもののほうが綺麗にアウトラインができます。
Sceneのキャプチャ
使用した画像
ソースコード
Shader "Unlit/rotateOutline" { Properties { _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {} _BlurColor ("Blur Color", Color) = (1, 1, 1, 1) _BlurSize ("Blur Size", float) = 1 _Speed ("Speed", float) = 1 _Angle ("Angle", Range(0, 1)) = 1 _OffSet("xy : offset, zw : notUseing", Vector) = (0.5,0.5,0,0) } CGINCLUDE struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; half2 texcoord : TEXCOORD0; float4 worldPosition : TEXCOORD1; }; #include "UnityCG.cginc" sampler2D _MainTex; float4 _MainTex_TexelSize; fixed4 _BlurColor; float _BlurSize; half _Speed; half _Angle; fixed4 _OffSet; static const float PI = 3.14159265; v2f vert (appdata v) { v2f o; o.worldPosition = v.vertex; o.vertex = UnityObjectToClipPos(o.worldPosition); o.texcoord = v.texcoord; return o; } fixed4 frag(v2f v) : SV_Target { half4 color = (tex2D(_MainTex, v.texcoord)); return color; } fixed4 frag_blur (v2f v) : SV_Target { int k = 1; float2 blurSize = _BlurSize * _MainTex_TexelSize.xy; float blurAlpha = 0; float2 tempCoord = float2(0,0); float tempAlpha; for (int px = -k; px <= k; px++) { for (int py = -k; py <= k; py++) { tempCoord = v.texcoord; tempCoord.x += px * blurSize.x; tempCoord.y += py * blurSize.y; tempAlpha = tex2D(_MainTex, tempCoord).a; blurAlpha += tempAlpha; } } half4 blurColor = _BlurColor; half timeAngle = _Time.y * _Speed; half2x2 rotate = half2x2(cos(timeAngle), -sin(timeAngle), sin(timeAngle), cos(timeAngle)); float2 offsetUv = v.texcoord - _OffSet.xy; offsetUv = mul(rotate, offsetUv); half uvAngle = atan2(offsetUv.y, offsetUv.x); half tolerance = (-_Angle + 0.5) * 2 * PI; int angleStep = step(uvAngle, tolerance); offsetUv *= (offsetUv.xy * angleStep); offsetUv = step(offsetUv, 0); offsetUv = offsetUv * step(0.001, blurAlpha); blurColor.a *= offsetUv; return blurColor; } ENDCG SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Cull Off Lighting Off ZWrite Off ZTest [unity_GUIZTestMode] Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag_blur ENDCG } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } }
前回のソースコードに数行加えました。
Imageを描画
前回の結果
前回のままですと、Imageがそもそも描画されないので、描画します。
fixed4 frag(v2f v) : SV_Target { half4 color = (tex2D(_MainTex, v.texcoord)); return color; } // 省略 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG }
ここの部分に関しては特に説明は不要かと思います。
Imageを描画した結果
Imageのアウトラインに添わせる
今のままですと、Imageのテクスチャに関係なく描画されてしまっているので、アウトラインにします。
offsetUv = offsetUv * step(0.001, blurAlpha);
前回の記事で計算したアルファ値が0以上なら描画するようにstepをしています。
こうすることによりアウトラインに添うようになります。
step
引数に応じて0.0か1.0を返す関数です。
// y >= x なら1.0を、x > y なら0.0を返す step(x, y) // 1.0が返ってくる step(0, 1) // 0.0が返ってくる step(0.5, 0.1)
一定の角度を描画する
アウトラインの描画が出来ました。
しかし今のままですと、角度が必ず180度になってしまっているので変更出来るようにします。
角度の計算
half uvAngle = atan2(offsetUv.y, offsetUv.x); half tolerance = (-_Angle + 0.5) * 2 * PI; int angleStep = step(uvAngle, tolerance); offsetUv *= (offsetUv.xy * angleStep);
角度を調整しているのは上記の部分です。
まず以下の部分で自身の角度を出しています。
half uvAngle = atan2(offsetUv.y, offsetUv.x);
atan2
atan2は、点(0, 0)から点(x, y)までの半直線と、正のx軸の間の平面上での角度(ラジアン)を返すものです。
以下の画像を見ていただけるとイメージが掴みやすいと思います。
第一引数がyなのと、戻り値は~なので気をつけてください。
自身の角度を出した後は、以下で許容する角度を出してstepしています。
half tolerance = (-_Angle + 0.5) * 2 * PI; int angleStep = step(uvAngle, tolerance); offsetUv *= (offsetUv.xy * angleStep);
自分は_Angleを0~1の間に収めたかったのでこうしています。
結果
角度が調整できれば成功です。
描画結果
プロパティ
ソースコードにコメントを付与
Shader "Unlit/rotateOutline" { Properties { _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {} _BlurColor ("Blur Color", Color) = (1, 1, 1, 1) _BlurSize ("Blur Size", float) = 1 _Speed ("Speed", float) = 1 _Angle ("Angle", Range(0, 1)) = 1 _OffSet("xy : offset, zw : notUseing", Vector) = (0.5,0.5,0,0) } CGINCLUDE // passで共通処理をまとめる struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; half2 texcoord : TEXCOORD0; float4 worldPosition : TEXCOORD1; }; #include "UnityCG.cginc" sampler2D _MainTex; // MainTexのサイズを扱う // x : 1.0 / width // y : 1.0 / height // z : width // w : height float4 _MainTex_TexelSize; fixed4 _BlurColor; float _BlurSize; half _Speed; half _Angle; fixed4 _OffSet; static const float PI = 3.14159265; v2f vert (appdata v) { v2f o; o.worldPosition = v.vertex; o.vertex = UnityObjectToClipPos(o.worldPosition); o.texcoord = v.texcoord; return o; } fixed4 frag(v2f v) : SV_Target { half4 color = (tex2D(_MainTex, v.texcoord)); return color; } fixed4 frag_blur (v2f v) : SV_Target { // -1 ~ 1でforを回す int k = 1; float2 blurSize = _BlurSize * _MainTex_TexelSize.xy; float blurAlpha = 0; float2 tempCoord = float2(0,0); float tempAlpha; // xy方向にblurSize分ズラし、アルファ値を計算 for (int px = -k; px <= k; px++) { for (int py = -k; py <= k; py++) { tempCoord = v.texcoord; tempCoord.x += px * blurSize.x; tempCoord.y += py * blurSize.y; tempAlpha = tex2D(_MainTex, tempCoord).a; blurAlpha += tempAlpha; } } half4 blurColor = _BlurColor; half timeAngle = _Time.y * _Speed; // 回転座標 half2x2 rotate = half2x2(cos(timeAngle), -sin(timeAngle), sin(timeAngle), cos(timeAngle)); // 回転の中心座標 float2 offsetUv = v.texcoord - _OffSet.xy; // timeAngle回転させたときの座標 offsetUv = mul(rotate, offsetUv); // 自身の角度 half uvAngle = atan2(offsetUv.y, offsetUv.x); // 許容する角度(0~1) half tolerance = (-_Angle + 0.5) * 2 * PI; int angleStep = step(uvAngle, tolerance); offsetUv *= (offsetUv.xy * angleStep); // アルファ値を0or1に offsetUv = step(offsetUv, 0); offsetUv = offsetUv * step(0.001, blurAlpha); blurColor.a *= offsetUv; return blurColor; } ENDCG SubShader { Tags { "Queue"="Transparent" // プロジェクターの影響を受けないように "IgnoreProjector"="True" "RenderType"="Transparent" // マテリアルのincpecterでの表示をplaneに "PreviewType"="Plane" // spriteに不具合がある場合にfalseになる "CanUseSpriteAtlas"="True" } Cull Off Lighting Off ZWrite Off // CanvasのRenderModeによって動的に変化 // Screen Space - Overlay : Always // Screen Space - Camera : LEqual // World Space : LEqual ZTest [unity_GUIZTestMode] Blend SrcAlpha OneMinusSrcAlpha // アウトラインの描画 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag_blur ENDCG } // テクスチャの描画 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } }
今回は以上となります。
ここまでご視聴ありがとうございました。