#region OpenTK

OpenTKがGitHubで更新されるようになりました。NuGetから手に入れることができるようになりました。
現在は.NET Coreに対応する機運があるようです。

ステンシルテスト

型紙

ステンシルテストは、アルファテストとデプステストと同じように、ピクセル毎に記憶された値を元にそのピクセルを描画するかを判定しています。

ステンシル(プレート)は型紙ですが、OpenGLではルールを設定することで指定したポリゴンを特殊な型紙として扱うことができます。

またデプステストと同じように、ステンシルシャドウという影(Shadow)を描画する方法の一つに使われることもあります。

上手く使えば効果的

Stencil Test
球の奥が見える

ソースコードにはステンシルテストの設定が追加され、これを確認できるように型紙用の球を追加しています。

		//800x600のウィンドウを作る。タイトルは「1-12:Stencil Test」GrahpcsModeでステンシルバッファのサイズを8に指定
		public Game()
			: base(800, 600, new GraphicsMode(GraphicsMode.Default.ColorFormat, GraphicsMode.Default.Depth, 8), "1-12:Stencil Test")

OpenTKのクラスが生成する画面は、初期状態ではステンシルバッファ(書き込む場所)のサイズが0になっているので、GraphicsModeを使って指定する必要があります。

気にしていないと指定し忘れるかもしれないので注意してください。

			//ステンシルテスト
			GL.Enable(EnableCap.StencilTest);
			GL.ClearStencil(0);
			GL.StencilMask(~0);

まずは起動時に、ステンシルテストを許可します。

次に、ステンシル値をリセットしたときの初期値をClearStencilで設定します。また、ステンシル値に対するマスクの値をStencilMaskで設定します(NOT 0x00なので、全てが'1'のビット列)。ちなみに、この例ではどちらとも初期値を設定しているので、書かれていない場合もあるはずです。

		//画面描画で実行される。
		protected override void OnRenderFrame(FrameEventArgs e)
		{
			base.OnRenderFrame(e);

			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit);

ここの引数の指定も変わります。一番最後にStencilBufferBitを追加していて、これでステンシル値が、ClearStencilで指定した値になります。

			GL.MatrixMode(MatrixMode.Modelview);

			plate.Draw();

			GL.ColorMask(false, false, false, false);
			GL.DepthMask(false);
			GL.StencilOp(StencilOp.Keep, StencilOp.Replace, StencilOp.Replace);
			GL.StencilFunc(StencilFunction.Always, 1, ~0);
			
			//型紙描画
			clip.Draw();

			GL.ColorMask(true, true, true, true);
			GL.DepthMask(true);
			GL.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Keep);
			GL.StencilFunc(StencilFunction.Equal, 1, ~0);

			//ステンシル値==+1で見えるものを描画
			GL.PushMatrix();
			GL.Translate(-7.5f, 0.0f, 0.0f);

			torus.Draw();

			GL.PopMatrix();

			GL.StencilFunc(StencilFunction.Equal, 0, ~0);

			//ステンシル値==0で見えるものを描画
			GL.PushMatrix();
			GL.Translate(7.5f, 0.0f, 0.0f);

			sphere.Draw();

			GL.PopMatrix();

			SwapBuffers();

ColorMaskとDepthMask関数は、バッファに対しての書き込み制御するもので、falseで書き込まない(≒表示しない)、trueで書き込む(≒表示する)となっています。結果の表示のために追加しています。

ステンシルテストの処理の設定は主に二つです。

StencilOpは、ステンシルテストとデプステストの結果によって、どのような処理をステンシル値に施すかを指定します。各引数は順に、ステンシルテスト不合格、ステンシルテスト合格かつデプステスト不合格、どちらも合格、のときとなっています。

StencilFuncは、テストの評価方法を設定します。最初の引数で不等号を、次の引数で基準値を定めます。最後の引数はステンシル値へのマスクです。

StencilOpで用いるStencilOp列挙型は下のとおりです。(値はステンシル値のことです。)

Keep
値を変化させない
Zero
値を0にする
Incr
値を+1
IncrWrap
値を+1 (最大値を超えると0)
Decr
値を-1
DecrWrap
値を-1 (0を下回ると最大値)
Replace
StencilFuncの第2引数の値とする
Invert
値のビット列を反転する

StencilFuncで用いるStencilFunction列挙型は、デプステストのものと内容は同じです。

Never
全て不合格
Less
比較値未満なら合格
Equal
比較値と同じなら合格
Lequal
比較値以下なら合格
Greater
比較値を超えるなら合格
Notequel
比較値と異なれば合格
Gequal
比較値以上なら合格
Always
すべて合格

サンプル図

文だけでは動作を掴みにくいと思うので、図でさらに説明します。その前に状況から。

StencilTestOff
すべてを普通に表示した場合

この型紙は左右の物体より早く(ステンシルバッファに)描画され、画面の中で型紙があるところのステンシル値が1となります。そうでない場合は0です。ちなみに、回転の中心に球の型紙があるので画面の中心のステンシル値は常に1となることになります。

StencilTestOn
通常描画

この場合、中心以外のステンシル値は0なので、赤い球は無事描画されます。しかし1ではないので青のトーラスは描画されません。なお、下の床はステンシル値がClearされ0で、関数の最後のStencilFuncの設定が残っているので描画されています。

StencilTest2StencilTest2Navi
どちらも欠ける状態

なんとも奇妙な形ですが、中心以外のステンシル値は0なので、中心以外では赤い球が描画されています。また、中心のステンシル値は1なので、中心では青いトーラスが描画されています。

記事

外部リンク

Thanks