Il primo passo è dire alla scheda grafica che abbiamo bisogno del buffer di stencil. Per fare ciò quando crei GraphicsDeviceManager, impostiamo PreferredDepthStencilFormat su DepthFormat.Depth24Stencil8 in modo che ci sia effettivamente uno stencil su cui scrivere.
graphics = new GraphicsDeviceManager(this) {
PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8
};
AlphaTestEffect viene utilizzato per impostare il sistema di coordinate e filtrare i pixel con alfa che superano il test alfa. Non imposteremo alcun filtro e il sistema di coordinate sulla porta di visualizzazione.
var m = Matrix.CreateOrthographicOffCenter(0,
graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,
graphics.GraphicsDevice.PresentationParameters.BackBufferHeight,
0, 0, 1
);
var a = new AlphaTestEffect(graphics.GraphicsDevice) {
Projection = m
};
Successivamente è necessario impostare due DepthStencilStates. Questi stati determinano quando lo SpriteBatch esegue il rendering sullo stencil e quando lo SpriteBatch esegue il rendering sul BackBuffer. Siamo principalmente interessati a due variabili StencilFunction e StencilPass.
- StencilFunction determina quando SpriteBatch disegna singoli pixel e quando verranno ignorati.
- StencilPass determina quando i pixel disegnati hanno effetto sullo Stencil.
Per il primo DepthStencilState impostiamo StencilFunction su CompareFunction. Questo fa sì che lo StencilTest abbia esito positivo e quando lo StencilTest SpriteBatch esegue il rendering di quel pixel. StencilPass è impostato su StencilOperation. Sostituire significa che quando lo StencilTest ha esito positivo, quel pixel verrà scritto nello StencilBuffer con il valore di ReferenceStencil.
var s1 = new DepthStencilState {
StencilEnable = true,
StencilFunction = CompareFunction.Always,
StencilPass = StencilOperation.Replace,
ReferenceStencil = 1,
DepthBufferEnable = false,
};
In breve, lo StencilTest passa sempre, l'immagine viene disegnata sullo schermo normalmente e per i pixel disegnati sullo schermo un valore 1 viene memorizzato nello StencilBuffer.
Il secondo DepthStencilState è leggermente più complicato. Questa volta vogliamo disegnare sullo schermo solo quando il valore in StencilBuffer è. Per ottenere ciò, impostiamo StencilFunction su CompareFunction.LessEqual e ReferenceStencil su 1. Ciò significa che quando il valore nel buffer dello stencil è 1, StencilTest avrà esito positivo. Impostazione di StencilPass su StencilOperation. Keep fa sì che StencilBuffer non si aggiorni. Questo ci consente di disegnare più volte utilizzando la stessa maschera.
var s2 = new DepthStencilState {
StencilEnable = true,
StencilFunction = CompareFunction.LessEqual,
StencilPass = StencilOperation.Keep,
ReferenceStencil = 1,
DepthBufferEnable = false,
};
In breve, StencilTest passa solo quando StencilBuffer è inferiore a 1 (i pixel alfa della maschera) e non ha effetto su StencilBuffer.
Ora che abbiamo impostato i nostri DepthStencilStates. Possiamo effettivamente disegnare usando una maschera. Disegna semplicemente la maschera usando il primo DepthStencilState. Ciò avrà effetto sia sul BackBuffer che sullo StencilBuffer. Ora che il buffer dello stencil ha un valore di 0 in cui la maschera aveva la trasparenza e 1 in cui conteneva il colore, possiamo usare StencilBuffer per mascherare le immagini successive.
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s1, null, a);
spriteBatch.Draw(huh, Vector2.Zero, Color.White); //The mask
spriteBatch.End();
Il secondo SpriteBatch utilizza il secondo DepthStencilStates. Indipendentemente da ciò che disegni, solo i pixel in cui StencilBuffer è impostato su 1 supereranno il test dello stencil e verranno disegnati sullo schermo.
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s2, null, a);
spriteBatch.Draw(color, Vector2.Zero, Color.White); //The background
spriteBatch.End();
Di seguito è riportato l'intero codice nel metodo Draw, non dimenticare di impostare PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8 nel costruttore del gioco.
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();