前に書く
数日前、元同僚が突然、シェーダーの作成を手伝ってくれるように頼み、UIカットシーン効果を作成したいと言いました。おそらく漫画の最後の黒いフィールド効果のように、黒い背景の真ん中でズームインおよびズームアウトするには、透明なUI画像を使用する必要があります。透明UIが最大の場合、シーンの他のUIが表示されます。透明UIが最小の場合、シーン全体が黒になります。効果は次のとおりです。
実際、これは私の同僚が望んでいる効果です。実現の原則は非常に単純で、私はそれほど努力せずにそれを書きました。後で、このカットシーンをより魅力的にするために表示効果を最適化できるかどうかを確認するために、それについて考えてすべてを書きました。そこで、この記事を作成しました。
テキスト
この効果を実現する原理も非常に簡単です。まず、ズームインとズームアウトが必要な画像のUVを取得し、UVが0から1であるため、ティリングとオフセットを変更するために使用します。直接変更する場合は、 uvはのみになります左上隅がズームの原点であるため、ここでは画像のオフセットを(0.5、0.5)にオフセットしてからズームする必要があります。次に、必要な画像のアルファチャネルの情報を取得します。ズームインおよびズームアウトして、ベース画像と比較します。はい、2つの画像を重ねて、ズームされた画像のアルファが1の部分を削除します。これは、マスク効果と呼ばれることがよくあります。コードは次のとおりです
Shader "custom/masknormal"
{
Properties
{
//底图
_MainTex("_MainTex",2D) = "white"{}
//需要缩放的图
_MaskTex("_MaskTex", 2D) = "white" {}
//缩放比例
_Progress("Progress", Range(-0.5,0.5)) = 0
}
SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float2 uv2:TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
sampler2D _MaskTex;
float _Progress;
float4 _MaskTex_ST;
v2f vert(appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//_Progress = pow(_Progress,2);
_Progress = log(0.5 - _Progress);
_MaskTex_ST.xy = _Progress;
_MaskTex_ST.zw = (_Progress - 1)*-0.5;
//缩放图 同时做为掩膜图
fixed4 maskcol = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw);
//底图取样
fixed4 outcol = tex2D(_MainTex,i.uv);
//去除缩放图的alpha值
outcol.w = outcol.a*(1 - maskcol.a);
return outcol;
}
ENDCG
}
}
}
上記のシェーダーを使用して、c#コードの_Progressで補間制御を実行すると、達成される効果はこの図と同じになります。
元同僚にはかなり満足のいく効果ですが、それでも非常に粗雑な効果ですので、全部書いたらもっと良い効果を出しましょう!
背景色を置き換える
黒の背景がカラーパレットに置き換えられます。この実装は比較的簡単です。元の出力色に入力色を掛けるだけです。または、純粋なカラー画像であるため、入力色のRGBとスケーリングされたマスク画像のアルファチャネルをカラー出力として直接取得できます。
ぼかし効果
透明なズーム画像とベース画像の間の遷移効果は少し厄介で、ぼかし効果が追加されています。実装の原理も非常に簡単です。左上、左下、右上、右下、5つを取るだけです。現在の頂点のポイントとそれらを平均します。出力、得られた平均色はフラグメントの出力として使用できます。
レンダリングを添付する
コードは以下のように表示されます
Shader "custom/mask"
{
Properties
{
//底图
_MainTex("_MainTex",2D) = "white"{}
//需要缩放的图
_MaskTex("_MaskTex", 2D) = "white" {}
//缩放比例
_Progress("Progress", Range(-0.5,0.5)) = 0
//底图颜色
_Color("Color",color) = (0,0,0,0)
//模糊系数
_blurOffset("Blur",Range(0,0.01)) = 0.0075
}
SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float2 uv2:TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _MaskTex;
sampler2D _MainTex;
float _Progress;
float4 _MaskTex_ST;
fixed4 _Color;
fixed _blurOffset;
v2f vert(appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.uv;
o.uv2 = cos(o.uv*_Time.x);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//_Progress = pow(_Progress,2);
_Progress = log2(0.5 - _Progress);
_MaskTex_ST.xy = _Progress;
_MaskTex_ST.zw = (_Progress - 1)*-0.5;
fixed4 maskcol = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw);
//高斯模糊
//leftup
fixed4 maskcol1 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(-_blurOffset, _blurOffset));
//leftdown
fixed4 maskcol2 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(-_blurOffset, -_blurOffset));
//rightup
fixed4 maskcol3 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(_blurOffset, _blurOffset));
//rightdown
fixed4 maskcol4 = tex2D(_MaskTex, i.uv*_MaskTex_ST.xy + _MaskTex_ST.zw + fixed2(_blurOffset, -_blurOffset));
fixed4 mix = (maskcol + maskcol1 + maskcol2 + maskcol3 + maskcol4)*0.2;
mix = lerp(maskcol, mix, 0.5);
fixed4 outcol = tex2D(_MainTex,i.uv);
outcol.w = outcol.a*(1 - mix.a);
outcol = fixed4(_Color.x,_Color.y,_Color.z, outcol.w);
return outcol;
}
ENDCG
}
}
}
このようにすると、目にはより心地よく見えます。。
しかし、それが脳のポンピングなのかどうかはわかりませんが、それでもズーム画像の回転効果を追加したいと思います。それは、サンプルを取り、それを見つける前に数日間オンラインで調べたためです。以前は数学の授業を聞いていなかったと自分を責めただけだったので、最初に紫外線回転式を投稿しました。
この式がなぜであるかについては、あなたは私のメモを読むことができます。
レンダリングを添付する
最後に、コードを添付します
Shader "custom/mask1"
{
Properties
{
_MainTex("_MainTex",2D) = "white"{}
_MaskTex("_MaskTex", 2D) = "white" {}
_Progress("Progress", Range(0,1)) = 0
_Color("Color",color) = (0,0,0,0)
_blurOffset("Blur",Range(0,0.03)) = 0.0075
_RotateSpeed("RotateSpeed",Range(0,10))=5
}
SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MaskTex;
sampler2D _MainTex;
float _Progress; //声明缩放量
float4 _MaskTex_ST;
fixed4 _Color;
fixed _blurOffset;
fixed _RotateSpeed;
v2f vert(appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 uv;
//手动控制缩放
//_Progress =abs(log(_Progress)));
_Progress =abs(log(sin(_Time.y)));
//offset偏移
uv.xy = i.uv.xy- fixed2(0.5, 0.5);
uv.zw *=(1-_Progress) ;
//旋转公式 乘上缩放
uv.xy = fixed2(uv.x * cos(_RotateSpeed * _Time.y) - uv.y * sin(_RotateSpeed * _Time.y),
uv.x * sin(_RotateSpeed * _Time.y) + uv.y * cos(_RotateSpeed * _Time.y))*_Progress;
//复原offset
uv.xy += fixed2(0.5, 0.5);
fixed4 maskcol = tex2D(_MaskTex, uv);
//高斯模糊
//leftup
fixed4 maskcol1 = tex2D(_MaskTex, uv+fixed2(-_blurOffset, _blurOffset));
//leftdown
fixed4 maskcol2 = tex2D(_MaskTex, uv + fixed2(-_blurOffset, -_blurOffset));
//rightup
fixed4 maskcol3 = tex2D(_MaskTex, uv + fixed2(_blurOffset, _blurOffset));
//rightdown
fixed4 maskcol4 = tex2D(_MaskTex, uv + fixed2(-_blurOffset, -_blurOffset));
fixed4 mix = (maskcol + maskcol1 + maskcol2 + maskcol3 + maskcol4)*0.2;
mix = lerp(maskcol, mix, 0.5);
fixed4 outcol = tex2D(_MainTex,uv);
outcol.w = outcol.a*(1 - mix.a);
outcol = fixed4(_Color.x,_Color.y,_Color.z, outcol.w);
return outcol;
}
ENDCG
}
}
}
総括する
シェーダーを制御するC#コードを書くのが面倒なので、sin関数を直接使用してズームを制御します。そのため、サイクルが少し長くなり、ズーム比の制御がまだ少し不十分であるようです。私だけです。ログ方式で制御することを考えたので、ズームは一気に消えてしまいますので、最適化方式を考えてここに書いてください。紫外線の回転は、他の人の数式をオンラインで直接コピーするために使用されていました。今回は、自分で一度導出しました。幸い、忘れませんでした。UV回転とスケーリングを別々に記述するのは比較的簡単ですが、一緒に使用すると面倒になります。コードを数日変更した後、スケーリングは問題ありませんが、回転は特定の頂点を中心に回転します。これはほぼ高速です。Iなんらかの理由で諦めたので考えてみました。もちろん、頑張っていれば利益はあります!