本日もShader勉強枠です。
前回UnityのレファレンスからDiffuse SimpleShaderを見て細部のディテールアップを行う2つ目のテクスチャを使用するShaderを作成しました。
今回はDiffuse SimpleShaderを編集し、スクリーンスペースの座標を扱うShaderを作成します。
〇スクリーンスペースとは?
スクリーンスペースは常にカメラに対して一定の場所に配置されるテクスチャです。
iPhoneのカメラを例として挙げると

カメラで取得した実際の映像のほかに多くのUIが並んでいます。
これらはどれだけスマートフォンを動かしても現実の画像に合わせて動くことはありません。 デバイスの画面に対して常に固定されています。
Unityに話を戻すとスクリーンスペースを用いることでオブジェクトの動きなどに影響されることはなくカメラの座標に対してテクスチャが配置されます。
iPhoneの場合複数のオブジェクトで実装していると予想できますが、一枚のパネルなどオブジェクトでメインテクスチャにカメラの映像、前回扱ったDetailTextureのように2つ目のテクスチャはUIというように行う場合スクリーン座標を行います。

〇スクリーンスペースを扱うShaderを書く。
●Example/Diffuse Detail
//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse Detail"{
Properties{
_MainTex("Texture",2D) = "white"{}
_BumpMap("Bumpmap", 2D) = "bump"{}
_RimColor("Rim Color", Color) = (0.26,0.19,0.0)
//リムpowerのレンジ defaultでは3に設定される。
_RimPower("Rim Power", Range(0.5,8.0)) = 3.0
//SecondryTexture
_Detail("Detail", 2D) = "gray"{}
}
//Shaderの中身サブシェーダーではレンダリングパスの一覧を定義し、任意のオプションとしてすべてのパスに共通の State (状態)を設定します。追加で、サブシェーダーの特定の Tag を設定できます。
SubShader{
//RenderingType
Tags{"RenderType" = "Opaque"}
CGPROGRAM
#pragma surface surf Lambert
//Input構造体はテクスチャ座標として扱うものを記述します。
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
float2 uv_Detail;
};
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;
sampler2D _Detail;
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
//_Detailのrgbをo.Albedoに乗算
o.Albedo *= tex2D(_Detail, IN.uv_Detail).rgb * 2;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
//rim = 1.0 -viewDirの正規化したものとノーマルマップの内積(0~1の値を取る)
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
//Emissionに代入 propertyのRimColorの値(RGB)× rim^_RimPower= 0~1の値を取る 参考https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-pow
o.Emission = _RimColor.rgb * pow(rim, _RimPower);
}
ENDCG
}
Fallback "Diffuse"
}
ここまでDetailTextureをuv_Detailでオブジェクトに対しての座標で扱っていましたが、ここをScreenSpaceで扱ってみます。
テクスチャ座標を扱うInput構造体にScreenPosを宣言します。
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
float2 uv_Detail;
float4 screenPos;
};
screenPosを宣言します。
float4 screenPos;
最後に処理を行います。IN.screenPos.xy / IN.screenPos.wに関して理解が及ばないところがあったのですが、よく使用されるおまじないみたいな処理のようです。
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
//screenUVはスクリーン座標のxyをwで割ったものです。(よく使用されるおまじないみたいなもののようです。)
float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
screenUV *= float2(8, 6);
//_Detailのrgbをo.Albedoに乗算
o.Albedo *= tex2D(_Detail, screenUV).rgb * 2;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
//rim = 1.0 -viewDirの正規化したものとノーマルマップの内積(0~1の値を取る)
half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
//Emissionに代入 propertyのRimColorの値(RGB)× rim^_RimPower= 0~1の値を取る 参考https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-pow
o.Emission = _RimColor.rgb * pow(rim, _RimPower);
// の少数部分を返す。
clip(frac((IN.uv_MainTex.x + IN.uv_MainTex.y * _Power3) * _Power2) - _Power);
}
以上でスクリーンスペースを扱うShaderができました。

遠くのオブジェクトなどに使用できそうです。