はじめに
ついにUnityもバージョン6.2が先日公開されましたね。Unity 6からはビルトインレンダーパイプライン(BRP)が廃止され、それに伴いサーフェスシェーダーもURPでは非対応となりました。
そこで、シェーダーの学習を兼ねて、インターネットで公開されているサーフェースシェーダーを、頂点・フラグメントシェーダーとしてURP向けに移植する作業を進めています。その備忘録として、今回の記事を作成しました。
今回の氷シェーダーのプログラムは下記のブログを参考にさせていただきました。
今回取り扱うシェーダーの原理がとてもわかりやすく解説されているため、この記事を読む前に一度読んでおくことをお勧めします。
開発環境
- Unity 2022.3.61f1(URP)
- JetBrains Rider 2025.1.5
移植手順
- 1.シンプルなUnlitシェーダーを用意します
-
今回移植するシェーダーはテクスチャを扱わないので、インスペクターから設定した色に塗るだけのシンプルなシェーダーを用意します。
SimpleIceShader.shader
Shader "Unlit/SimpleIceShader" { Properties { _MainColor("Color",color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; float4 _MainColor; v2f vert (appdata v) { v2f o; o.vertex = TransformObjectToHClip(v.vertex); return o; } float4 frag (v2f i) : SV_Target { float4 col = _MainColor; return col; } ENDHLSL } } }
- 2.透明なシェーダーに必要な各種設定を追記します
-
ブレンドモードとカリングモードに関してはお好みですが、Zバッファはオフがおすすめです。詳しくは公式ドキュメント(Blend
,ZWrite,Cull)を参考にしてください。
Pass { + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + Cull Off HLSLPROGRAM #pragma vertex vert #pragma fragment frag
- 3.頂点とピクセルが扱う情報を新たに追加します
-
各頂点の法線の情報を扱うため、appdata構造体にnormal変数を追加します。また、各ピクセルに対するワールド空間での法線とカメラからの視線ベクトルを扱うため、v2f構造体にそれぞれnormalWSとviewDir変数を追加します。
struct appdata { float4 vertex : POSITION; + half3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; + float3 normalWS : TEXCOORD1; + float3 viewDir : TEXCOORD2; };
- 4.頂点シェーダーに処理を追加します
-
参考元のサーフェースシェーダーは、引数を追加するだけでUnityが自動的に多くの処理を行ってくれますが、URPではすべて手動で記述する必要があります。そこで、core.hlslが用意してくれている関数たちを用いて自分たちで座標変換を行い、フラグメントシェーダーに渡す情報を変数に格納していきます。
v2f vert (appdata v) { v2f o; o.vertex = TransformObjectToHClip(v.vertex); + o.normalWS = GetVertexNormalInputs(v.normal).normalWS; + o.viewDir = GetWorldSpaceViewDir(TransformObjectToWorld(v.vertex.xyz)); return o; }
- 5.フラグメントシェーダーに処理を追加します
-
頂点シェーダーから受け取った各頂点の情報を正規化などをして整えて、それをもとにアルファ値を計算します。計算方法ははじめに紹介したサーフェスシェーダーを参考にしているので詳しい説明はそちらをご覧ください。
float4 frag (v2f i) : SV_Target { float4 col = _MainColor; + float3 normal = normalize(i.normalWS); + float3 view = normalize(i.viewDir); + float alpha = 1 - abs(dot(view,normal)); + col.a = 1.5 * alpha; return col; } ENDHLSL
実行結果
実行結果はご覧の通りです。左側のカプセルが通常の透明マテリアル、右側が今回作成したシェーダーのマテリアルを適用したものです。オブジェクトの中心部ほど透明度が高く、外側ほど不透明度が高いという、理想通りの表現ができました。これで移植は完了です。
コード全文
クリックしてコード全文を表示する
Shader "Unlit/SimpleIceShader"
{
Properties
{
_MainColor("Color",color) = (1,1,1,1)
_Transparency ("Transparency", Float) = 1.5
}
SubShader
{
Tags { "Queue" = "Transparent" "RenderType"="Transparent" }
LOD 100
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 vertex : POSITION;
half3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 normalWS : TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
float4 _MainColor;
float _Transparency;
v2f vert (appdata v)
{
v2f o;
o.vertex = TransformObjectToHClip(v.vertex);
;
o.normalWS = GetVertexNormalInputs(v.normal).normalWS;
o.viewDir = GetWorldSpaceViewDir(TransformObjectToWorld(v.vertex.xyz));
return o;
}
float4 frag (v2f i) : SV_Target
{
float4 col = _MainColor;
float3 normal = normalize(i.normalWS);
float3 view = normalize(i.viewDir);
float alpha = 1 - abs(dot(view,normal));
col.a = 1.5 * alpha;
return col;
}
ENDHLSL
}
}
}
おまけ
おまけとして、今回作成したシェーダーをShader Graphでも作ってみました。プログラム上で定数としていた値をStrength変数として定義することで、インスペクターから不透明度を調整できるようにしています。
おわりに
今回のシェーダーはUnlitであり、座標変換のための関数が充実していたため、比較的簡単に移植できました。今回と同じ方法で、参考にさせていただいたブログで紹介されているリムライティングシェーダーの移植も可能ですので、興味がある方はぜひ挑戦してみてください。
最後に、貴重な知見を共有してくださった、おもちゃラボのちくわ氏に心より感謝申し上げます。そして、最後までお読みいただき、ありがとうございました。