HAMELN 03 Wepon Package
https://nonamere.booth.pm/ #booth_pm
上記の素材に同梱しているShaderについての解説記事です。
*Unity向けのShaderです ParticleSystem用の最適化(CustomVertexStremsへの対応)はしていません
今回のShader適用例
はじめに
この記事は03 Wepon Packageに内包するEffect用Shaderを改修しようとする際にShaderを触ったことが無い方向け用の解説記事です。
Shaderをある程度触ったことがある方(こちらが理解できている方)は
Shaderのフラグメントブロックの内容を見て頂けますと幸いです。
簡単な改変をしたい場合の対応方法
発光色を変えたい
=> マテリアルのColor欄を任意の色に変える
エフェクトの柄を変える
=> マテリアルのMainTex欄に設定する画像を変える
Shaderパラメータ説明
Shaderパス HAMELN/Effect/ActivateEffect
-
Color: Effectに設定するHDRカラー
HDRとは
簡単にいうとデフォルト0~1の色の値以上の色を設定できるようにするもの(発光しているのと同義です)
RGBに対してIntensityで色の値をオフセットしています。 -
MainTex: Effectに設定するテクスチャ
-
テクスチャのR値 UVスクロールする柄1
-
テクスチャのG値 UVスクロールする柄2
-
テクスチャのB値 エフェクトのアルファマスク
-
-
Alpha: テクスチャのR値の柄を適用する強さ
- 0:適用しない
- 1:適用する -
ScrollX SCrollY
- テクスチャのR値の柄のUVスクロールスピード
-
Scroll2X SCroll2Y
- テクスチャのG値の柄のUVスクロールスピード
Shader全文
コード
Shader "HAMELN/Effect/ActivateEffect" {
Properties{
[HDR]_Color("Color" , Color) = (1, 1, 1, 1)
//R: G: B:
_MainTex ("MainTex", 2D) = "white" {}
_Alpha ("Alpha", Range(0, 1)) = 0.5
_Scroll_X ("ScrollX", Float) = 1
_Scroll_Y ("ScrollY", Float) = 1
_Scroll2_X ("Scroll2X", Float) = 1
_Scroll2_Y ("Scroll2Y", Float) = 1
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
//Blend SrcAlpha OneMinusSrcAlpha
Blend SrcAlpha One
LOD 200
Cull Off
ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _Scroll_X;
float _Scroll_Y;
float _Alpha;
float _Scroll2_X;
float _Scroll2_Y;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o, o.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
float2 uv2 = i.uv;
float2 uv3 = i.uv;
i.uv.x += _Time * _Scroll_X;
i.uv.y += _Time * _Scroll_Y;
uv2.x += -1 * _Time * _Scroll2_X;
uv2.y += -1 * _Time * _Scroll2_Y;
// sample the texture
fixed Msk1 = tex2D(_MainTex, i.uv).r;
fixed Msk2 = tex2D(_MainTex, uv2).g;
fixed Msk3 = tex2D(_MainTex, uv3).b;
half4 col1 = half4(1, 1, 1, Msk1);
col1 *= _Color;
col1.a *= _Alpha * pow(Msk3, 3);
half4 col2 = half4(1, 1, 1, Msk2);
col2 *= _Color;
col2.a *= Msk3;
col1 += pow(col2.a, 2);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col1);
return col1;
}
ENDCG
}
}
}
Shader説明
①プロパティブロック
ここはUnityのInspectorで操作可能なマテリアルプロパティを表示させるための設定です。
プロパティの書式は下記となります
⑥で宣言した変数名("Inspectorに表示したい文字列", タイプ) = タイプ別の初期値
参考: https://docs.unity3d.com/ja/2021.3/Manual/SL-Properties.html
②SubShaderブロック/GPU設定
ここではShaderのGPU設定を行います
- Tagsに関して
- LODに関して
-
Blendに関して
透明なテクスチャを合成できるようにしているのはここのBlend設定によるものです。
ここの設定次第では、透過合成の他にも乗算や加算のような合成表現が可能となります
Blendを正しく設定している場合 | Blend設定を無効化(コメントアウト)した場合 |
---|---|
-
CullとZWriteについて
Cull設定により、メッシュの裏面を描画するか(両面描画にするか)などの設定を行うことができます
ZWrite設定により、深度情報を書き込むかの設定を行うことができます
Cull ZWriteをコメントアウト | Cull off ZWrite offで設定した場合 |
---|---|
cullがデフォルトonのためメッシュ裏面が描画されていない見た目となってしまっている | ZWrite offにすることで深度情報を書き込まず、半透明に半透明を重ねる場合の描画の不具合にある程度対処しています |
(3DCGの半透明描画の不具合は色々とあり、先人たちの奮闘の歴史が調べると出てくるので色々とみてみると面白いです
メッシュの裏面とは?
メッシュには、面ごとに法線という向きを持っています。(写真水色の線)
この反対方向が俗にいう裏面となります。
よくリッチなモデルで用いられるNormalMapは、この面ごとの法線とNormalMapの持つ法線情報を掛け合わせることで
法線を変更しています。
③Passブロック
ここでは一回の描画命令で処理する内容の設定を行います
Shaderによってはこの描画命令が複数ある場合があります。(UTS2は4パス使ってたりします
このPassブロック数(描画命令回数)が増えると描画コストが増えてしまい、ゲームが重たくなってしまいます。
CGPROGRAM ~ ENDCG
これはここの中でプログラムを書くよ~みたいな相図みたいなものです。
#pragma vertex vert
これは頂点シェーダとしてどの関数を呼び出すかの指定を行っています
今回は⑦の部分の関数を指しています。
頂点シェーダとは
頂点シェーダについて
ものすごく簡単に要約すると、メッシュの頂点毎にモデル空間の頂点座標(モデル原点から見た場合の頂点座標位置)から
カメラ空間の頂点座標(カメラから見た場合の頂点座標位置)への座標変換をしている部分です。
応用していくと頂点を動かすアニメーションみたいな仕組みも作れます
#pragma fragment frag
これはフラグメントシェーダとしてどの関数を呼び出すかの指定を行っています
今回は⑧の部分の関数を指しています。
フラグメントシェーダとは
フラグメンシェーダについて
ものすごく簡単にようやくすると頂点シェーダを元に、画面の1ピクセルに表示する色を決定するための部分です。
#pragma multi_compile_fog
一旦おまじないとして認識してください
Unityのフォグ機能を適用するためのものです。
#include "UnityCG.cginc"
Unityが提供してくれている便利な関数やマクロなどを使うための読み込み宣言です。
*.cgincは自分でも作成することができます
Shaderを良く書いている中で似たような機能を何回も各場合は自作した.cgincファイルへ纏めるようにしましょう。
④頂点データの構造を宣言するブロック
メッシュの頂点には、モデルの原点から見た場合のローカル座標位置やUV座標位置、頂点カラー、法線情報などを持っています
Blenderのメッシュタブの内容などを見てもらえるとわかりやすいかもしれません
これらをShaderの中で取得するために頂点データがどのようなデータを持っているのかを宣言する必要があります。
宣言する場合は下記のようにパラメータを宣言する必要があります。
型 変数名 : 入力セマンティックス;
セマンティックスとは
要約すると、この変数にこの頂点データを流し込むよという宣言
⑤フラグメントデータの構造を宣言するブロック
頂点データから計算した内容を⑧のフラグメントシェーダで使うための構造を宣言する必要があります。
宣言する場合は下記のようにパラメータを宣言する必要があります。
*④頂点データの構造を宣言するブロックの宣言と同形式。
型 変数名 : 入力セマンティックス;
⑥パラメータ宣言
⑦⑧などの関数内で使うための変数の宣言。
ここで宣言されている変数を①のプロパティブロックに記述することでUntiyのインスペクターから制御できるようになります。
宣言形式は下記
型 変数名;
今回のShaderでは
//Effectのカラー
fixed4 _Color;
//MainTexのためのテクスチャ情報用変数
sampler2D _MainTex;
//MainTexのタイリングオフセット用変数
float4 _MainTex_ST;
//MainTex.R用のUVスクロールスピード制御用変数
float _Scroll_X;
float _Scroll_Y;
//MainTex.Rの適用度制御用
float _Alpha;
//MainTex.G用のUVスクロールスピード制御用変数
float _Scroll2_X;
float _Scroll2_Y;
⑦頂点シェーダ
やっていることは⑧のフラグメントシェーダへ⑤の構造形式でデータを渡すための計算をしています。
//⑤で宣言した構造体名 ③の#pragma vertex行で設定した名称 (④で宣言した構造体名 引数名)
v2f vert(appdata v)
{
//⑧のフラグメントシェーダへ渡す構造体変数の宣言
v2f o;
//座標変換のための計算
o.vertex = UnityObjectToClipPos(v.vertex);
//_MainTex上のUV座標位置計算
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//フォグの適用
UNITY_TRANSFER_FOG(o, o.vertex);
//⑧へ構造体変数を返却
return o;
}
⑧フラグメントシェーダ
やっていることは⑧の構造体変数を受け取り、画面の1ピクセルの色を決定します。
よく解像度が高いゲームの描画が重くなる理由は、1ピクセル毎に画面解像度分このフラグメントシェーダの処理が走るためです。
今回のShaderではR・G値の柄でそれぞれUVスクロールしつつ合成、合成結果のαをB値でマスクする機能で構成されています。
//不動点小数4配列型(R,G,B,A) ③の#pragma fragment行で設定した名称(⑤で宣言した構造体名 引数名) : System-Value 出力セマンティック 大概はSV_Targetでいいです
fixed4 frag(v2f i) : SV_Target
{
//MainTex.G値UVスクロール用のUV宣言
float2 uv2 = i.uv;
//MainTex.B値UVスクロール用のUV宣言
float2 uv3 = i.uv;
//MainTex.R値UVスクロール用のUV座標ずらし計算
i.uv.x += _Time * _Scroll_X;
i.uv.y += _Time * _Scroll_Y;
//MainTex.G値UVスクロール用のUV座標ずらし計算
uv2.x += -1 * _Time * _Scroll2_X;
uv2.y += -1 * _Time * _Scroll2_Y;
// UV座標位置の色を取得する処理
//i.uvの位置のMainTex色のR値を取得
fixed Msk1 = tex2D(_MainTex, i.uv).r;
//uv2の位置のMainTex色のG値を取得
fixed Msk2 = tex2D(_MainTex, uv2).g;
//uv3の位置のMainTex色のB値を取得
fixed Msk3 = tex2D(_MainTex, uv3).b;
//色を計算
//考え方は_ColorのA値にMainTex.R値とMainTex.G値を掛け合わせ
//MainTex.R値の色を計算
half4 col1 = half4(1, 1, 1, Msk1);
col1 *= _Color;
//MainTex.B値の形でマスクしつつ、_Alpha(0~1)でMainTex.R値の適用度を決定
col1.a *= _Alpha * pow(Msk3, 3);
//MainTex.G値の色を計算
half4 col2 = half4(1, 1, 1, Msk2);
col2 *= _Color;
//MainTex.B値の形でマスク
col2.a *= Msk3;
//col1とcol2を合成
col1 += pow(col2.a, 2);
// Untiyのフォグ機能を適用
UNITY_APPLY_FOG(i.fogCoord, col1);
//ピクセルの最終的なカラーを出力
return col1;
}
UVスクロール
_Time
これはUnity側が提供してくれているゲーム内経過時間を持っている変数です。
これをUV座標に合成します
i.uv.x += _Time * _Scroll_X;
i.uv.y += _Time * _Scroll_Y;
元のUVの座標位置に経過時間分の増加量を合成しUV座標位置が移動していくことをUVスクロールといいます。
Blender画面左のUV座標がいどうしている形になります。
*ちなみに、Unity側のテクスチャのインポート設定内WrapModeがRepeatになっていないと正しい見た目になりません
WarpModeがClampの場合、UVスクロールを行うとテクスチャ端でスクロールが止まってしまい、引き伸ばされたような見た目になります
WarpModeがMirrorの場合、UVスクロールを行うとテクスチャ端でUVが反転し繰り返されるような見た目になります。
このようなUVスクロールをする場合に用いるTextureはテクスチャを繰り返しても問題ないような見た目になっている(する必要があるともいう)のですが、
そういうテクスチャをシームレステクスチャ 意味としては(つなぎ目がないテクスチャ) と呼ばれています。
マスク処理について
col1.a *= _Alpha * pow(Msk3, 3);
col2.a *= Msk3;
今回のシェーダでは上記の通りMainTex.B値の形でマスクしています。
この処理はMainTex.B値の値で最終的な見た目を切り抜いている(マスキングしている)と同義です。
マスク処理をコメントアウト(無効化)した場合
マスクを反転させたい場合の記述例
col1.a *= _Alpha * pow(1 - Msk3, 3);
col2.a *= 1 - Msk3;
このような、1-元の値 みたいな反転処理は良くOneMinus(ワンマイナス)と呼ばれます。
BlendModeの設定パラメータ名にOneMinusみたいな名称が入っているのはこういう処理が入ってます。
まとめ
今回のShaderは2つのUVスクロールを行い、それを合成、合成結果をマスクする
という処理をしています。
もし追加でテクスチャを合成したいや、RG毎に色を変えたいみたいな改修をしたい場合、
①と⑥に必要なパラメータを追加、そのパラメータを用いて⑧で計算処理を追加みたいな流れになります。
以上、Shaderの全体的な解説と今回のShaderの機能内容に関する説明記事でした。
ここが良く分からないみたいな箇所があればぜひご質問ください!
よきShaderライフを