411 REFERENCE · Wikibooks
OpenGL Programming/Mini-Portal Recursive
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
Drawing portals-within-portals introduces a new set of corner cases.
Recursive portals
Here's the algorithm overview:
/* TODO: Code here */
Clipping the portal scene
thumb|Shape intersection in the stencil buffer
The stencil buffer will be created incrementally:
draw a portal shape
draw its intersection with the sub-portal shape to get a new portal clip shape
repeat
To get the intersection is not straightforward:
draw the outer portal using GL_INCR
draw the sub-portal using GL_INCR
re-draw the outer portal using GL_DECR
pixels >= 1 represent the intersection between the outer portal and the sub-portal
Technically we could save and restore the buffer at each level of recursion (instead of re-drawing all the portal shapes from scratch), but this would be slow, and retrieving the stencil buffer data is not possible with OpenGL ES 2.
The outer portal and sub-portal are exactly the same object, only the View matrix changes. So we pass a stack of view to the function, and push a new View matrix with each new level of recursion.
Note that the stencil drawing must support volumetric portals, which means it may be written to more than once (so you cannot achieve intersection using just GL_INVERT).
void draw_portal_stencil(vector<glm::mat4> view_stack, Mesh* portal) {
GLboolean save_color_mask[4];
GLboolean save_depth_mask;
glGetBooleanv(GL_COLOR_WRITEMASK, save_color_mask);
glGetBooleanv(GL_DEPTH_WRITEMASK, &save_depth_mask);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glStencilFunc(GL_NEVER, 0, 0xFF);
glStencilOp(GL_INCR, GL_KEEP, GL_KEEP); // draw 1s on test fail (always)
// draw stencil pattern
glClear(GL_STENCIL_BUFFER_BIT); // needs mask=0xFF
glUniformMatrix4fv(uniform_v, 1, GL_FALSE, glm::value_ptr(view_stack[0]));
portal->draw();
for (unsigned int i = 1; i < view_stack.size() - 1; i++) { // -1 to ignore last view
// Increment intersection for current portal
glStencilFunc(GL_EQUAL, 0, 0xFF);
glStencilOp(GL_INCR, GL_KEEP, GL_KEEP); // draw 1s on test fail (always)
glUniformMatrix4fv(uniform_v, 1, GL_FALSE, glm::value_ptr(view_stack[i]));
portal->draw();
// Decremental outer portal -> only sub-portal intersection remains
glStencilFunc(GL_NEVER, 0, 0xFF);
glStencilOp(GL_DECR, GL_KEEP, GL_KEEP); // draw 1s on test fail (always)
glUniformMatrix4fv(uniform_v, 1, GL_FALSE, glm::value_ptr(view_stack[i-1]));
portal->draw();
}
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
/* Fill 1 or more */
glStencilFunc(GL_LEQUAL, 1, 0xFF);
glColorMask(save_color_mask[0], save_color_mask[1], save_color_mask[2], save_color_mask[3]);
glDepthMask(save_depth_mask);
glUniformMatrix4fv(uniform_v, 1, GL_FALSE, glm::value_ptr(view_stack.back()));
// -Ready to draw main scene-
}
Think about the fill_screen trick if you need to debug and see what your shape looks like.
thumb|Portals within portals within portals