Перший крок - сказати відеокарті, що нам потрібен буфер трафарету. Для цього під час створення GraphicsDeviceManager ми встановлюємо PreferredDepthStencilFormat на DepthFormat.Depth24Stencil8, щоб насправді трафарет писати.
graphics = new GraphicsDeviceManager(this) {
PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8
};
AlphaTestEffect використовується для встановлення системи координат і фільтра пікселів з альфа-альфа-тесту, які проходять альфа-тест. Ми не збираємось встановлювати жодні фільтри та встановлювати систему координат на порт перегляду.
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 піксель запишеться до StencilBuffer зі значенням ReferenceStencil.
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 (альфа-пікселі від маски) і не впливає на 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();