转自:http://www.cnblogs.com/mazhenyu/p/5083026.html

在一个场景中,如果有有些物体被其他物体遮住了不可见。那么我们就不需要绘制它。在复杂的场景中,这可以减少大量的顶点和像素的处理,大幅度的提高帧率。遮挡查询就是允许我们判断一组图形在进行了深度测试之后是否可见。

遮挡查询之前

为了显示遮挡查询对性能的提升,我们需要一个对照组(不使用遮挡查询来渲染场景)。

首先我们先绘制“主遮挡物”。这个主遮挡物不需要太多的细节,一般是墙,天花板,地板之类的物体。在下面的例子中我们,使用6面墙来组成这个主遮挡物。

void DrawOccluder()
{
  glColor3f(0.5f, 0.25f, 0.0f);   glPushMatrix();
    glScalef(30.0f, 30.0f, 1.0f);
    glTranslatef(0.0f, 0.0f, 50.0f);
    glutSolidCube(10.0f);
    glTranslatef(0.0f, 0.0f, -100.0f);
    glutSolidCube(10.0f);
  glPopMatrix();   glPushMatrix();
    glScalef(1.0f, 30.0f, 30.0f);
    glTranslatef(50.0f, 0.0f, 0.0f);
    glutSolidCube(10.0f);
    glTranslatef(-100.0f, 0.0f, 0.0f);
    glutSolidCube(10.0f);
  glPopMatrix();   glPushMatrix();
    glScalef(30.0f, 1.0f, 30.0f);
    glTranslatef(0.0f, 50.0f, 0.0f);
    glutSolidCube(10.0f);
    glTranslatef(0.0f, -100.0f, 0.0f);
    glutSolidCube(10.0f);
  glPopMatrix();
}

现在我们在每一个单元格中,放置一个高度分挌化的纹理球体。这些球体可能是被遮挡物,也可能是遮挡物。

void DrawSphere(GLint sphereNum) 
{  ...
    glutSolidSphere(50.0f, 200, 200); 
... }void DrawModels(void) 
{     //开启纹理 自动生成纹理坐标
  glEnable(GL_TEXTURE_2D); 
  glEnable(GL_TEXTURE_GEN_S); 
  glEnable(GL_TEXTURE_GEN_T);    //绘制27个不同颜色的球体
  for (r = 0; r < 3; r++) 
  { for (g = 0; g < 3; g++) 
    { 
      for (b = 0; b < 3; b++) 
      { 
        glColor3f(r * 0.5f, g * 0.5f, b * 0.5f);          glPushMatrix(); 
        glTranslatef(100.0f * r - 100.0f,  
          100.0f * g - 100.0f,  
          100.0f * b - 100.0f); 
        DrawSphere((r*9)+(g*3)+b); 
        glPopMatrix(); 
      } 
    } 
  }    glDisable(GL_TEXTURE_2D); 
  glDisable(GL_TEXTURE_GEN_S); 
  glDisable(GL_TEXTURE_GEN_T);  }

在我的机器上没有遮挡查询下渲染的帧率是20左右。

包围体

在遮挡查询中,如果一个物体的边界都是不可见的,那么就代表这个物体不可见。所以我们只需检测物体外围的包围体可见,就可以判断物体是否被遮挡。物 体外围的包围体包含着整个物体,这也就意味着包围体的体积是大于等于物体的体积的。对于一个球体来说,包围体可以有很多种,最常见的就是立方盒子,四面体 等。

为什么要选择包围体去判断遮挡,而不是直接用球体的。因为球体太过于复杂,包含的顶点也多,渲染较耗时。遮挡之所以能够提升性能,就是我们可以在无 光照,无纹理等其他效果的下,并且不需要改变缓冲区的值,先渲染简单的包围体。通过这些包围体进行深度测试,我们就能判断出哪些物体被遮挡,那些被遮挡的 物体就可以不需要被渲染(不需要调用任何渲染该物体的命令),如果这个物体拥有非常多的顶点,那么遮挡在这个时候就能大幅度的提高性能。

遮挡查询

遮挡查询的步骤:

  1. 首先为这些物体生成查询对象ID 调用glGenQueries

  2. 调用glBeginQuery开始遮挡查询

  3. 渲染包围体

  4. 调用glEndQuery 结束遮挡查询

  5. 调用glGetQueryObject[u]iv,根据ID提取遮挡查询的结果,并根据结果进行相应的操作

  6. glDeleteQueries 删除ID,回收资源

查询对象的标识符(ID/名称)是一个无符号整数,我们可以通过glGenQueries函数生成,也可以自己定义。一般用OpenGL提供的glGenQueries会比较方便。

void glGenQueries(GLsizei n, GLuint *ids);

第一个参数是生成ID的个数,第二个参数是来存放这些ID的数组。0是保留的ID,不会被产生。我们还可以通过glIsQuery来判断一个ID是否是一个遮挡查询对象的ID。

void glIsQuery(GLuint id);

如果是返回GL_TRUE,不是则返回GL_FALSE。

有了遮挡查询对象的ID后,可以开始遮挡查询了。例如

