一、Queries
你可以向OpenGL的渲染管线“提问题”,问它现在的某个状况如何。就像上课提问要举手,你向OpenGL提问也要先举手。举手的时候,老师不知道你想问啥,但已经知道你有问题了。举手的操作就是glGenQueries(1, &query),query可以理解为“你”这个提问的人。和glGenTextures()很像有木有!
举手之后就提问,使用glBeginQuery(target, query);glEndQuery(target)。其中target就是你想问的问题。OpenGL已经预置了如GL_SAMPLES_PASSED之类的问题类型。
GL_SAMPLES_PASSED问的是:在BeginQuery和EndQuery之间的渲染有多少次采样通过了深度测试。之后会把结果告诉query(不是直接赋值哦,是把结果告诉这个人)。
想要取得这个值,就要使用glGetQueryObjectuiv(query, GL_QUERY_RESULT, &result);让query把结果存进result。
利用以上特性,可以有一个优化渲染的想法:将复杂的图形先简化(通常情况下bounding box即可),然后渲染简化的模型。然后向pipeline提问:有多少采样通过了深度测试呀?如果返回不为0,那说明这个图形在界面中是可见的,然后就可以渲染他了。示例代码如下:
// code 1: basic query
glGenQueries(1, &query); // 举手
glBeginQuery(GL_SAMPLES_PASSED, query); // 提问
RenderSimplifiedObject(object); // 渲染bounding box
glEndQuery(GL_SAMPLES_PASSED); // 结束提问
glGetQueryObjectuiv(query, GL_QUERY_RESULT, &result); // 统计
if (the_result != 0)
RenderRealObject(object); // 渲染真图形
另外一种情况是,BeginQuery和EndQuery之间的渲染太多啦,在你取值时还没统计完,这会导致OpenGL程序卡在这,等它统计。这就导致OpenGL程序优化不行。有个方法可以检查这个值统计好没,就是glGetQueryObjectuiv(query, GL_QUERY_RESULT_AVAILABLE, &result)。统计好了,result==GL_TRUE;没统计好,result==GL_FALSE。
再回到之前这个优化渲染的想法:如果统计简化图形(bounding box)渲染的深度测试通过数量时发生了上述问题,则完全不必担心:result没有准备好就算了呗,我们只想知道跳过这个图形安不安全,result没好咱就不等了,只要把确定可以跳过的跳过就行了。优化过的代码如下:
// code 2: 老子不等啦
glGenQueries(1, &query);
glBeginQuery(GL_SAMPLES_PASSED, query);
RenderSimplifiedObject(object);
glEndQuery(GL_SAMPLES_PASSED);
glGetQueryObjectuiv(query, GL_QUERY_RESULT_AVAILABLE, &result); // 问它result能用了嘛
if (result == GL_TRUE) // 能用啦
glGetQueryObjectuiv(the_query, GL_QUERY_RESULT, &result); // 告诉我通过多少吧
else
result = 1; // result还不能用?那老子不等了
if (result != 0)
RenderRealObject(object);
像这样取得具体数值固然可行,但我们的工作并不建立在具体数值上。因此,可以进一步使用条件渲染(Conditional Render)。所有夹在glBeginConditionalRender(query, mode)和glEndConditionalRender(query, mode)之间的“渲染指令”将会在query得到0时全部跳过。
其中mode可以选GL_QUERY_WAIT和GL_QUERY_NO_WAIT,表示要不要等query统计完返回值。如过不等,则若result尚未available时,后续渲染指令就会无条件执行。因此,上述代码还能再优化:
// code 3: 老子都不用取回来
glGenQueries(1, &query);
glBeginQuery(GL_SAMPLES_PASSED, query);
RenderSimplifiedObject(object);
glEndQuery(GL_SAMPLES_PASSED);
// 条件渲染
glBeginConditionalRender(query, GL_QUERY_NO_WAIT); // 根据query取得的值决定接下来的渲染:若还没取到值则无条件渲染
RenderRealObject(object);
glEndConditionalRender();
像这种统计采样点的query,叫做Occlusion Queries。