Dissolve Shaderとは次のチュートリアル動画で説明されているものです。
動画ではShader Graphで実装されていますが、今回はHLSLの勉強も兼ねて、HLSLでDissolve Shaderを実装しました。本記事ではHLSLでのDissolve Shaderの実装について説明します。
今回はこのようなものを作ります(Bloomをpost processingとして加えています)。
プログラムはGitHubにて公開しています。
方針
大まかな実装の流れを説明します。
まず、次のようなNoise Textureを用意します。
このテクスチャの各画素をアルファ値に変換します。グレースケールなので0.0 ~ 1.0の値が得られます。
ユーザーが任意に指定できる閾値を用意し、その閾値以下のアルファ値を持つ画素を消すという処理を記述する事で実現できます。
実装
それではコードを見ていきたいと思います。
Shader "Custom/Dissolve"
{
Properties
{
[HDR] _BaseColor ("Color", Color) = (1,1,1)
[HDR] _EdgeColor ("Dissolve Color", Color) = (0, 0, 0)
_MainTex ("Texture", 2D) = "white" {}
_DissolveTex ("Dissolve Texture", 2D) = "white" {}
_AlphaClipThreshold ("Alpha Clip Threshold", Range(0,1)) = 0.5
_EdgeWidth ("Disolve Margin Width", Range(0,1)) = 0.01
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
fixed4 _BaseColor;
fixed4 _EdgeColor;
half _AlphaClipThreshold;
half _EdgeWidth;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _DissolveTex;
float4 _DissolveTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 edgeCol = fixed4(1, 1, 1, 1);
// noise textureからalpha値を取得
fixed4 dissolve = tex2D(_DissolveTex, i.uv);
float alpha = dissolve.r * 0.2 + dissolve.g * 0.7 + dissolve.b * 0.1;
// dissolveを段階的な色変化によって実現する
if (alpha < _AlphaClipThreshold + _EdgeWidth && _AlphaClipThreshold > 0) {
edgeCol = _EdgeColor;
}
if (alpha < _AlphaClipThreshold) {
discard;
}
fixed4 col = tex2D(_MainTex, i.uv) * _BaseColor * edgeCol;
return col;
}
ENDCG
}
}
}
まずPropertiesにて必要な変数を定義します。
Properties
{
[HDR] _BaseColor ("Color", Color) = (1,1,1)
[HDR] _EdgeColor ("Dissolve Color", Color) = (0, 0, 0)
_MainTex ("Texture", 2D) = "white" {}
_DissolveTex ("Dissolve Texture", 2D) = "white" {}
_AlphaClipThreshold ("Alpha Clip Threshold", Range(0,1)) = 0.5
_EdgeWidth ("Disolve Margin Width", Range(0,1)) = 0.01
}
ここで注目してほしいのは_AlphaClipThresholdで、この変数がクリッピングの閾値になっています。
次に、フラグメントシェーダーを見ます。
fixed4 frag (v2f i) : SV_Target
{
...
// noise textureからalpha値を取得
fixed4 dissolve = tex2D(_DissolveTex, i.uv);
float alpha = dissolve.r * 0.2 + dissolve.g * 0.7 + dissolve.b * 0.1;
...
}
まずnoise画像からアルファ値を取得します。そして、このアルファ値が先程定義した_AlphaClipThresholdより小さい画素に対してはdiscardするようにします。
if (alpha < _AlphaClipThreshold) {
discard;
}
ここまででDissolve Shaderは完成です。
必須ではありませんが、消えていく部分のエッジを光らせたいので次のコードを加えます。
Properties
{
...
[HDR] _EdgeColor ("Dissolve Color", Color) = (0, 0, 0)
...
_EdgeWidth ("Disolve Margin Width", Range(0,1)) = 0.01
}
_EdgeColorが光らせる色で_EdgeWidthが幅になります。
そして次のように、閾値_AlphaClipThresholdを増やす事でクリッピングするタイミングを早めます。
if (alpha < _AlphaClipThreshold + _EdgeWidth && _AlphaClipThreshold > 0) {
edgeCol = _EdgeColor;
}
最後にこのedgeColを出力します。
fixed4 col = tex2D(_MainTex, i.uv) * _BaseColor * edgeCol;
return col;
以上で完成です。
終わりに
今回はDissolve Shaderをhlslで実装しました。
アウトプットを続けて表現の幅を広げていきたいです。