第一步是告诉图形卡我们需要模板缓冲区。为此,在创建GraphicsDeviceManager时,我们将PreferredDepthStencilFormat设置为DepthFormat.Depth24Stencil8,因此实际上有一个可写入的模板。
graphics = new GraphicsDeviceManager(this) {
PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8
};
AlphaTestEffect用于设置坐标系并过滤通过alpha测试的alpha像素。我们将不会设置任何过滤器并将坐标系设置到视口。
var m = Matrix.CreateOrthographicOffCenter(0,
graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,
graphics.GraphicsDevice.PresentationParameters.BackBufferHeight,
0, 0, 1
);
var a = new AlphaTestEffect(graphics.GraphicsDevice) {
Projection = m
};
接下来,我们需要设置两个DepthStencilStates。这些状态决定了何时将SpriteBatch渲染到模具以及何时将SpriteBatch渲染到BackBuffer。我们主要对两个变量StencilFunction和StencilPass感兴趣。
- StencilFunction指示SpriteBatch何时绘制单个像素以及何时将其忽略。
- StencilPass指示何时绘制的像素像素影响模板。
对于第一个DepthStencilState,我们将StencilFunction设置为CompareFunction。这将导致StencilTest成功,并且当StencilTest SpriteBatch渲染该像素时。StencilPass设置为StencilOperation。替换表示在StencilTest成功后,该像素将使用ReferenceStencil的值写入StencilBuffer。
var s1 = new DepthStencilState {
StencilEnable = true,
StencilFunction = CompareFunction.Always,
StencilPass = StencilOperation.Replace,
ReferenceStencil = 1,
DepthBufferEnable = false,
};
总而言之,StencilTest总是通过,图像被正常绘制到屏幕上,对于绘制到屏幕上的像素,StencilBuffer中存储的值为1。
第二个DepthStencilState稍微复杂一些。这次,我们只想在StencilBuffer中的值为时绘制到屏幕上。为此,我们将StencilFunction设置为CompareFunction.LessEqual并将ReferenceStencil设置为1。这意味着,当模板缓冲区中的值为1时,StencilTest将成功。将StencilPass设置为StencilOperation。保持将导致StencilBuffer不更新。这使我们可以使用相同的蒙版绘制多次。
var s2 = new DepthStencilState {
StencilEnable = true,
StencilFunction = CompareFunction.LessEqual,
StencilPass = StencilOperation.Keep,
ReferenceStencil = 1,
DepthBufferEnable = false,
};
总而言之,StencilTest仅在StencilBuffer小于1(蒙版的Alpha像素)时通过,并且不会影响StencilBuffer。
现在我们已经设置了DepthStencilStates。我们实际上可以使用蒙版进行绘制。只需使用第一个DepthStencilState绘制蒙版。这将同时影响BackBuffer和StencilBuffer。现在,模板缓冲区的值为0(在蒙版具有透明度的情况下)和值为1(在其中包含颜色的情况下),我们可以使用StencilBuffer遮罩以后的图像。
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s1, null, a);
spriteBatch.Draw(huh, Vector2.Zero, Color.White); //The mask
spriteBatch.End();
第二个SpriteBatch使用第二个DepthStencilStates。无论您进行什么绘画,只有将StencilBuffer设置为1的像素才能通过模板测试,并被绘制到屏幕上。
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s2, null, a);
spriteBatch.Draw(color, Vector2.Zero, Color.White); //The background
spriteBatch.End();
以下是Draw方法中的全部代码,请不要忘记在游戏构造函数中设置PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8。
GraphicsDevice.Clear(ClearOptions.Target
| ClearOptions.Stencil, Color.Transparent, 0, 0);
var m = Matrix.CreateOrthographicOffCenter(0,
graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,
graphics.GraphicsDevice.PresentationParameters.BackBufferHeight,
0, 0, 1
);
var a = new AlphaTestEffect(graphics.GraphicsDevice) {
Projection = m
};
var s1 = new DepthStencilState {
StencilEnable = true,
StencilFunction = CompareFunction.Always,
StencilPass = StencilOperation.Replace,
ReferenceStencil = 1,
DepthBufferEnable = false,
};
var s2 = new DepthStencilState {
StencilEnable = true,
StencilFunction = CompareFunction.LessEqual,
StencilPass = StencilOperation.Keep,
ReferenceStencil = 1,
DepthBufferEnable = false,
};
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s1, null, a);
spriteBatch.Draw(huh, Vector2.Zero, Color.White); //The mask
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s2, null, a);
spriteBatch.Draw(color, Vector2.Zero, Color.White); //The background
spriteBatch.End();