glBeginQuery(GL_SAMPLES_PASSED, 1);
glBegin(GL_TRIANGLES);
  glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, 5.0f, 0.0f);
glVertex3f(6.0f, 20.0f, 0.0f);
glEnd();
glEndQuery(GL_SAMPLES_PASSED);

void glBeginQuery(GLenum target, GLuint id);

其中target必须是GL_SAMPLES_PASSED. id是用来标识这次遮挡查询的ID。

void glEndQuery(GLenum target);结束这次遮挡查询,其中target必须是GL_SAMPLES_PASSED。

在完成对需要遮挡查询的物体渲染之后,我们需要提取遮挡查询的结果,可以通过glGetQueryObject[u]iv来提取结果,函数将返回片段或采样的数量。

void glGetQueryObjectiv(GLenum id, GLenum pname, GLint *param);

void glGetQueryObjectuiv(GLenum id, GLenum pname, GLuint *param);

id是这个遮挡查询对象的id,pname如果是GL_QUERY_RESULT, param将包含了通过深度测试的片段或样本(如果启用了多重采样)的数量,如果数量为0,则表示这个物体完全被遮挡。

在完成遮挡查询操作时,可能会有延迟。我们可以通过设置pname为GL_QUERY_RESULT_AVAILABLE来检查是否完成了。如果遮挡查询有效地完成了,则param将为GL_TRUE,否则为GL_FALSE.

例:

int count = 1000; //等待1000次循环
GLuint queryReady = GL_FALSE;
while (!queryReady && count--)
{
glGetQueryObjectuiv(1, GL_QUERY_RESULT_AVAILABLE, &queryReady);
} GLuint samples; glGetQueryObjectuiv(1, GL_QUERY_RESULT, &samples);
if(samples > 0)
DrawSomething();

使用完遮挡查询对象之后,调用glDeleteQueries回收资源。

void glDeleteQueries(GLsizei n, const GLuint *ids);

修改前面的例子,我们可以先渲染27个球体的包围体,进行遮挡查询,如果有哪个包围体被完全遮挡,我们就不需要绘制这个球体了。代码片段如下:

void DrawModels(void) 

GLint r, g, b;  //绘制主遮挡物
DrawOccluder();  //在绘制包围体时,越简单越好。关掉纹理,光照等等。
//不需要往缓冲区中写值。
glShadeModel(GL_FLAT); 
glDisable(GL_LIGHTING); 
glDisable(GL_COLOR_MATERIAL); 
glDisable(GL_NORMALIZE); 
glDepthMask(GL_FALSE); 
glColorMask(0, 0, 0, 0);  // 画27个立方体
for (r = 0; r < 3; r++) 

for (g = 0; g < 3; g++) 

for (b = 0; b < 3; b++) 

if (showBoundingVolume) 
glColor3f(r * 0.5f, g * 0.5f, b * 0.5f);  glPushMatrix(); 
glTranslatef(100.0f * r - 100.0f,  
100.0f * g - 100.0f,  
100.0f * b - 100.0f); 
//开始遮挡查询
glBeginQuery(GL_SAMPLES_PASSED, queryIDs[(r*9)+(g*3)+b]); 
//绘制包围体
glutSolidCube(100.0f); 
//结束遮挡查询
glEndQuery(GL_SAMPLES_PASSED); 
glPopMatrix(); 


} //恢复正常的渲染状态
glDisable(GL_POLYGON_STIPPLE); 
glShadeModel(GL_SMOOTH); 
glEnable(GL_LIGHTING); 
glEnable(GL_COLOR_MATERIAL); 
glEnable(GL_NORMALIZE); 
glColorMask(1, 1, 1, 1); 
glDepthMask(GL_TRUE);  //开启纹理 自动生成纹理坐标
glEnable(GL_TEXTURE_2D); 
glEnable(GL_TEXTURE_GEN_S); 
glEnable(GL_TEXTURE_GEN_T);  //绘制27个不同颜色的球体
for (r = 0; r < 3; r++) 

for (g = 0; g < 3; g++) 

for (b = 0; b < 3; b++) 

glColor3f(r * 0.5f, g * 0.5f, b * 0.5f);  glPushMatrix(); 
glTranslatef(100.0f * r - 100.0f,  
100.0f * g - 100.0f,  
100.0f * b - 100.0f); 
//函数中根据,遮挡查询的结果来判断是否要绘制这个球体
DrawSphere((r*9)+(g*3)+b); 
glPopMatrix(); 


}  glDisable(GL_TEXTURE_2D); 
glDisable(GL_TEXTURE_GEN_S); 
glDisable(GL_TEXTURE_GEN_T); 
}
void DrawSphere(GLint sphereNum) 

  GLboolean occluded = GL_FALSE;    if (occlusionDetection) 
  { 
    GLint passingSamples;      //检查物体是否被完全遮挡
    glGetQueryObjectiv(queryIDs[sphereNum], GL_QUERY_RESULT,  
      &passingSamples); 
    if (passingSamples == 0) 
      occluded = GL_TRUE; 
  } 
  //没有被遮挡则绘制
  if (!occluded) 
  { 
    glutSolidSphere(50.0f, 200, 200); 
  } 
}

