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

遮挡查询之前

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

首先我们先绘制“主遮挡物”。这个主遮挡物不需要太多的细节,一般是墙,天花板,地板之类的物体。在下面的例子中我们,使用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超级宝典笔记----框架搭建

    自从工作后,总是或多或少的会接触到客户端3d图形渲染,正好自己对于3d图形的渲染也很感兴趣,所以最近打算从学习OpenGL的图形API出发,进而了解3d图形的渲染技术.到网上查了一些资料,OpenGL ...

  2. OpenGL超级宝典笔记——深度纹理和阴影 【转】

    目录[-] 光源视角 新型的纹理 深度纹理的大小 首先绘制阴影 然后是光照 投影阴影贴图 阴影比较 之前我们介绍过简单的把物体压平到投影平面来制造阴影.但这种阴影方式有其局限性(如投影平面须是平面). ...

  3. OpenGL超级宝典笔记----渲染管线

    在OpenGL中任何事物都在3D空间中,但是屏幕和窗口是一个2D像素阵列,所以OpenGL的大部分工作都是关于如何把3D坐标转变为适应你屏幕的2D像素.3D坐标转为2D坐标的处理过程是由OpenGL的 ...

  4. 【转】OpenGL超级宝典笔记——纹理映射Mipmap

    原文地址 http://my.oschina.net/sweetdark/blog/177812 , 感谢作者,若非法转载请联系本人. 目录[-] Mipmapping Mipmap过滤 构建Mip层 ...

  5. 【转载】OpenGL超级宝典笔记——GLSL语言基础

    变量 GLSL的变量命名方式与C语言类似.变量的名称可以使用字母,数字以及下划线,但变量名不能以数字开头,还有变量名不能以gl_作为前缀,这个是GLSL保留的前缀,用于GLSL的内部变量.当然还有一些 ...

  6. OpenGL超级宝典笔记——画三角形(转)

    http://my.oschina.net/sweetdark/blog/161002 学习了画线的知识,我们可以使用GL_LINE_LOOP来画闭合的多边形.但是使用这种方式画出来的只有线框,多边形 ...

  7. OpenGL超级宝典笔记——贝塞尔曲线和曲面(转)

    http://my.oschina.net/sweetdark/blog/183721 参数方程表现形式 在中学的时候,我们都学习过直线的参数方程:y = kx + b;其中k表示斜率,b表示截距(即 ...

  8. win8+VS2012搭建OpenGL超级宝典的环境

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/booirror/article/details/36957799 自从公司搬到腾讯附近,每天上班都迟 ...

  9. OpenGL超级宝典visual studio 2013开发环境配置,GLTools

    做三维重建需要用到OpenGL,开始看<OpenGL超级宝典>,新手第一步配置环境就折腾了一天,记录下环境的配置过程. <超级宝典>中的例子使用了GLEW,freeglut以及 ...

随机推荐

  1. ThinkPHP简单结构介绍!

    thinkPHP简单结构介绍: application : 应用 extend:扩展 扩展内库 public:入口文件 index.php 在里面 runtime:缓存文件(里面的文件可以随便删除) ...

  2. 【HTTP header】【Access-Control-Allow-Credentials】跨域Ajax请求时是否带Cookie的设置

    1. 无关Cookie跨域Ajax请求 客户端 以 Jquery 的 ajax 为例: $.ajax({ url : 'http://remote.domain.com/corsrequest', d ...

  3. .net配置404错误页面

    如果你的网站出现一堆让人看不懂的报错,那么你就不是一个合格的程序员,也不是一个合格的站长. 下面的方面可以帮助你的网站远离让人头大的页面. 第一步:配置web.config 打开web.config, ...

  4. java 中的控制台端口的输入

    java 中基于控制台端的输入时最基本的操作. 第一步 导入Scanner类 import java.util.Scanner; 第二步 创建输入对象 input Scanner input=new ...

  5. Mac下通过远程桌面向Windows发送Ctrl+Alt+Delete

    今天在Mac中通过远程桌面给Windows安装Git Extensions,在安装程序自动关闭资源管理器时,Windows无响应,桌面显示黑屏.于是,想通过Ctrl+Alt+Delete快捷键调出对话 ...

  6. 设计模式之——浅谈strategy模式(策略模式)

    strategy模式,即策略模式.个人觉得吧,策略模式更多的是一种思维方式. 首先我们要知道,为什么需要策略模式.举个例子,比如用程序输出今天下午去玩什么. PlayGame 玩游戏 package ...

  7. 评论抓取:Python爬取微信在APPStore上的评论内容及星级

    #完整程序如下: import requests import re def getHTMLText(url): try: r = requests.get(url) r.raise_for_stat ...

  8. (2.13)Mysql之SQL基础——触发器

    (2.13)Mysql之SQL基础——触发器 关键词:Mysql触发器 1.一般形式 -- 0.查看触发器[1]SHOW TRIGGERS;[2]SELECT * FROM `information_ ...

  9. centos shell脚本编程2 if 判断 case判断 shell脚本中的循环 for while shell中的函数 break continue test 命令 第三十六节课

    centos  shell脚本编程2 if 判断  case判断   shell脚本中的循环  for   while   shell中的函数  break  continue  test 命令   ...

  10. 用nginx的反向代理机制解决前端跨域问题在nginx上部署web静态页面

    用nginx的反向代理机制解决前端跨域问题在nginx上部署web静态页面 1.什么是跨域以及产生原因 跨域是指a页面想获取b页面资源,如果a.b页面的协议.域名.端口.子域名不同,或是a页面为ip地 ...