本日はUnityShader勉強枠です。
筆者はUnity Shaderの勉強を独学で初めて1年ほどたちますが、ほかの方がGitHubに上げているコードを読んでいると次のようなコードを目にすることが多くありました。
Shader "HoloMoto/GeometryFirst"
{
Properties
{
...
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
HLSLPROGRAM
#include "sample.hlsl"
ENDHLSL
}
}
}
[HLSLPRPGRAM~ENDHLSL]を見てみるとhlslファイルを読み込んでいることがわかります。
#include ●●.hlsl
は[UnityCg.cginc]や[Core.hlsl]などのようにShaderで定義済みの変数を使用するために多く使用されます。
UnityC#でいうところの[Monobehavior]のような感じでしょうか?
今まではなぜShaderのコードをわざわざ別のファイルに処理を記述するのか理解できていませんでしたが、最近になってようやく意味を理解できるようになったため記事に残します。
〇なぜShaderlab内で処理を丸ごと外部hlslに記述して読み込んでいるのか?
結論から先に記述すると『コードエディタでの補完・校正機能を使用してコーディングの正確性や速さを高めるため』です。
Unity ShaderLabではShaderコード内で2種類のプログラム言語を使用しています。
[ShaderLab]言語の中の[HLSLPROGRAM~ENDHLSL(CGPROGRAM~ENDCG)]の間はcg/hlsl言語で記述されます。
//ShaderLab
{
Properties
{
...
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
HLSLPROGRAM//←ここからcg/hlsls言語
#include "sample.hlsl"
ENDHLSL//←ここまでcg/hlsl言語
}
}
}
cg/hlsl言語ではC系の言語のためコードの末尾に[;]がつけられているため見分けがつきます。
この場合問題となることがIDEでコードのチェックや校正のサポートが受けられなくなる点です。
[VisualStudio]や[JetBrain Rider]など様々なIDEがありますが、いずれもデフォルトではShaderLabに対応していない問題があります。
しかしhlsl言語はコーディングのサポートを受けることができます。
そのためcg/hlsl言語の処理である[HLSLPROGRAM~ENDHLSL(CGPROGRAM~ENDCG)]の間の処理をhlslの別ファイルで記述して[#include]で読み込むことでShaderLab言語にまとめるよりも効率的にコーディングが行えます。

〇サンプルコード
Shader "Unlit/NewUnlitShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
HLSLPROGRAM
#include "TestHLSL.hlsl"
ENDHLSL
}
}
}
●TestHLSL.hlsl
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"
struct appdata_t
{
float4 vertex:POSITION;
float2 uv : TEXCOORD0;
half3 normal: NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD1;
};
v2f vert (appdata_t v)
{
v2f o;
o.vertex = TransformObjectToHClip(v.vertex);
VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
o.normalWS = normal.normalWS;
return o;
}
float4 frag(v2f i):SV_TARGET
{
Light lt = GetMainLight();
float4 col = (1,1,1,1);
float strength = dot(lt.direction, i.normalWS);
float4 lightColor = float4(lt.color, 1);
return col* lightColor*strength;
}
ここではShaderとhlslファイルを同一階層に置いていますが、違う階層にある際は
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
などAssets以下の階層を記述する必要があります。
筆者がShaderを学び始めた際躓いた点の一つがIDEの校正のサポートが受けられず、大文字小文字含めタイプミスに気が付けない点がありました。
また二つの言語に関しても理解が及ぶのに時間がかかりました。最初から別ファイルにしておくことで理解も深まりそうです。