有了遮挡查询后,帧率达到了32左右,当然还要看观察场景的角度。如果从某个角度看大部分球体都被遮挡了,性能的提升更大。

在osg中 example_osgocclusionquery
一)演示了osg::OcclusionQueryNode的使用。

OpenGL遮挡查询的更多相关文章

  1. OpenGL 遮挡查询

    原文地址:http://www.linuxidc.com/Linux/2015-02/114036.htm 在一个场景中,如果有有些物体被其他物体遮住了不可见.那么我们就不需要绘制它.在复杂的场景中, ...

  2. OpenGL超级宝典笔记——遮挡查询 [转]

    目录[-] 遮挡查询之前 包围体 遮挡查询 在一个场景中,如果有有些物体被其他物体遮住了不可见.那么我们就不需要绘制它.在复杂的场景中,这可以减少大量的顶点和像素的处理,大幅度的提高帧率.遮挡查询就是 ...

  3. D3D10/11中的遮挡查询的使用

    原文:D3D10/11中的遮挡查询的使用       在D3D10/11中,有D3D10_QUERY/D3D11_QUERY接口,通过QUERY接口,我们可以查询GPU的一些状态,比如GPU的时间戳信 ...

  4. CSharpGL(31)[译]OpenGL渲染管道那些事

    CSharpGL(31)[译]OpenGL渲染管道那些事 +BIT祝威+悄悄在此留下版了个权的信息说: 开始 自认为对OpenGL的掌握到了一个小瓶颈,现在回头细细地捋一遍OpenGL渲染管道应当是一 ...

  5. OpenGL Insights 阅读有感 - Tile Based架构下的性能调校 翻译

    Performance Tunning for Tile-Based Architecture Tile-Based架构下的性能调校 by Bruce Merry GameKnife译 译序 在大概1 ...

  6. opengl入门学习

    OpenGL入门学习 说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640 ...

  7. OpenGL入门学习(转)

    OpenGL入门学习 http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html 说起编程作图,大概还有很多人想起TC的#includ ...

  8. OpenGL理解

    说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色 ...

  9. OpenGL入门学习(转载)

    说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色 ...

随机推荐

  1. 机器学习理论基础学习13--- 隐马尔科夫模型 (HMM)

    隐含马尔可夫模型并不是俄罗斯数学家马尔可夫发明的,而是美国数学家鲍姆提出的,隐含马尔可夫模型的训练方法(鲍姆-韦尔奇算法)也是以他名字命名的.隐含马尔可夫模型一直被认为是解决大多数自然语言处理问题最为 ...

  2. 用户用户组管理:用户管理命令-passwd

    passwd直接回车就是给root设密码.或加root. 普通用户只能改自己的密码.改时直接敲passwd,回车.否则报错. 因为只有root可以在passwd后加用户名.其实最常见的就是不加选项. ...

  3. 面经:Bloomberg Internship第一轮

    上来先问了一个系统设计的问题,一个front end, 一个back end. front end有很多UI,一个UI对10个多customers,back end有许多processor,或者pro ...

  4. LINQ的左连接、右连接、内连接和Lamda表达式实现Left join

    1.左连接: var LeftJoin = from t1 in l1join t2 in l2on t1.ID equals t2.ID into Joinedt12from t3 in Joine ...

  5. VS2010/MFC编程入门之三十一(常用控件:树形控件Tree Control 下)

    前面一节讲了树形控件Tree Control的简介.通知消息以及相关数据结构,本节继续讲下半部分,包括树形控件的创建.CTreeCtrl类的主要成员函数和应用实例. 树形控件的创建 MFC为树形控件提 ...

  6. VS2010/MFC编程入门之三十二(常用控件:标签控件Tab Control 上)

    前面两节鸡啄米讲了树形控件Tree Control,本节开始讲解标签控件Tab Control,也可以称为选项卡控件. 标签控件简介 标签控件也比较常见.它可以把多个页面集成到一个窗口中,每个页面对应 ...

  7. 网站app原型设计工具:axure,Mockups,墨刀

    网站app原型设计工具:axure,Mockups,墨刀 Balsamiq Mockups 3 网站原型设计工具非常高效,非常简单,几分钟就能搞定比axure好用很多 墨刀 - 免费的移动应用原型与线 ...

  8. Ajax jquery的库的简化版本

    Ajax jquery的库的简化版本 (function(){    //面向外界的唯一变量接口!    var myajax = window.myajax = {};    //作者.版本号等等信 ...

  9. 20145101《Java程序设计》第6周学习总结

    20145101<Java程序设计>第6周学习总结 教材学习内容总结 第十章输入和输出 java.io.InputStream.java.io.OutputStream实例分别作为输入.输 ...

  10. Android实践项目汇报(四)

    全国天气客户端 本周学习计划 添加修改功能,完成项目 实际完成情况 1.成功显示当天及后几天的天气信息 通过修改chaxun.java程序,比较JSON数据格式中JSONObject("to ...