全天球テクスチャのルール画像による切り替えshader
2018/7/8
対象:Unity 2018.1.1f1
Unityでの全天球テクスチャの切り替え用shader作成の経過を以下に記す。
なお記述者はShaderにはド素人なので非効率な記述があるかもしれないが勘弁していただきたい。
完成イメージ
https://gyazo.com/6b08a764e0fac74c0a4fbb425fc38c5b
ルール画像
ルール画像とは何か、については以下のリンク先を参照されたし。
Shader作成
まず、Threshold指定でuvのy座標基準でBefore/Afterテクスチャを切り替えるshaderを作る。
プロジェクトにシェーダーをCreate > Shader > Unlit ShaderでRuleTransitionという名前で追加して、_MainTexを_BeforeTex, _AfterTexに分離、_Threshold項目を新規に追加する。
Y座標によるテクスチャの切り替え
fragルーチン内でuv.yと_Thresholdの比較で対象テクスチャを切り替える。
ここまでの変更結果は以下の通り。
code:RuleTransition初期状態.shader
Shader "Unlit/RuleTransition"
{
Properties
{
_BeforeTex ("Before Texture", 2D) = "red" {}
_AfterTex ("After Texture", 2D) = "white" {}
_RuleTex ("Rule Texture", 2D) = "gray" {}
_Threshold ("Threshold", Range(0, 1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
// make fog work
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _BeforeTex;
float4 _BeforeTex_ST;
sampler2D _AfterTex;
float4 _AfterTex_ST;
sampler2D _RuleTex;
float4 _RuleTex_ST;
float _Threshold;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _BeforeTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = (i.uv.y > _Threshold) ? tex2D(_BeforeTex, i.uv) : tex2D(_AfterTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
fragルーチンの
fixed4 col = (i.uv.y > _Threshold) ? tex2D(_BeforeTex, i.uv) : tex2D(_AfterTex, i.uv);
にてテクスチャの切り替えを行なっている。
動作確認用にUnityで適当にCube, Sphere, Planeを配置する。
Materialも適当に新規作成して上記のオブジェクト全てに割り当て、MaterialのShaderにUnlit/RuleTransitionを指定する。
動作確認用テクスチャとして
からユニティちゃんHD画像パック Vol.1をDLして正方っぽい画像をBefore/Afterにセットする。
https://gyazo.com/371250af68d4e0e34242137979e76309
Thresholdを変更してテクスチャが切り替わるのを確認する。
これでテクスチャの切り替え自体は成功したので、次にルール画像による画像のブレンドの検証を行う。
ルール画像による画像ブレンド
シンプルに考えるため一旦Thresholdパラメータは使わないで実装する。
ルール画像として単純なな白->黒のグラデーション画像を作る。
https://gyazo.com/e94dee634bf59707adf9b0fa163d236a
これをShaderのRuleテクスチャに割り当てる。
ルールベースでのテクスチャの切り替え
先ほどのshaderのfragルーチンを以下のように書き換える
code:fragルーチン書き換え.shader
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
//fixed4 col = (i.uv.y > _Threshold) ? tex2D(_BeforeTex, i.uv) : tex2D(_AfterTex, i.uv);
fixed4 col1 = tex2D(_BeforeTex, i.uv);
fixed4 col2 = tex2D(_AfterTex, i.uv);
fixed4 col3 = tex2D(_RuleTex, i.uv);
fixed4 col = lerp(col1, col2, col3.r);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
パラメータはそれぞれ
col1=前画像
col2=後画像
col3=ルール画像
のuv座標の色で、
lerp関数によりルール画像のr(グレイスケールなのでr,g,bなんでも良い)を元に、ブレンドした色を求めた結果をcolに入れている。
lerpはパラメータ(v1, v2, percent)でpercentの値(0.0〜1.0)によりv1とv2のブレンドした値を求める関数。
(lerpの詳細については色々なサイトが解説しているので各自ググってください)
https://gyazo.com/2758a5c166563a3199ddeed4dd0623b3
異なるルール画像への切り替えによりテクスチャの重ね合わせが変化しているのが確認できる。
Threshold+ルール画像による切り替え
ここまでの作業でルール画像とThresholdをうまく計算するとテクスチャの切り替えが出来そうな目処がついた。
ひとまず、単純にlerpパラメータに_Thresholdを足してみる。
fixed4 col = lerp(col1, col2, col3.r + _Threshold );
https://gyazo.com/814923d08d50709327a451ba2fe6fa73
それっぽくなってるが2点問題がある。
0の時に前画像だけにならない
1の時に後画像が焼き付けを起こしている
_Threshold=0の時に前画像だけにならないのは当然なので、_Thresholdの範囲を-1〜+1にする事で解決する。
_Threshold ("Threshold", Range(-1, 1)) = 0.0
これにより、ルール画像で色が(1,1,1)となっている箇所も(1-1,1-1,1-1)=(0,0,0)となりlerp(v1, v2, 0)が求められ、前画像(v1)が適用されることになる。
_Threshold=1の時に焼き付けを起こしているのはlerpパラメータがi.uv.r + _threshold(=1.0)で1以上になってしまっているからなので、clamp関数を使う事で解決する。
clamp関数は(value, min, max)を指定して、min未満をmin、max超過をmaxに強制する関数。
(clamp関数についても詳細は各自ググってください)
fixed4 col = lerp(col1, col2, clamp(col3.r + _Threshold, 0.0, 1.0) );
この2点の変更により_Thresholdの値が
-1=全て前画像
1=全て後画像
となる。
https://gyazo.com/6c0639f3c1bc49704c2df3dc382f059b
期待した形でテクスチャ切り替えができていることが確認できる。
全天球に適用
Probuilderを使い法線を逆向きにした全天球Sphereを作成し、カメラと中心を合わせる。scaleはxyzとも10くらいにしておく。
(ProBuilderについては各自ググってください)
上記で作成したMaterialをSphereに適用し、ShaderのBefore, Afterに適当な全天球画像を2枚貼り付ける。
と、冒頭の画像が出来上がる。
https://gyazo.com/6b08a764e0fac74c0a4fbb425fc38c5b
ルール画像として164などを選ぶと、カメラ正面から広がってく形で画像切り替えが発生する。
ここでは手動でThresholdを設定しているが、以下のようなScriptを対象オブジェクトに追加する事でコード上からも操作可能である。
code:sample.cs
void Start(){
material = GetComponent<Renderer>().material;
material.SetFloat("_Threshold", 0.5);
}
Shaderの最終的なソースコードは以下になる
code:RuleTransition.shader
Shader "Unlit/RuleTransition"
{
Properties
{
_BeforeTex ("Before Texture", 2D) = "red" {}
_AfterTex ("After Texture", 2D) = "white" {}
_RuleTex ("Rule Texture", 2D) = "gray" {}
_Threshold ("Threshold", Range(-1, 1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
// make fog work
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _BeforeTex;
float4 _BeforeTex_ST;
sampler2D _AfterTex;
float4 _AfterTex_ST;
sampler2D _RuleTex;
float4 _RuleTex_ST;
float _Threshold;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _BeforeTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col1 = tex2D(_BeforeTex, i.uv);
fixed4 col2 = tex2D(_AfterTex, i.uv);
fixed4 col3 = tex2D(_RuleTex, i.uv);
fixed4 col = lerp(col1, col2, clamp(col3.r + _Threshold, 0.0, 1.0) );
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
おまけ