3.1 Stencil Test & Z Test
1. Stencil Test (ステンシル)
条件が満たされた時、何かの効果を入れる。
1.1 Stencil Test ができること
1.1.1 Portal
1.1.2 MinionsArt の作品



以上の例をまとめると、これらのエフェクトは基本的3つのレイヤーで構成されました。Portalの例では、ドア外、ドア中、ドア。二つのオブジェクトやシーンと一つのマスクで構成された。
1.2 Stencil Test は何ですか
1.2.1 レンダリングパイプラインから考えて
- StencilTestはPixel Processing 段階中の一環です。
- StencilTest はプログラムすることができない、配置することができる。
1.2.2 ロジックから考えて
if(referenceValue & readMask comparisonFunction stencilBufferValue&readMask)
//ピクセル通過
else
//ピクセル除外
- referenceValue StencilBuffer中の参考値
- stencilBufferValue 現在StencilBuffer中の値
- StrencilBuffer 一般的は8ビット
1.2.3 UnityShaderのStencil
- referenceValue 参考値
- readMask writeMask
- ComparisonFunction 比較方法
- Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never
- Pass StencilTest通過した後の操作
- Fail StencilTest通過しなかった後の操作
- ZFail StencilTest通過したが、深度テスト通過しなかった
- StencilOperation
- Keep StencilBufferValueを保留する
- Zero StencilBufferValue = 0
- Replace StencilBufferValue = referenceValue
- IncrSat StencilBufferValue += 1; if(StencilBufferValue >=255) StencilBufferValue = 255;
- DecrSat StencilBufferValue -= 1; if(StencilBufferValue <=0) StencilBufferValue = 0;
- Invert StencilBufferValue = ~StencilBufferValue
- IncrWrap StencilBufferValue += 1; if(StencilBufferValue >255) StencilBufferValue = 0;
- DecrWrap StencilBufferValue -= 1; if(StencilBufferValue < 0) StencilBufferValue = 255;
1.3 . Stencil Test を応用する
1.3.1 3DカードDemoを真似する
-
最初のScene
-
実現原理
-
UnityのStencilBufferValue のデフォルト値は0です
-
Maskオブジェクトを レンダリングする
Stencilの設定
Comp always いつも通過する、
Pass replace 通過してから、StencilBufferValue とReferenceValueを交換する
Mask部分のStencilBufferValue 1になります。
Properties{ _ID("Mask ID", Int) = 1 } SubShader { Tags { "RenderType" = "Opaque" "Queue" = "Geometry+1" //default + 1 } ColorMask 0 //RGBA RGB R,G,B,A,0 ZWrite off Stencil { Ref[_ID] Comp always //default always いつも通過する Pass replace // default keep そして値をキープする //Fail keep //ZFail Keep } }
-
Maskedオブジェクトをレンダリングする
Stencilの設定
Comp equal
if(currentStencilBufferValue == referenceValue) { //レンダリングする } else{ //レンダリングしない }
つまり、Mask以外は0ですから、Maskedオブジェクトはレンダリングしない。Mask以内は1ですから。レンダリングする。簡単効果をてきった。
Pass Keep 通過したら、変化なし。StencilBufferValue = 1
Properties { _Color("Color",Color) = (0,0,0,1) _MainTex ("Texture", 2D) = "white" {} _Ramp("Toon Ramp(RGB)", 2D) = "Gray" {} _ID("Mask ID",Int) = 1 } SubShader { Tags { "RenderType"="Opaque" "Queue" = "Geometry+2" //Default + 2 after StencilMask } Stencil { Ref [_ID] Comp equal //Pass Keep default }
-
レンダリング順番 Geometry -> Mask(Geometry+1) -> Masked Object(Geometry+2)
-
1.3.2 Stencil Box
-
効果
-
実現原理は上と同じ、Enumを使ってマテリアルを単独設置する。
Properties { _Color ("Color", Color) = (1,1,1,1) _StencilRef("Stencil Ref",float) = 1 [Enum(UnityEngine.Rendering.CompareFunction)] _SComp("Stencil Comp", Int) = 8 [Enum(UnityEngine.Rendering.StencilOp)] _SOp("Stencil Op", Int) = 2 } SubShader { Tags { "RenderType" = "Opaque" "Queue" = "Geometry+1" } ColorMask 0 ZWrite off Stencil { Ref[_StencilRef] Comp[SComp] Pass[_SOp] } }
1.4 . StencilTest まとめ
- Stencilbufferを用いて一番重要な二つの値:現在StencilBufferの値(StencilBufferValue) とStencilBuffer参考値(referenceValue)。
- StencilTestは、主にこの二つの値に対して、Never,Always,Less などの比較演算をすること。
- StencilTest後はその現在StencilBufferの値(StencilBufferValue)に対して更新操作をする、Keep,Zero,Replace,IncrSat,DecrSatなどを使用する。
- StencilTest後はピクセルを操作こと、Pass、Fail、ZFailなど
1.5. その他の用途
-
OutLine
-
効果
-
原理
Stencil { Ref 0 //0-255 Comp Equal //default:always Pass IncrSat //default:keep Fail keep //default:keep ZFail keep //default:keep } /* Pass 1 オブジェクトをレンダリングする、 そしてStencilTest通過して、IncrSat操作 StencilBufferValue + 1 */ Pass{} /* Pass 2 頂点を拡大する StencilBufferValueは1ですから、StencilTestを失敗て。 拡大の部分だけは現在StencilBufferValueは0の部分でレンダリングする */ Pass { ... o.vertex= v.vertex+ normalize(v.normal)*_OutLineWidth; ... return _OutLineColor; ... }
-
-
ポリコン充填
-
効果
-
原理 Pass 1 レンダリング StencilTest通過 +1、残ったPassはstencilValue2,3,4でレンダリングする
Pass{ Stencil { Ref 0 Comp always Pass IncrWrap } } Pass{ Stencil { Ref 2 Comp Equal Pass keep Fail keep ZFail keep } } Pass{ Stencil { Ref 3 Comp Equal Pass keep Fail keep ZFail keep } } Pass{ Stencil { Ref 4 Comp Equal Pass keep Fail keep ZFail keep } }
-
-
反射
-
効果
-
原理
SimpleStencilReflection
//正常描画する Pass{} //頂点を変換して StencilValueを1を設定して CompをEqualにする Pass { Stencil { Ref 1 //0-255 Comp Equal //default:always Pass keep //default:keep Fail keep //default:keep ZFail keep //default:keep } ZTest Always // 深度テストalways通過する ... //頂点をReflect v.vertex.xyz=reflect(v.vertex.xyz,float3(-1.0f,0.0f,0.0f)); v.vertex.xyz=reflect(v.vertex.xyz,float3(0.0f,1.0f,0.0f)); ... }
SimpleMirror //Mirro部分のStencilValueを1にする
Stencil { Ref 0 Comp always Pass IncrSat Fail keep ZFail keep }
-
-
ShadowVolume (シャッドー・ボリューム)
-
原理 : 方法はいくつありますが。このDemoはDepthFailを使います。Carmack’s reverseと呼ばれます
-
通常レンダリングします
-
ShadowVolumeを計算する、
-
Pass1はCull Front, StencilTest中で、深度テスト失敗した場合、このピクセル見えない。StencilValue+1
-
Pass2はCull Back 深度テスト失敗した場合、StencilValue - 1
対象A、CullFront 深度テスト失敗 +1 Cull Back 深度テスト失敗 -1 結果0
対象B、CullFront 深度テスト失敗 +1 CullBack 深度テスト成功 +0 結果1
対象C、CullFront 深度テスト成功 +0 CullBack 深度テスト成功 +0 結果0
-
この二つのPass経過して、StencilBufferValue = 1の部分で影を描画する。
Pass { Cull Front Stencil { Ref 0 //0-255 ZFail IncrWrap //default:keep } ColorMask 0 //Color write Off } pass { Cull Back Stencil { Ref 0 //0-255 ZFail DecrWrap //default:keep } ColorMask 0 } pass { Cull Back Stencil { Ref 1 //0-255 Comp equal //default:always } }
-
-
効果
これはStencilTest勉強のため、StencilBufferの役割を示すだけです。ShadowVolumeを動的生成ではない、Unityのデフォルトジオメトリの円柱を使ってShadowVolumeを粗雑に模倣しました。
-
2. 深度テスト(Depth Test/ Z-Test)
2.1 深度テストは何ですか
-
ロジックから理解する
if(ZWrite On && (currentDepthValue ComparisionFunction DepthBufferValue)){ DepthValue更新する }else{ 無視する } if(currentDepthValue ComparisionFunction DepthBufferValue){ 色バッファに書き込み } else { 色バッファに書き込まない }
StencilTestと似ている、通過したかどうか深度値を基づいて判断する、通過したら、色バッファに書き込む、しなかったら、無視する。
2.2 基本原理と使用方法
-
Z-buffer 一般的は24ビット
-
ZWrite On && ZWrite Off && ZTest
- ZTest pass Zwrite On : Z-bufferに書き込み、色バッファに書き込み
- ZTest pass Zwrite Off : Z-bufferに書き込まない、色バッファに書き込み
- ZTest fail Zwrite On : Z-bufferに書き込まない、色バッファに書き込まない
- ZTest fail Zwrite Off : Z-bufferに書き込まない、色バッファに書き込まない
-
ZTestの比較操作
ZTest の比較ルール Greater 現在の深度>ZBufferの深度 LEqual <= Less < GEqueal >= Equal == NotEqual != Always(Off) いつも通過する Never いつも通過しない デフォルトの状態
- ZWrite : On
- ZTest : LEqual
- Zbufferのデフォルト値は1.0
-
Unityのレンダリングキュー
数値は小さいほうのキューは先にレンダリングする。
Unity内蔵キュー Background(1000) 最初でレンダリングされたオブジェクトの待ち行列 Geometry(2000) 不透明なオブジェクトをレンダリングする、UnityShaderのデフォルト行列 AlphaTest(2450) 透明なオブジェクト&&AlphaTestが必要なオブジェクトでここでレンダリングするとGeometryより効率がいい Transparent(3000) 半透明のオブジェクト、一般的はZWriteOffのオブジェクト、AlphaBlendなど Overlay(4000) 最後でレンダリングされたオブジェクトの待ち行列、一般的はカメラエフェクトなと - 不透明なオブジェクトのレンダリング順番:前からレンダリングする
- 透明なオブジェクトのレンダリング順番:後ろからレンダリングする(OverDraw)
2.3 EazyZ 技術紹介
-
従来のレンダリングパイプラインは、ZTestはBlendingステージにある、深度テストが実行されると、すべてのオブジェクトのピクセルシェーダが一度計算され、性能向上はなく、正しい効果を得るためだけに多くの無駄な計算が行われます(深度テストに失敗したピクセルは、すでに計算されたものであり、つまり、次のステップでテストが失敗して破棄されるまでは、それまでの計算は無意味です)
-
このような不要な計算を減らすために、現代のGPUでは、頂点とピクセルステージの間(ラスタライズ後、ピクセルステージの前)で深度テストを行うEarly-Zテクニックを採用しています
- もしここの深度テスト失敗した、後のピクセル計算は必要ないです。性能向上になります
- 正しいオブジェクトの前後関係を確認するために、最終的なZTestをまだ実行する必要があります
- 前のZ-Cullは性能向上のため、後ろのZ-Checkは正しいオブジェクトの前後関係を確認するため
2.4 深度値
-
何でz/wを深度バッファに書き込むですか。
-
深度バッファの範囲は[0~1]の間、深度値を[0~1]の間に変換するには一つの方法はリニア変換(near/far はカメラのfrustum)
実際にはノンリニア変換を使う
人の目は近いものを見るち精度が高い、遠いものを見ると精度が低い、ノンリニアグラフは人の目と似ている。
-
Z-Fight 原因
二つの三角形が互いに密接に平行である場合、深度バッファの精度が足りない、どれが前かわからないことがある。
ノンリニアを使うと、近いもの精度が高いで、ZFightは出現しにくい、遠いものはZ-Fightがあったとしても、遠いものだから、気づかない。
もちろんZFightを避けるが必要です。
-
物理的、オブジェクトを近すぎように注意する。
-
CameraのNearPlaneを近くに設置する
-
すこし性能を犠牲し、深度バッファを精度を上げる。深度バッファの値は一般的は24ビットです。現代のGPUは32ビットの深度バッファもう支持する。
-
UnityShaderは Offsetを設置することでZFightを避ける
Offset -1, -1
-
-
-
2.5 深度テストを応用する
-
ZWrite ZTest をいくつ実験をする。
- 正常描画
- 赤をZWriteOffにして。赤部分の深度値はデファクト値1.0、青の深度値<=1.0、青の深度値を深度バッファに書き込む
- 青のZTest をAlways通過する、比較なしで、青の深度値を深度バッファに書き込む
- 青と緑もZTestをAlways通過する、比較なしで、青と緑の深度値を深度バッファに書き込む、同じレンダリングキュー(Geometry)のレンダリング順番は前から始まるので、緑が青の深度値を上書きした。
- 青と緑もZTestをAlways通過する、青のレンダリングキュー+1(Geometry+1),青が緑の深度値を上書きした。
- 青のZTest をGreaterに設定、デフォルト値>緑の深度値>青の深度値>赤の深度値、青と赤が交差する部分だけ、青深度テスト成功した。
-
XRay
-
効果
-
原理
-
三つの部分:壁、オブジェクト、X-Ray効果
-
XrayはZTest Greaterを設定する。
-
オブジェクト は デフォルト値
Pass //RenderXray { Tags{"RenderType" = "Transparent" "Queue" = "Transparent"} Blend SrcAlpha One // ZTest Greater ZWrite Off Cull Back } Pass //Normal Rendering {}
-
-
一つ現象があります。XRayPassを "Queue" = "Transparent"設定しましたが、FrameDebugの中はOpaqueGeometry中にいる、そしてXRay効果はNormalRenderingの前です。
実はオブジェクトはmulti-passシェーダーを使うとUnityはそのシェーダーの中一番小さいキューを選択する、オブジェクトをこのキューに入れる、(Geometry(2000)<TransParent(3000))このオブジェクトはGeometryの中でレンダリングする、それからPassの書き順番を基づいてPassを実行する。XRay効果はNormalRenderingの前になります。
-
-
パーティークルのテスト
2.6 深度テストまとめ
- 深度テスト重要な二つの値、現在の深度バッファ値(ZBufferValue)と深度参考値(reference)
- Unityのレンダリング順番、不透明オブジェクト、前から、透明なオブジェクト、後ろから
- ZWriteとZTestの使用透明なオブジェクトのレンダリングをコントロールできる。
- EarlyZ技術
- 深度バッファの値は「o~1」、ノンリニア。
2.7 その他の用途
Project
参考資料
Real-Time Rendering 4th Edition