UnityのShader勉強10 ZTestとCullとか
今回の記事で基本的な機能のおさらいはおわりです。
さくっとおわらせます。
参考:
Cull
ポリゴンのどちら側を描画するかを制御
モデルの裏側が描画されないよーっていうときに使います。
・Back:裏側を描画しない。(省略時のデフォルト)
・Front:表面を描画しない。
・Off:どちらも描画する。
ZWrite
デプステストを書き込むかどうか
モデルを半透明にしたときなど、重なったパーツが見えて汚く見えてしまうのを制御できます。
まだ試してないです。すいません。
・Off:書き込まない。デフォルト
・On:書き込む
ZTest
奥にあるオブジェクトが手前のオブジェクトと重なっている場合デプステストをパスできないので、描画されません。
レンダリング順を変えるだけでは解決できない場合に使います。
オブジェクトに隠れているけど、ステンシルに書き込みたいときなどに使いました。
・Less:すでに描画されているオブジェクトより近い場合のみ描画
・Greater:すでに描画されているが前にある場合のみ描画
・LEqual:すでに描画されているオブジェクトと距離が等しいかより近い場合に描画(デフォルト)
・GEqual:すでに描画されているオブジェクトが前にある場合か完全に重なっているときに描画
・Equal:完全に重なっているときのみ描画
・NotEqual:完全に重なっているとき以外描画
・Always:常に描画
Offset
よくわかりません。
気が向いたら試します。
UnityのShader勉強9 RenderTexture
こんにちは!
今回は直接Shaderに関係ないのですが、RenderTextureというものをおさらいします。
参考:
簡単にいうと、カメラで映しているものをテクスチャにします。
GrabPassと違って任意のカメラを指定できるので監視カメラなどに使えます。
またリアルタイムでテクスチャを更新しつづけるため、うまく設定しないと重くなってしまいます。
実際に使うとこんな感じになります。
わかりづらいですが、右にある監視カメラが見ているものをテクスチャにして貼り付けています。
実装方法は、
Create>RenderTextureで専用のテクスチャを作成
サブカメラを作成、Cameraコンポーネントに作成したRenderTextureをアタッチ
これだけです。
これでShaderのテクスチャとしてそのまま使えます。
詳しい設定はマニュアへ。
テクスチャなので、他の画像を重ねて古いカメラっぽい演出も楽にできました。
ちなみに現在作っているホラーゲームの半透明マスクもRenderTextureを使っています。
3週間のシェーダー勉強の成果
— はる@ 10/2ホラーゲーム作成開始 (@haru_android) 2017年11月25日
・複数の擬似ライトの設置
・ライトが当たった部分だけおばけの表示(アルファに対応)
をついに成功した!ヽ(*´∇`)ノ
だいぶシェーダーにもなれたし、開発すすめるぞー! pic.twitter.com/0ZZh8mvXfN
おわりに
簡単にRenderTextureをおさらいしました。
次回はZtestやCullなどをちょっとだけ書こうかな...
UnityのShader勉強8 GrabPass
今回はGrabPassをおさらいします。
GrabPassとは、すでに描画されている画面ををテクスチャとして扱えるというものです。(まちがっているかも...)
すでに描画されている背景をゆがませたりといった面白い表現が可能になります。
参照:
勉強段階で知った機能なのでまだ実践では使っていません。ですので軽く試しておわりにしようと思います。
コード
Shader "Unlit/GrabPass" { Properties{ _Texture("Texture",2D)="white"{} _Float("float",float) = 1 _Color("Color",Color) = (1,1,1,1) _Range("Range",Range(-10,10))=0.5 _Vector("ector",Vector)=(0,0,0,0) } SubShader { Tags{ "RenderType"="Transparent" "Queue"="Transparent" } GrabPass{} Pass { Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _GrabTexture; sampler2D _Texture; float4 _Texture_ST; fixed _Float; fixed4 _Color; fixed _Range; fixed4 _Vector; struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _Texture); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 tex = tex2D(_GrabTexture, i.uv); return tex; } ENDCG } } }
GrabPassの使い方ですが、まずはPassの前に
GrabPass{}
と記述します。
そして
sampler2D _GrabTexture;
と宣言します。
これでテクスチャの取得がおわりました。
あとは
frag関数のテクスチャを_GrabTextureとしてやります。
fixed4 tex = tex2D(_GrabTexture, i.uv);
適当に作成したSpriteにこのシェーダーを適用したマテリアルをアタッチします。
(枠を作るために適当な画像をSpriteに入れておく必要があります。)
結果
わかりずらいですが、カメラでみている状態のようなテクスチャが貼られました。
次に、スクリーンのポジションを利用してテクスチャを貼ってみます。
コードを以下のように書き換えます。
Shader "Unlit/GrabPass" { Properties{ _Texture("Texture",2D)="white"{} _Float("float",float) = 1 _Color("Color",Color) = (1,1,1,1) _Range("Range",Range(-10,10))=0.5 _Vector("ector",Vector)=(0,0,0,0) } SubShader { Tags{ "RenderType"="Transparent" "Queue"="Transparent" } GrabPass{} Pass { Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _GrabTexture; sampler2D _Texture; float4 _Texture_ST; fixed _Float; fixed4 _Color; fixed _Range; fixed4 _Vector; struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float4 screenPos : TEXCOORD1; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); //o.screenPos = ComputeScreenPos(o.vertex); o.screenPos = ComputeGrabScreenPos(o.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _Texture); return o; } fixed4 frag (v2f i) : SV_Target { half2 grabUV = (i.screenPos.xy / i.screenPos.w); fixed4 tex = tex2D(_GrabTexture, grabUV); return tex; } ENDCG } } }
取得したスクリーンのポジションを保持するためにv2f関数に
float4 screenPos : TEXCOORD1;
と追加します。
次にvert関数にてスクリーンのポジションを取得します。
//o.screenPos = ComputeScreenPos(o.vertex);
o.screenPos = ComputeGrabScreenPos(o.vertex);
最初はComputeScreenPos(o.vertex)を利用していたのですが、UIに適用したところ反転してしまったため調べたところ、環境によって反転してしまったりする(?)というので、それに対応したComputeGrabScreenPos(o.vertex)を利用しました。
この二つはどちらも"UnityCG.cginc"にある関数で、
ComputeGrabScreenPos()はUV座標によって自動で反転してくれるようです。
こちらを適用すると、
こうなります。
なにも表示されていないように見えますが、すでに描画されているものをスクリーンのポジションに合わせてテクスチャを貼っています。
わかりずらいので、frag関数のtexに_Colorを乗算してみます。
fixed4 tex = tex2D(_GrabTexture, grabUV)*_Color;
実際にテクスチャが貼られているのがわかりました。
結果としてみると透明にしてるだけじゃないかと思いますが、テクスチャとして扱えるため、様々な表現ができます。
例えば、
frag関数のtexに
fixed4 tex = tex2D(_GrabTexture, grabUV);
tex~1-tex;
としてみると
テクスチャの色が反転しました。
ぼかしたり
fixed4 frag (v2f i) : SV_Target { half2 grabUV = (i.screenPos.xy / i.screenPos.w); fixed4 tex = tex2D(_GrabTexture, grabUV); tex += tex2D(_GrabTexture, float2(grabUV.x+_Range,grabUV.y)); tex += tex2D(_GrabTexture, float2(grabUV.x - _Range, grabUV.y)); tex += tex2D(_GrabTexture, float2(grabUV.x , grabUV.y + _Range)); tex += tex2D(_GrabTexture, float2(grabUV.x , grabUV.y - _Range)); return tex/5; }
・_Rangeの値は0.01ぐらい
・ImageをCanvas全体に広げる
簡単ですが中央と上下左右にずらしたものを加算して足したぶんを割っています。
おわりに
お手軽に画面にエフェクトをかけることができるのがわかってもらえるとうれしいです。
次は、RenderTextureをおさらいします。
UnityのShader勉強7 ステンシルマスク
ステンシルについておさらいします。
参考:
ステンシルとはピクセルごとに数値を保存しておき、それを比較し描画するかどうかを判断することができます。
これにはshaderが最低でも二つ必要ですのでまずは簡単に試してみます。
基本となるコードは
Shader "Unlit/StencilTest" { Properties{ _Texture("Texture",2D)="white"{} _Float("float",float) = 1 _Color("Color",Color) = (1,1,1,1) _Range("Range",Range(-10,10))=0.5 _Vector("ector",Vector)=(0,0,0,0) } SubShader { Tags{ "RenderType"="Transparent" "Queue"="Geometry" } Pass { Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _Texture; float4 _Texture_ST; fixed _Float; fixed4 _Color; fixed _Range; fixed4 _Vector; struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; float4 vec = v.vertex; o.vertex = UnityObjectToClipPos(vec); o.uv = TRANSFORM_TEX(v.texcoord, _Texture); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 tex = tex2D(_Texture,i.uv)*_Color; return tex; } ENDCG } } }
これを改造していきます。
複製し、パスを”Unit/StencilWrite”と”Unit/StencilRead”とします。
マテリアルも二つ作り名前をStencilWriteMat、StencilReadMatとし、shaderを割り当てておきます。
Sphereを二つ作成し作ったマテリアルをアタッチしておきます。
配置はWrite側を奥、Read側を手前とし重なるように配置します。
わかりやすいように色も変えておきます。
さてまずはStencilを書き込みます。
StencilWriteシェーダーを書き加えます。
Pass { Stencil{ ref 2 Comp always Pass replace } Blend SrcAlpha OneMinusSrcAlpha ~~~~~~~~
ステンシルは、Stencil{}に記述します。
refで比較、書き込みたい数値(0~255)
Compで描画するかどうか比較します。alwaysは常にパスします。
Passはテストにパスした後の操作で、replceで書き込みます。
操作には
Pass | ステンシルテストと深度テストをパスした場合 |
Fail | ステンシルテストに失敗した場合 |
Zfail | ステンシルテストにパスし、デプステストに失敗した場合 |
をが使えます。
続いて、StencilReadシェーダーも書き加えます。
Pass { Stencil{ ref 2 Comp equal Pass keep } Blend SrcAlpha OneMinusSrcAlpha ~~~~~~~~
equalは書き込まれている値と同じ場合のみパス
keepはバッファには書き込まず、保持します。(省略可)
結果
書き込まれた部分だけ描画されました。
ステンシルマスクをしたいときは、これだけ覚えておけば充分実用的です。
比較には、alwaysやequalだけでなく色々あります。
Passはパスしたときの操作ですが、Failでパスしなかった場合の操作もできます。
あとレンダリング順も重要で、今回はどちらもGeometryで描画しましたが、
WriteStencilシェーダーよりも先にReadStencilシェーダーを描画してしまうと、当然うまく比較できません。(ステンシルバッファlの初期値は0)
あと重要なのは深度テストです。
同じレンダリング順でも描画はカメラに近いほうからおこなわれるため、Writeオブジェクトの後ろにReadオブジェクトをおくと描画されません。
詳しくは
に書いてありますが、ステンシルテストにパスしても深度テストをパスしてないと描画できません。
ZTest Always と書くことで深度テストを無効にできるので奥行きを気にしなくてもよくなります。
最後に
簡単ですがステンシルテストをおさらいしてみました。
このあたりから、だいぶレンダリング順などに苦手意識がなくなってきました。
次回はGrabPassをおさらいします。
UnityのShader勉強6 時間を操る
お疲れ様です。
今回はTime.deltaTimeのように時間を使って動作を変えるシェーダーをおさらいします。
参考:
っていうか全部マニュアルに書いてあるんだよね。
まぁ知識の定着のために書こう。
Shader内で使える時間は
_Time | ステージの読み込みからの時間:(t/20,t,t2,t3) |
_SinTime | 時間の正弦:(t/8,t/4,t2,t) |
_CosTime | 時間の余弦:(t/8,t/4,t2,t)) |
unity_DeltaTime | デルタ時間:(dt、1/dt、smoothDt、1/smoothDt) |
になります。
使い方はこれらを普通にfloatとして使うだけです。
特別なにかインクルードする必要はありません。
簡単にuvスクロールだけします。
前回書いたコードのvert関数を少し書き換えます。
v2f vert (appdata v) { v2f o; float4 vec = v.vertex; o.vertex = UnityObjectToClipPos(vec); o.uv = TRANSFORM_TEX(v.texcoord, _Texture); o.uv.x += _Time.y; return o; }
テクスチャがスクロールするアニメーションができました。
uv.xに時間を加算しているだけです。
xyzによって速度が変わります。目的に応じて使い分けたり、乗算してください。
往復したい場合は_SinTimeと_CosTimeが有効です。
そのまま使ってもよいのですが加速と減速を行っているため一定速度では往復しません。
unity_DeltaTimeについては使い道がまだわかりません。
メモ:
組み込み関数を使うことで、_SinTimer、_CosTimerを表現できます。
Sin(_Time.x)、Cos(_Time.x)と書くことで往復します。このようにかいてあるブログも多いのですが_SinTimer、_CosTimerを使ったほうが楽でいいと思う。
使える組み込み関数については
色々便利な関数があるので使っていこう。
終わりに
短いですが時間のおさらいでした。
組み込み関数については、あとで便利なやつをまとめてみたいです。
次回はステンシルです。
UnityのShader勉強5 TagsとBlend
はい、今回は僕もまだまだ勉強中のTagsなどを書いていきます。
透過処理なども今回の記事で書きます。
まずはさくっと透過させてみます。
前回のコードを書き換えます
Shader "Unlit/TestShader" { Properties{ _Texture("Texture",2D)="white"{} _Float("float",float) = 1 _Color("Color",Color) = (1,1,1,1) _Range("Range",Range(-10,10))=0.5 _Vector("ector",Vector)=(0,0,0,0) } SubShader { Tags{ "RenderType"="Transparent" "Queue"="Transparent" } Pass { Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _Texture; float4 _Texture_ST; fixed _Float; fixed4 _Color; fixed _Range; fixed4 _Vector; struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; float4 vec = v.vertex; o.vertex = UnityObjectToClipPos(vec); //o.uv = v.texcoord; o.uv = TRANSFORM_TEX(v.texcoord, _Texture); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 tex = tex2D(_Texture,i.uv)*_Color; return tex; } ENDCG } } }
結果は
_Colorのアルファを減らすと透明になるのがわかりました。
新しいことはふたつです。
まずはTagsです。
Tags{ "RenderType"="Transparent" "Queue"="Transparent" }
Tagsではシェーダーのレンダリング順などを記述します。
”RanderType”は公式を読むと定義済みのいくつかのグループに分類し、カメラの深度テクスチャを生成するのに使うよと書いてあります。
僕のイメージではUnityのオブジェクトのタグ付けやレイヤーなどと同じと思っています。
かかなくても何も変化はおきません。
重要なのは
”Queue”です。レンダリング順を決めています。不透明であれば奥に何があろうと距離に応じて描画するかしないか決めるので意識しませんが、透明なものは奥が描画される前に描画してしまうと、透過した後ろには背景色が描画されてしまいます。
レンダリング順は整数で決まっていて
Background | 1000 |
Geometry | 2000 |
AlphaTest | 2450 |
Transparent | 3000 |
Overlay | 4000 |
これらから数字が少ない順にレンダリングします。
記述を省略した場合はGeometryが使用され、デフォルトのStandardマテリアルもGeometryです。
つまりデフォルトで用意したオブジェクトを描画したあとに透過オブジェクトを描画するという意味です。
詳しくは
次にBlendです。
Blend SrcAlpha OneMinusSrcAlpha
こちらは実際にアルファチャンネルを使用することを宣言?しています。
基本的には上記のものを使ってよいと思いますが、他にも色々なブレンド方法があります。
詳しくは
あとはtexに_Colorをかけているだけです。
fixed4 tex = tex2D(_Texture,i.uv)*_Color;
さて無事に透過しましたが、先ほどのレンダリング順について少しためしてみます。
Tags{ "RenderType"="Transparent" "Queue"="Geometry-1" }
”Geometry”の値を-1してみます。
そして_Colorのアルファを0にしてみると...
完全に透過されたオブジェクトの背景にはSolidColorが描画されました。
先に透明なオブジェクトが表示された後に地面が描画されたのがわかります。
UIのImageは背景の地面が描画されています。
まだ検証していませんがUIの描画順は普通のオブジェクトと違うのかもしれません。
ちなみにSolidColorではなくSkyBoxにすると真っ黒になります。
SkyBoxの描画っていつやってるんですかね...
まぁ特に問題ないのでいいでしょう。
終わりに
今回短かったのは僕の理解が追いついていない部分があったからです。
次回は時間によって動作を変えるシェーダーを書きます。
UnityのShader勉強4 テクスチャを貼る
今回はテクスチャを貼ります。
まずは貼る画像を用意します。
なんでもいいですが、今回はカラーグリッドを用意しました。
これをUnityに取り込み、作ったマテリアルのTextrueにD&Dします。
前回のコードを書き換えます。
Shader "Unlit/TestShader" { Properties{ _Texture("Texture",2D)="white"{} _Float("float",float) = 1 _Color("Color",Color) = (1,1,1,1) _Range("Range",Range(-10,10))=0.5 _Vector("ector",Vector)=(0,0,0,0) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _Texture; fixed _Float; fixed4 _Color; fixed _Range; fixed4 _Vector; struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 tex = tex2D(_Texture,i.uv); return tex; } ENDCG } } }
結果は
無事に表示されました。
ちなみに使うテクスチャをunityちゃんのテクスチャにすればちゃんと表示されます。
今回やったことは、モデルからUV座標を受け取り、それを元にテクスチャの座標から色を受け取り表示しています。
順番に見てみます。
struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; };
まず取り込むデータにTEXCOORD0を追加します。
TEXCOORD0は1番目のUV座標です。
例のごとく詳しくは
次にv2fです。
struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; };
変換後の頂点にデータを入れるわけですが、こちらでもTEXCOORD0を指定します。
前は指定しなくてもよかったらしいのですが、バージョンアップで値を保存したい場合は指定する必要がでました。
uv以外にも自分の好みのデータを保存したい場合も基本的にTEXCOORDを使います。
後ろにつける番号でいくつも保存できます。
UV座標はxとyだけなのでfloat2ですが、
別のデータを使いたいときはfloatやfloat4でも問題ありません。
(上限とかあるんですかね...)
さて頂点シェーダーです。
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; }
こちらでは特に変換せず、受け取った値をそのまま保存します。
最後にフラグメントシェーダーです。
fixed4 frag (v2f i) : SV_Target { fixed4 tex = tex2D(_Texture,i.uv); return tex; }
ここでUV座標を使ってテクスチャ座標の色を取得します。
初めてみたときはfixed4って値は4つしかないのに画像全体の色なんて取得できるのかわからなかったのですが、ピクセルごとにfrag関数が呼ばれると知ったらわかりました。
ためしにi.uvのみを使って表示してみます。
frag関数を
fixed4 frag (v2f i) : SV_Target {
fixed4 color = fixed4(i.uv.x, i.uv.y, 0, 1); return color; }
としてみます。
xとyは0~1の値なので赤と緑に入れてみました。
結果はこんな感じです。
どんな値があるのか結果として現れるのがUnityのいいところです。
さて次にTillingとOffsetです。
マテリアルのテクスチャにある項目ですね。
このコードのままではここを動かしても意味がありません。
テクスチャが動かない予定なら別によいのですが、川の流れを表現したいときなどはこれを動かすのが楽ですのでこれを使えるようにします。
以下のようにコードを書き換えます。
Shader "Unlit/TestShader" { Properties{ _Texture("Texture",2D)="white"{} _Float("float",float) = 1 _Color("Color",Color) = (1,1,1,1) _Range("Range",Range(-10,10))=0.5 _Vector("ector",Vector)=(0,0,0,0) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _Texture; float4 _Texture_ST; fixed _Float; fixed4 _Color; fixed _Range; fixed4 _Vector; struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; float4 vec = v.vertex; o.vertex = UnityObjectToClipPos(vec); //o.uv = v.texcoord; o.uv = TRANSFORM_TEX(v.texcoord, _Texture); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 tex = tex2D(_Texture,i.uv); return tex; } ENDCG } } }
変更点はそんなに多くありません。
CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _Texture; float4 _Texture_ST;
まず#include "UnityCG.cginc"です。
#includeとはC#でいうところの継承のようなものです。
"UnityCG.cginc"とはデフォルトでつける便利なものが色々入っているやつで、C#のMonobehaviourみたいなかんじです。
公式からダウンロードできるシェーダーに入っているので中を確認してみると、なんとなくわかると思います。
次にfloat4 _Texture_STです。
これはテクスチャを宣言することで意味が出てきます。テクスチャの変数名の後ろに_STと付け加えます。
使っていないように見えますがちゃんと"UnityCG.cginc"で使っています。
そして頂点シェーダーです。
v2f vert (appdata v) { v2f o; float4 vec = v.vertex; o.vertex = UnityObjectToClipPos(vec); //o.uv = v.texcoord; o.uv = TRANSFORM_TEX(v.texcoord, _Texture); return o; }
o.uvにv.texcoordをそのままいれるのでなく
TRANSFORM_TEX(v.texcoord, _Texture)
を入れています。
この関数は"UnityCG.cginc"に記述されていて
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
と書かれています。
#defineにはついてはまだ勉強中です。
よくわかりませんが_STを使っているのがわかります。
これで使えるようになりました。
TillingとOffsetの値を弄ってみます。
できました。
終わりに
無事にテクスチャを貼ることができました。
texに_Colorをかけてみたりすると色々楽しいので遊んでみると楽しいです。
まだ透過などやっていないことはあるのですが、次回に書きます。
次回はtagなどです。