【UnityShader】Imageのアウトラインを動かす 【1】#50
前回の成果
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); offsetUv = step(offsetUv, 0); 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 } } }
回転させる
half2x2 rotate = half2x2(cos(timeAngle), -sin(timeAngle), sin(timeAngle), cos(timeAngle)); float2 offsetUv = v.texcoord - _OffSet.xy; offsetUv = mul(rotate, offsetUv);
まずはImageを考慮せずに、回転させていきたいと思います。
回転行列
回転行列(かいてんぎょうれつ、英: rotation matrix)とは、ユークリッド空間内における原点中心の回転変換の表現行列のことである。
回転行列 - Wikipedia:より引用
ベクトルと乗算することにより原点を中心として回転させることができる行列の認識で問題ないです。
公式は以下になります。
どうしてこの式で求めることが出来るのか解説します。
極座標
行いたいこと
今回やりたいことは、上記の図のように
点を回転させて点 (画像では)にすることです。
そのためには、加法定理を知る必要があるのですがあります。
しかし、その加法定理を理解するには極座標を知る必要があります。
極座標とは
極座標とは、原点からの距離であると角度であるを使い点を表すものです。
上記の図の場合、点は普段と表します。
これを直交座標と呼びます。
極座標の場合、点はと表します。
変換
同じ点が直交座標では、極座標ではと表されている場合、三角関数より
が成り立ちます。
加法定理
極座標を理解したので、加法定理に移ります。
加法定理とは
加法定理(かほうていり、英: addition theorem)、加法法則(かほうほうそく、英: addition law/rule)あるいは加法公式(かほうこうしき、英: addition formula)とは、ある関数や対応・写像について、2 つ以上の変数の和として記される変数における値を、それぞれの変数における値によって書き表したもの。
加法定理 - Wikipedia:より引用
とのことです。
加法定理の公式
加法定理は以下の6つがあります。
回転行列で求めることが出来る理由
まず、点Pと点Qを極座標に置き換えます。
そして、点Qの極座標を加法定理を使用して置換します。
加法定理を使用したら、極座標で求めた点Pを代入します。
最後にこれを行列にします。
回転行列の公式は以下ですので、
回転後の座標を求めることができます。
half2x2 rotate = half2x2(cos(timeAngle), -sin(timeAngle), sin(timeAngle), cos(timeAngle)); float2 offsetUv = v.texcoord - _OffSet.xy; offsetUv = mul(rotate, offsetUv);
以上のことからrotate(回転座標)とoffsetUv(点P)を乗算することにより回転座標を求めることができます。
結果
以下のように回転すれば成功です。
ソースコードにコメントを付与
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); // アルファ値を0or1に offsetUv = step(offsetUv, 0); 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 } } }
今回は以上となります。
ここまでご視聴ありがとうございました。