Reputation: 2115
How can I achieve a clean separation between 2 viewports?
I have 2 viewports, one takes full screen length, the other one needs to be less or equal to a quarter of the screen size (I want it to be a map). The problem is they keep interfering, I get to see in the small viewport content from the big one.
Here's the display()
function I'm using:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0,0,newWidth,newHeight);
gluPerspective(45,(float)newWidth/(float)newHeight,0.2,500);
//setup view
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
camera.render(crntMode, radiusNew);
//ground
glColor3f(0.5,0.5,0.5);
draw_ground(50,50,2,2,-2);
...
...
...
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(newWidth / 2, newHeight / 2, newWidth / 2, newHeight / 2);
gluPerspective(45,(float)newWidth/(float)newHeight,0.2,500);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
camera.render(mini_map, radiusNew);
//ground
glColor3f(0.5,0.5,0.5);
draw_ground(50,50,2,2,-2);
...
...
...
//swap buffers
glutSwapBuffers();
Both viewport cameras are following the character, one from a third person perspective, the other one from top up. It might be harder to see, but the background from the mini-map viewport is interfering with large one (the large on is acting almost as a background for the mini-map). Sometimes the large viewport covers the small viewport.
Upvotes: 1
Views: 4459
Reputation: 474436
Viewports aren't mini-windows. The viewport is nothing more than a location in the main window where rendering takes place. All rendering still happens in the same framebuffer. The problem happens because both viewports are using the same depth buffer. OpenGL doesn't know that you want the depth in the larger scene to affect the rendering in the smaller one. It's all just one scene, one framebuffer, to OpenGL.
There are many ways to correct this, all with different performance implications/OpenGL requirements:
Clear the depth buffer between rendering the two scenes with glClear(GL_DEPTH_BUFFER_BIT);
. This is the simplest, but the most costly in terms of performance. I only list it here for the sake of completeness; you should instead use:
Draw your big scene first. Then set the viewport to the smaller scene. Turn off depth testing, but leave glDepthMask
on. Then draw a screen-aligned quad who's Z is -1, where the extents of the quad are [-1, 1] in X and Y. The matrices should be identity for both the projection and modelview matrices. This will effectively clear the depth for a part of the scene.
Don't forget to turn depth testing on after drawing the quad. Also don't forget to repair your matrices if needed. This will only work if you're drawing to every pixel of the new viewport (unless you set the quad's color to the intended background color).
Draw your big scene first. Then use glScissor
to set the scissor box to the smaller scene, and use glEnable(GL_SCISSOR_TEST)
to enable scissoring. After that, clear the depth buffer with glClear(GL_DEPTH_BUFFER_BIT)
. Then disable the scissor test and render the smaller scene.
This is like #2, except you don't have to draw a screen-aligned quad. glClear
respects the scissor box when scissor testing is enabled, so it will only clear the scissored area.
I offer this as an alternative instead of the suggested mechanism because OpenGL implementations can get the glClear
and glScissor
behavior wrong. This should be the preferred mechanism.
Employ the depth range. This can affect the quality of your scene, but it will likely be the fastest (note: you shouldn't care unless you have a real reason to care. That is, only do this if profiling shows that performance is a problem). For your main scene, use glDepthRange(0.1, 1.0)
; for your small scene, use glDepthRange(0.0, 0.1)
.
This effectively means that all of the depth values for the small scene will be in front of all of the depth values of the large scene. However, it also means that your large scene will have less depth precision, so z-fighting may be evident.
You can move the 0.1
around as you see fit; you can split the range in half with 0.5, but I personally advise against it. The small scene is less important and z-fighting is less important in the smaller resolution. So you should give more precision to the most important scene.
Render the small scene to an FBO and blit it to the screen. This is the simplest to reason about. Just create some renderbuffers, one for color and one for depth. Stick them in a framebuffer object. Render your small scene there. And then use glBlitFramebuffer
to draw it to the location you want in the default framebuffer.
There are other methods, like employing the stencil test, but these are the ones you could rely on.
Upvotes: 10