こういう漫画っぽい表現やってみたかったんですよね。
Shader "Legacy Shaders/Diffuse Dither" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
fixed4 _Color;
struct Input {
float2 uv_MainTex;
float4 screenPos;
};
void surf (Input IN, inout SurfaceOutput o) {
float2 s = IN.screenPos.xy / IN.screenPos.w * 640.0;
s.y *= _ScreenParams.y / _ScreenParams.x;
clip(0.5 + (sin(s.x) + sin(s.y)) * 0.25 - (1.0 - _Color.a));
o.Albedo = _Color.rgb;
o.Alpha = _Color.a;
}
ENDCG
}
Fallback "Legacy Shaders/Diffuse"
}
解説
サーフェースシェーダーです。Unity ビルトインの Diffuse シェーダーをベースに、マスク処理を追加してます。他のシェーダーベースでも同じようにやれば出来るかと。
ポイントは clip()
の呼び出しです。この関数は、与えられた引数が 0
より小さい位場合は、描画を行いません。これを利用してマスク処理を行います。
surf
の処理を上から見ていきましょう。
float2 s = IN.screenPos.xy / IN.screenPos.w * 640.0;
今回は、スクリーンスペースでのエフェクトにしたかったので、screenPos
を使います。xy
を w
で割ることで、(0, 0)〜(1, 1) に正規化された状態で描画先の座標が得られます。640.0
は調整用の係数です。これを変えると網掛けの大きさの具合が変わります。
s.y *= _ScreenParams.y / _ScreenParams.x;
_ScreenParams
にはレンダリング先の画面の情報が入ってます。y / x
でアスペクト比が求まります。先ほど求めた s
は正規化された状態であるため、画面の比率が考慮されていません。縦長だろうが横長だろうが (0, 0)〜(1, 1) になってしまいます。そこで、アスペクト比を掛けておくことで正しい比率になり、この後行う網掛けが楕円ではなく正円で行えるようになります。
clip(0.5 + (sin(s.x) + sin(s.y)) * 0.25 - (1.0 - _Color.a));
いよいよクリッピングです。sin(s.x) + sin(s.y)
で、いい感じにグリッド上に丸が配置された感じの値が -2.0〜+2.0 で求まります。それを * 0.25
して -0.5〜+0.5 の範囲に収め、+ 0.5
することで、0.0〜1.0 の値が得られます。
こうして得られた値から、一律で定数を引くと、1.0 に近いピクセル以外はだんだんマイナスの値となり、結果クリップ関数によって消えていくという寸法です。元の値を sin + sin
で求めているので、この消え方が円状になります。
この例では、(1.0 - _Color.a)
を引くことで、アルファの値に応じて消え具合を調整できるようにしてあります。アルファが 1.0 の時は完全に描画され、0.0 の時は完全に消えます。このシェーダーを指定したマテリアルを作成して、ベースカラーのアルファの値を弄ると網掛けが変わる様子がわかると思います。
sin + sin
の部分を別の式にすると別の消え方になったりして面白いので是非カスタマイズしてみてください。