问题
My game is in C# using OpenTK, which is a wrapper around OpenGL. I'm rendering my mini-map as shown below. The problem is that objects aren't clipped at the edge, so they bleed over. I could render a thicker border to hide it, but that's not ideal and it won't work for larger objects. There could be trajectory lines and other things which also bleed beyond the bounds. I'm just rendering these using basic primitives.
How can I go about clipping this region? Is there any way to render this to a hidden canvas then copy just the circular region over?
SOLUTION
@BDL answered the question, but I had to tweak a few more things to get it to work, so here is the full solution in case anyone finds it useful:
Enable the stencil buffer in the GameWindow constructor GraphicsMode param.
private static OpenTK.Graphics.GraphicsMode GraphicsMode {
get {
var defaultMode = OpenTK.Graphics.GraphicsMode.Default;
var custom = new OpenTK.Graphics.GraphicsMode(
defaultMode.ColorFormat,
defaultMode.Depth,
1, // enable stencil buffer
defaultMode.Samples,
defaultMode.ColorFormat,
defaultMode.Buffers,
defaultMode.Stereo);
return custom;
}
}
public BaseHelioUI(int windowWidth, int windowHeight)
: base(
windowWidth,
windowHeight,
BaseHelioUI.GraphicsMode,
"",
GameWindowFlags.Default )
{
Then use this block to draw the stencil, which is a circle in my case:
GL.Enable(EnableCap.StencilTest);
GL.StencilFunc(StencilFunction.Always, 1, 0xFF);
GL.StencilOp( StencilOp.Keep, StencilOp.Keep, StencilOp.Replace );
GL.Clear(ClearBufferMask.StencilBufferBit);
// draw background & outline
this.Renderer.DrawCircle(
this.MinimapScreenCenter,
SENSOR_RANGE_IN_METERS * this.GameMetersToMinimapUnitsFactor,
Colors.ReduceAlpha( Colors.Black, MINIMAP_BACKGROUND_OPACITY )
//Colors.ReduceAlpha( Colors.DarkGrey, MINIMAP_BACKGROUND_OUTLINE_OPACITY),
//MINI_MAP_BORDER_WIDTH
);
GL.Enable(EnableCap.StencilTest);
GL.StencilFunc(StencilFunction.Equal, 1, 0xFF);
GL.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Keep);
Then draw everything else you want within that region, which are the items on the minimap in my case.
Then when you're done with the stencil usage, just clear it so everything else will render okay. In my case, this is at the end of the RenderMinimap method.
// disable stencil
GL.Clear(ClearBufferMask.StencilBufferBit);
GL.Disable(EnableCap.StencilTest);
Here's the resulting minimap properly clipping items at the edge of the circle. Looks great. (the ship is in game space, not on the minimap)
回答1:
Clipping in arbitrary regions is possible by using the stencil buffer:
First clear the stencil buffer with 0 (glClear
).
Next, render the background circle (the clipping region) with the following stencil operations enabled:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
This will set the stencil buffer to 1 for all pixels covered by the circle.
When you now render the content that should be displayed inside the circle use the following settings:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
Here, the stencil test only succeeds if there is already a 1 stored in the stencil buffer at that locations (which is only true for pixels inside the clipping region). Everywhere else the stencil test will fail and nothing will be rendered.
Don't forget to make sure that your framebuffer has a stencil buffer available.
来源:https://stackoverflow.com/questions/50055872/how-to-clip-circular-region-in-opentk-mini-map