本日はShader勉強枠です。
前回はオブジェクトをスライスするShaderを書きました。
前回のShaderのままではスライスする箇所はワールド座標で固定されており、オブジェクトが移動した場合クリップする範囲がずれてしまいます。

今回前回のスライスするShaderを加工していきます。
〇スライスする範囲をMaterialで扱えるようにする。
まずはスライスする線の太さをUnityのマテリアル側から扱えるようにします。
●Example/Diffuse DetaiShader
//シンタックス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;
float3 worldPos;
};
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;
clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
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"
}
●スライスしている処理のパラメータ
前回の記事で詳しく勉強しましたが、スライスはClip()を用いています。
void surf(Input IN, inout SurfaceOutput o) {
clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
}
ここで登場するパラメータは
・IN.worldPos.y=ワールド座標のy
・IN.worldPos.z=ワールド座標のz
・IN.worldPoszに掛かっている定数0.1
・(IN.worldPos.y+IN.worldPos.z*0.1) に掛かっている定数 5
・frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) に減算されている定数 0.5
このうちワールド座標はすでに変数として固定されているので固定されている定数は0.1、5、0.5の3つになります。
これらをマテリアルで扱えるようにします。
●Propeties
UnityのマテリアルでShaderのプロパティを扱うためにはPropeties{}に記述します。
Propeties{
_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"{}
//追加
_SlicePower1("SlicePower1",Range(0,1))=0.1
_SlicePower2("SlicePower2",Range(0,10))=5
_SlicePower3("SlicePower3",Range(0,1))=0.5
}
これでUnityのマテリアル側でスライダーが扱えるようになります。

●Surf関数の処理の定数を変数に置き換える。
surf関数でスライダーの値を扱うために宣言をします。
float _SlicePower1;
float _SlicePower2;
float _SlicePower3;
void surf(Input IN,input surfaceOutput o){
}
次に定数を_SlicePowerに置き換えます。
void surf(Input IN, inout SurfaceOutput o) {
clip (frac((IN.worldPos.y+IN.worldPos.z*_SlicePower1) * _SlicePower2) - _SlicePower3);
}
これでUnityのマテリアル側からスライダーを操作することでスライスする範囲や向きを変更できるようになりました。

●スライスをワールド座標からオブジェクトのローカル座標に変更する。
void surf(Input IN, inout SurfaceOutput o) {
clip (frac((IN.worldPos.y+IN.worldPos.z*_SlicePower1) * _SlicePower2) - _SlicePower3);
}
クリップはワールド座標のy,zが変数として与えられています。
ここをオブジェクトのローカル座標に変更します。
void surf(Input IN, inout SurfaceOutput o) {
clip (frac((IN.uv_MainTex.y+IN.uv_MainTex.z*_SlicePower1) * _SlicePower2) - _SlicePower3);
}
ここではテクスチャと同じようにuv座標を用いました。しかしこのままではエラーを吐いてしまいます。

これはテクスチャのuv座標は平面空間を扱うためzを扱っていないためです。
struct Input {
float2 uv_MainTex;
};
そのためx,yの座標に書き直します。
void surf(Input IN, inout SurfaceOutput o) {
clip (frac((IN.uv_MainTex.x+IN.uv_MainTex.y*_SlicePower1) * _SlicePower2) - _SlicePower3);
}
これでローカルの座標でスライスが行えるようになりました。

ここまでの編集したShaderは以下になります。
●Diffuse LocalSlice Shader
//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse LocalSlice"{
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"{}
_SlicePower1("SlicePower1",Range(0,1)) = 0.1
_SlicePower2("SlicePower2",Range(0,10)) = 5
_SlicePower3("SlicePower3",Range(0,1)) = 0.5
}
//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;
//ワールド座標を使用しないためコメントアウト
//float3 worldPos;
};
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;
sampler2D _Detail;
float _SlicePower1;
float _SlicePower2;
float _SlicePower3;
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;
clip(frac((IN.uv_MainTex.x + IN.uv_MainTex.y * _SlicePower1) * _SlicePower2) - _SlicePower3);
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"
}
〇裏面を表示させる
sliceを行うことができましたが、メッシュの裏面が描画されないため現実では不自然なオブジェクトになっています。
最後に裏面を表示させる処理を行います。
//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse Detail"{
Properties{
}
SubShader{
Tags{"RenderType" = "Opaque"}
//追加
Cull Off
CGPROGRAM
ENDCG
}
Fallback "Diffuse"
}
追加したものは以下の一文です
Cull Off
この一文を記述することで裏面も描画されるようになります。

完成したShaderは以下になります。
//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse LocalSlice CullOff"{
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"{}
_SlicePower1("SlicePower1",Range(0,1)) = 0.1
_SlicePower2("SlicePower2",Range(0,10)) = 5
_SlicePower3("SlicePower3",Range(0,1)) = 0.5
}
//Shaderの中身サブシェーダーではレンダリングパスの一覧を定義し、任意のオプションとしてすべてのパスに共通の State (状態)を設定します。追加で、サブシェーダーの特定の Tag を設定できます。
SubShader{
//RenderingType
Tags{"RenderType" = "Opaque"}
//メッシュの裏表両面を描画
Cull Off
CGPROGRAM
#pragma surface surf Lambert
//Input構造体はテクスチャ座標として扱うものを記述します。
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
float2 uv_Detail;
//ワールド座標を使用しないためコメントアウト
//float3 worldPos;
};
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;
sampler2D _Detail;
float _SlicePower1;
float _SlicePower2;
float _SlicePower3;
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;
clip(frac((IN.uv_MainTex.x + IN.uv_MainTex.y * _SlicePower1) * _SlicePower2) - _SlicePower3);
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"
}
今回は前回のスライスを扱うShaderを編集して使いやすくしました。