【UnityShader】ワイプエフェクト #28
前回の成果
画面をモノクロ・セピア調にすることができた。
今回やること
今回は、ワイプエフェクトを制作していきます。
ワイプエフェクトとは、だんだん視野が狭くなっていくエフェクトのことです。
事前準備
SceneにCameraを配置して、今回制作するScriptをアタッチします。
そして、ScriptのSerializeFieldにMaterialをアタッチしてください。
Shaderのソースコード
Shader "Unlit/wipeEffect" { Properties { _Radius("Radius", Range(0, 2)) = 2 _MainTex("MainTex", 2D) = "" {} } SubShader { Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment frag #include "UnityCG.cginc" float _Radius; sampler2D _MainTex; fixed4 frag(v2f_img i) : COLOR { fixed4 c = tex2D(_MainTex, i.uv); i.uv -= fixed2(0.5f, 0.5f); float4 projectionSpaceUpperRight = float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y); float4 viewSpaceUpperRight = mul(unity_CameraInvProjection, projectionSpaceUpperRight); i.uv.x *= viewSpaceUpperRight.x / viewSpaceUpperRight.y; if(distance(i.uv, fixed2(0, 0)) < _Radius) { return c; } return fixed4(0, 0, 0, 1); } ENDCG } } }
この部分が謎です。
float4 projectionSpaceUpperRight = float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y); float4 viewSpaceUpperRight = mul(unity_CameraProjection, projectionSpaceUpperRight); i.uv.x /= viewSpaceUpperRight.x / viewSpaceUpperRight.y;
Scriptのソースコード
using UnityEngine; public class PostEffects : MonoBehaviour { [SerializeField] Material _material; void OnRenderImage(RenderTexture source, RenderTexture dest) { Graphics.Blit(source, dest, _material); } }
Scriptは他のポストエフェクトと同じとなっているので、解説は省きます。
では、Shaderの解説に移っていきます。
i.uv -= fixed2(0.5f, 0.5f);
uv値の原点が左下にありワイプエフェクトを中央に表示させたいため、u座標とv座標を共に-0.5しています。
i.uv.x *= viewSpaceUpperRight.x / viewSpaceUpperRight.y;
これはアスペクト比を出しています。
アスペクト比を考慮しなかった場合
アスペクト比を考慮した場合
これで何をしているか紐解いていきます。
この計算式でプロジェクション空間での右上を出しています。
float4 projectionSpaceUpperRight = float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y);
UNITY_NEAR_CLIP_VALUE
これはnear clipping planeに定義されている値です。
Direct3Dでは0.0、OpenGLでは-1.0が値に入ります。
near clipping planeとは、描画できる最短距離のことです
_ProjectionParams
これはfloat4で、カメラのclippng planeの情報が格納されている変数です。
xyzw | 用途 |
---|---|
x | 反転した射影行列でレンダリングしている場合は-1.0、それ以外は1.0 |
y | カメラのnear plane |
z | カメラのfar plane |
w | 1 / far plane |
次に
float4 viewSpaceUpperRight = mul(unity_CameraInvProjection, projectionSpaceUpperRight);
について説明します。
unity_CameraInvProjection
これは、カメラのプロジェクション行列の逆行列になります。
プロジェクション行列とは、近くのものは大きく、遠くのものは小さく描画している行列です。
詳しくはこちらのサイト様を参考にしてください。
unity_CameraProjectionとUNITY_MATRIX_Pとの違い
少し話が逸れてしまうので、ここは飛ばしてくださっても構いません。
unity_CameraInvProjectionは、プロジェクション行列の逆行列と説明しました。
unity_CameraProjectionは、プロジェクション行列を取得できるものです。
しかし、UNITY_MATRIX_Pという変数も存在していてこれは現在のプロジェクション行列を表しているものです。
この2つの違いは対象となるものが何かの違いです。
unity_CameraProjectionはスクリーンスペースの描画、UNITY_MATRIX_Pはオブジェクトへの描画となっています。
これで取れる理由
プロジェクション行列は、
となっていて、今回使用しているプロジェクション行列の逆行列は、
となっています。
各要素の説明
名前 | 説明 |
---|---|
W | 画面の幅(ピクセル) |
H | 画面の高さ(ピクセル) |
θ | 視野角(field of view) |
Zn | Near |
Zf | Far |
ここまで来ればあとは行列の計算をするだけです。
から、
(xとyしか使用しないので、zとwは1とする)となり、
となります。
i.uv.x *= viewSpaceUpperRight.x / viewSpaceUpperRight.y; をしているので、
となるので、解像度を求めることができます。
結果
Radiusの値を変更することで、ワイプエフェクトを実装することができました。
今回はここまでとなります。
ご視聴ありがとうございました。