跳转到内容

OpenGL 编程/迷你门户递归

来自维基教科书,开放世界中的开放书籍

绘制门户内的门户引入了一套新的极端情况。

递归门户

[编辑 | 编辑源代码]

以下是算法概述

/* TODO: Code here */

剪切门户场景

[编辑 | 编辑源代码]
在模板缓冲区中进行形状相交

模板缓冲区将逐步创建

  • 绘制门户形状
  • 绘制其与子门户形状的交集以获得新的门户剪切形状
  • 重复

获得交集并不简单

  • 使用 GL_INCR 绘制外部门户
  • 使用 GL_INCR 绘制子门户
  • 使用 GL_DECR 重新绘制外部门户
  • 像素>=1 代表外部门户和子门户之间的交集

从技术上讲,我们可以在每个递归级别保存和恢复缓冲区(而不是从头开始重新绘制所有门户形状),但这会很慢,并且使用 OpenGL ES 2 无法检索模板缓冲区数据。

外部门户和子门户完全是同一个对象,只有视图矩阵会改变。因此,我们向函数传递一个视图堆栈,并在每个新的递归级别推送一个新的视图矩阵。

请注意,模板绘制必须支持体积门户,这意味着它可能会被写入多次(因此您无法仅使用 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-
}

如果您需要调试并查看形状的外观,请考虑使用 fill_screen 技巧

门户内的门户内的门户

< OpenGL 编程

浏览并下载 完整代码
华夏公益教科书