ノイズでアルファ加算画像を歪ませるシェーダーをつくる
ノイズシェーダーを歪みマップに応用する話です。
元記事
テクスチャと色指定でアルファ加算する
アルファ加算は、炎などを表現するときによく用いられる加算合成方法で、
元画像のアルファ値(不透明度)が高い部分ほど、色を強く加算します。
左から、Default-Particleテクスチャをそのまま表示、ベースカラーで赤を乗算、
ベースカラーを付けた上に黄色のアルファ加算カラーを加算、の状態です。
アルファ加算画像を表示するシェーダーを書いてみます。
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BaseColor ("Base Color" , Color ) = (1,1,1,1)
_AddAlphaColor ("Add Alpha Color", Color ) = (0,0,0,1)
}
SubShader
{
Tags { "RenderType"="Transparent" }
LOD 100
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
・・・
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _BaseColor;
fixed4 _AddAlphaColor;
・・・
メインテクスチャ(_MainTex)、ベースカラー(_BaseColor)、アルファ加算カラー(_AddAlphaColor) をパラメータとして追加します。TagsやBlendは透過色を使える設定にしておきます。
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 color = tex2D(_MainTex, i.uv.xy);
color *= _BaseColor;
color.rgb += _AddAlphaColor * color.a;
return color;
}
fragmentシェーダーでは、メインテクスチャ色にベースカラーを乗算して乗せた後、アルファ加算カラーを足し込みます。
アルファ加算カラーはメインテクスチャのアルファ値と乗算することで、
アルファ値が大きい部分ほど強く色が足されます。
(PhotoShopなどの色範囲0-255は、シェーダー内では0.0~1.0範囲に対応するので、
乗算されると暗く、加算されると明るくなります)
歪みマップの考え方
こうしてできたアルファ加算なDefault-Particleテクスチャを、ノイズを利用して歪ませます。
よくある歪みマップは、以下のように値を扱います。
(PhotoShopなどの色範囲0-255は、シェーダー内では0.0~1.0範囲になります)
・Rチャンネル(RGB=XYZの対応ならX)が0.5より大きいほど、メイン画像のUV値をU(X)+方向にずらす
・Rチャンネル(RGB=XYZの対応ならX)が0.5より小さいほど、メイン画像のUV値をU(X)-方向にずらす
・Gチャンネル(RGB=XYZの対応ならY)が0.5より大きいほど、メイン画像のUV値をV(Y)+方向にずらす
・Gチャンネル(RGB=XYZの対応ならY)が0.5より小さいほど、メイン画像のUV値をV(Y)-方向にずらす
これは、法線(=面の傾きを表す方向)マップと非常によく似た扱い方で、
法線のRG値を歪みマップ情報として扱うと都合が良いです。
※法線マップの場合、RGチャンネルに加えてBチャンネルが
貼り付けた面のモデル上の法線(面の傾き方向)=Z+方向を表す1.0となります。
法線マップを画像として見ると青っぽいのはこのためで、全く傾きのない面は全ピクセルの色が(0.5, 0.5, 1.0)になります。
ノイズで生成した法線を歪みマップとして扱う
ノイズシェーダーをつくった際、位相をずらしてXYそれぞれ異なる乱数を生成して、
法線をつくっていました。これを歪みマップとして扱うことを考えてみます。
ノイズテクスチャのTiling/Offset(_NoiseTilingOffset)とサイズ、UVスクロール速度(_NoiseSizeScroll)、さらに歪み強度(_DistortionPower)をパラメータとして追加します。
{
_MainTex ("Texture", 2D) = "white" {}
_BaseColor ("Base Color" , Color ) = (1,1,1,1)
_AddAlphaColor ("Add Alpha Color", Color ) = (0,0,0,1)
_NoiseTilingOffset ("NoiseTex Tiling(x,y)/Offset(z,w)", Vector) = (0.1,0.1,0,0)
_NoiseSizeScroll ("NoiseTex Size(x,y)/Scroll(z,w)" , Vector) = (16,16,0,0)
_DistortionPower ("Distortion Power", Float ) = 0
}
SubShader
{
・・・
Pass
{
・・・
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _BaseColor;
fixed4 _AddAlphaColor;
fixed4 _NoiseTilingOffset;
fixed4 _NoiseSizeScroll;
fixed _DistortionPower;
・・・
メインテクスチャとノイズテクスチャでUV値が2つになるので、vertexシェーダーから受け渡すUVを拡張します。
struct v2f
{
float4 vertex : SV_POSITION;
float4 uv : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
o.uv.zw = TRANSFORM_NOISE_TEX(v.uv, _NoiseTilingOffset, _NoiseSizeScroll);
return o;
}
fragmentシェーダーでは、まずノイズテクスチャのUV値から法線マップ(≒歪みマップ)を計算します。
値の範囲が0.0~1.0(中央値0.5)なので、これを-1.0~1.0(中央値0.0)に変換し、歪み強度を掛けたものが歪み量(dist)になります。
こうして算出した歪み量の分だけ、メインテクスチャを参照するためのUVをずらすことで、歪んだ画像を得ます。
fixed4 frag (v2f i) : SV_Target
{
fixed3 dist = normalNoise(i.uv.zw, _NoiseSizeScroll.xy); // perlinノイズで算出した法線を得る
dist = dist * 2 - 1; // 範囲を0.0~1.0から-1.0~1.0へ変換
dist *= _DistortionPower; // 歪み強度を乗算(歪み強度をシェーダーパラメータとして調整可能にする)
i.uv.xy += dist.xy; // 歪み量だけ、メインテクスチャのUVをずらす
fixed4 color = tex2D(_MainTex, i.uv.xy);
color *= _BaseColor;
color.rgb += _AddAlphaColor * color.a;
return color;
}
ノイズ歪みシェーダーで炎を描く
このノイズ歪みシェーダーで、先ほどのアルファ加算したDefault Particleテクスチャを歪ませると、炎ができます。
Tiling値を大きくすると急で細かい歪み方となり、0に近づける(絶対値を小さくする)と広く緩やかな歪みになります。
また、Distortion Powerの強さによって炎の歪み具合も変わります。
ノイズ=歪みマップは上方向にUVスクロールさせることもできるので、
燃えるようなアニメーションも可能です。
ノイズ歪みシェーダーで煙を描く
このノイズ歪みシェーダーですが、Particle Systemに適用することもできます。
パーティクル設定カラーは、どうやら頂点カラーとして入力されるらしいので、これを乗算するように直してみます。
struct appdata
{
...
float4 color : COLOR;
};
struct v2f
{
...
float4 color : TEXCOORD1;
};
v2f vert (appdata v)
{
...
o.color = _BaseColor * v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
...
color *= i.color;
color.rgb += _AddAlphaColor * color.a;
return color;
}
Particle SystemのRendererにMaterialをセットすれば準備完了。
Particle Systemの設定値は多すぎるので割愛しますが、Color over Lifetimeで透明、不透明、透明と遷移させることで煙のような表現になります。
煙量マシマシ化。
ノイズ歪みシェーダー(アルファ加算画像)のソースコード
同じ階層に、元記事のSasamiNoise.cgincと一緒に置くことで動くはずです。
gist.github.com