前回、Unity の Unlit Shader を新規作成し、指定された影の色のシェーダープロパティを参照し、その色で、タイルマップ画像の色を指定した影の色で塗る処理を実装しました。
しかし、なぜかタイルマップ画像のアルファ値は反映されていないらしく、透けて見える部分も指定された影の色で塗りつぶされてしまいました。
前回の記事:Unity シェーダープロパティを処理で参照するためのマッピング | Compota-Soft-Press
今回は、ピクセルシェーダーの処理(frag関数)で、タイルマップ画像のアルファ値を正しく読み取り、その形にあわせた影を作成するための検証と実装手順を紹介します。
※ Unity は 2021.3.14f1、Test Framework パッケージは Version 1.1.33、Visual Studio は Community 版 Version 17.2.5、 OS は Windows 10 です。
テンプレートの frag 関数の処理
新規作成した Unlit Shader をタイルマップオブジェクトに割り当てても、マップチップは正常に表示されました。
しかし、このとき、 _MainTex シェーダープロパティのアルファ値はすべて完全不透明を指しています。
Unlit Shader で扱うアルファ値の検証
Unlit Shader を新規作成し、その frag 関数の r (赤) の値を返す部分を、 _MainTex シェーダープロパティの同じ座標のアルファ値(不透明度)に変えてみました。
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 texCol = tex2D(_MainTex, i.uv);
// _MainTex の i.uv のアルファ値を R (赤) の値として出力
fixed4 retCol = fixed4(texCol.w, 0.0, 0.0, 1.0);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, retCol);
return retCol;
}
この結果は、冒頭で触れたように、前回の結果から考えると、全て R=255 の赤色で矩形の範囲が塗りつぶされるはずです。
しかし、結果は、赤(1.0)と黒(0.0)で不透明度はマップチップの形にそって適切に設定されていました。
しかし、次のように、アルファ値をアルファ値として扱うと、灰色(0.1)でさきほど黒かった(不透明度=0)だった部分も塗りつぶされています。
この画像の赤色や黒色が完全不透明と完全透明の二つでしかないことを確認するため GIMP に入力し、ヒストグラムダイアログで赤色の成分の分布を見ると 0 と 255 しかないことが確認できました。
それならば、アルファ値を、そもそものアルファ値として色の4番目の要素に指定して、1~3番目に歯 R, G, B を設定すれば、タイルマップ画像の形にそって影もできると思い、次のコードで試しました。
※ frag 関数で返す色の各要素の値は 0 ~ 255 ではなく 0.0 ~ 1.0 です。(0.3, 0.3, 0.3) は少し明るめの灰色を指します。
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 texCol = tex2D(_MainTex, i.uv);
// _MainTex の i.uv のアルファ値を R (赤) の値として出力
//fixed4 retCol = fixed4(texCol.w, 0.0, 0.0, 1.0);
fixed4 retCol = fixed4(0.3, 0.3, 0.3, texCol.w);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, retCol);
return retCol;
}
しかし、タイルマップ画像(_MainTex)のアルファ値をアルファ値として使うと、先ほどは赤と黒で完全不透明と不透明な部分に分けられていたのに、全て完全不透明で矩形で塗りつぶされてしまいました。
Tilemap のシェーダーによる描画はマス単位?
タイルマップは何も置かれていないマスも含めて複数のマスを描画します。
先ほど、灰色になった部分は、何かが配置されたマスの領域です。
しかも、その縦幅は、そのマスに指定したように半分になっています。
このことから、TilemapRenderer からシェーダーが描画するのは、何かが配置されたマスごとではないかと推測します。
画像のアルファ値を扱えるように変更
「【Unity】アルファテクスチャを利用できる Shader – うにてぃブログ」によると、シェーダにはアルファ値を扱えないものがあることがわかりました。
_MainTex シェーダープロパティからタイルマップ画像の情報としてアルファ値も適切に読みとれていることは、先ほどのアルファ値を赤色に置き換えたテスト画像でわかりました。
しかし、アルファ値として frag 関数が戻り値で返しても、アルファ値を扱えないシェーダの場合は、アルファ値が無視されて、指定した RGB で完全不透明で塗られてしまうようです。
そこで、シェーダープログラムの一部を変更し、アルファ値を扱えるようにします。
参照:【Unity】アルファテクスチャを利用できる Shader – うにてぃブログ、ShaderLab: Blending – Unity マニュアル
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
SubShader の冒頭で行っている Tags の設定を変更します。
SubShader
{
Tags {"Queue" = "Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
LOD 100
これで、アルファブレンドを扱えるシェーダになりました。
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 shadowCol = fixed4(_ShadowColor.x, _ShadowColor.y, _ShadowColor.z, col.w);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, shadowCol);
return shadowCol;
}
6 行目では、 _ShadowColor という Unity エディタの Inspector ウィンドウのシェーダープロパティで指定できる影の色で RGB を指定し、4 番目でタイルマップ画像から得たその座標のアルファ値を設定しています。
※fixed4 の場合は、x, y, z, w という要素で 1 ~ 4 番目この場合ならば、 r, g, b, a の値にアクセスします。
シェーダの Tags を変更し、アルファ値を扱えるようにしたシェーダーでは、アルファ値を用いて画像の形に合わせた影が塗られました。
作成したタイルマップ影用シェーダーのプログラムの公開
今回作成した影用シェーダーのプログラムは GitHub で公開しています。自己責任で使用してください。
Unity/SCTilemapShadowUnlitShader.shader at main · sakura-crowd/Unity
アルファ値を赤色の要素に出力したテスト用のシェーダーも公開しました。
Unity/UnlitShaderAlphaToRed.shader at main · sakura-crowd/Unity
影用シェーダーは画像のアルファ値をもとに、指定された色に塗り替えるだけです。
タイルマップの影として利用する場合は、Unity 用に自作した影用タイルマップ作成関数で作成した影用タイルマップのゲームオブジェクトに設定することで、タイルマップの影を表現します。
Unity Tilemap の壁に影をつける自作関数の処理手順の紹介 | Compota-Soft-Press
まとめ
今回は、Unity の Unlit Shader をベースにして、画像の不透明度に応じて指定した色で塗るシェーダを作成・公開しました。
シェーダの不透明度が正しく受け取れているかを確認するために、シェーダーの赤色の要素の値にアルファ値を設定し出力された画像の色を GIMP のヒストグラムを用いて検証しました。
シェーダの設定でアルファ値を扱うかどうかを変えられることもわかりました。
参照サイト Thank You!
- Unity のリアルタイム開発プラットフォーム | 3D/2D、VR/AR のエンジン
- Cg/HLSL でシェーダープロパティを参照する – Unity マニュアル
- 【Unity】アルファテクスチャを利用できる Shader – うにてぃブログ
- ShaderLab: Blending – Unity マニュアル
- Unity Shader入門した頃の自分に教えたい事 – Qiita
- 2.5. ヒストグラムダイアログ
記事一覧 → Compota-Soft-Press
コメント