OpenGL超级宝典笔记——遮挡查询 [转]
在一个场景中,如果有有些物体被其他物体遮住了不可见。那么我们就不需要绘制它。在复杂的场景中,这可以减少大量的顶点和像素的处理,大幅度的提高帧率。遮挡查询就是允许我们判断一组图形在进行了深度测试之后是否可见。
遮挡查询之前
为了显示遮挡查询对性能的提升,我们需要一个对照组(不使用遮挡查询来渲染场景)。
首先我们先绘制“主遮挡物”。这个主遮挡物不需要太多的细节,一般是墙,天花板,地板之类的物体。在下面的例子中我们,使用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左右。

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

为什么要选择包围体去判断遮挡,而不是直接用球体的。因为球体太过于复杂,包含的顶点也多,渲染较耗时。遮挡之所以能够提升性能,就是我们可以在无 光照,无纹理等其他效果的下,并且不需要改变缓冲区的值,先渲染简单的包围体。通过这些包围体进行深度测试,我们就能判断出哪些物体被遮挡,那些被遮挡的 物体就可以不需要被渲染(不需要调用任何渲染该物体的命令),如果这个物体拥有非常多的顶点,那么遮挡在这个时候就能大幅度的提高性能。
遮挡查询
遮挡查询的步骤:
首先为这些物体生成查询对象ID 调用glGenQueries
调用glBeginQuery开始遮挡查询
渲染包围体
调用glEndQuery 结束遮挡查询
调用glGetQueryObject[u]iv,根据ID提取遮挡查询的结果,并根据结果进行相应的操作
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超级宝典笔记——遮挡查询 [转]的更多相关文章
- OpenGL超级宝典笔记----框架搭建
自从工作后,总是或多或少的会接触到客户端3d图形渲染,正好自己对于3d图形的渲染也很感兴趣,所以最近打算从学习OpenGL的图形API出发,进而了解3d图形的渲染技术.到网上查了一些资料,OpenGL ...
- OpenGL超级宝典笔记——深度纹理和阴影 【转】
目录[-] 光源视角 新型的纹理 深度纹理的大小 首先绘制阴影 然后是光照 投影阴影贴图 阴影比较 之前我们介绍过简单的把物体压平到投影平面来制造阴影.但这种阴影方式有其局限性(如投影平面须是平面). ...
- OpenGL超级宝典笔记----渲染管线
在OpenGL中任何事物都在3D空间中,但是屏幕和窗口是一个2D像素阵列,所以OpenGL的大部分工作都是关于如何把3D坐标转变为适应你屏幕的2D像素.3D坐标转为2D坐标的处理过程是由OpenGL的 ...
- 【转】OpenGL超级宝典笔记——纹理映射Mipmap
原文地址 http://my.oschina.net/sweetdark/blog/177812 , 感谢作者,若非法转载请联系本人. 目录[-] Mipmapping Mipmap过滤 构建Mip层 ...
- 【转载】OpenGL超级宝典笔记——GLSL语言基础
变量 GLSL的变量命名方式与C语言类似.变量的名称可以使用字母,数字以及下划线,但变量名不能以数字开头,还有变量名不能以gl_作为前缀,这个是GLSL保留的前缀,用于GLSL的内部变量.当然还有一些 ...
- OpenGL超级宝典笔记——画三角形(转)
http://my.oschina.net/sweetdark/blog/161002 学习了画线的知识,我们可以使用GL_LINE_LOOP来画闭合的多边形.但是使用这种方式画出来的只有线框,多边形 ...
- OpenGL超级宝典笔记——贝塞尔曲线和曲面(转)
http://my.oschina.net/sweetdark/blog/183721 参数方程表现形式 在中学的时候,我们都学习过直线的参数方程:y = kx + b;其中k表示斜率,b表示截距(即 ...
- win8+VS2012搭建OpenGL超级宝典的环境
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/booirror/article/details/36957799 自从公司搬到腾讯附近,每天上班都迟 ...
- OpenGL超级宝典visual studio 2013开发环境配置,GLTools
做三维重建需要用到OpenGL,开始看<OpenGL超级宝典>,新手第一步配置环境就折腾了一天,记录下环境的配置过程. <超级宝典>中的例子使用了GLEW,freeglut以及 ...
随机推荐
- shell用curl抓取页面乱码,参考一下2方面(转)
1.是用curl抓取的数据是用类似gzip压缩后的数据导致的乱码.乱码:curl www.1ting.com |more乱码:curl -H "Accept-Encoding: gzip&q ...
- 次小生成树(poj1679)
The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 20737 Accepted: 7281 D ...
- 树链剖分-点的分治(dis[i]+dis[j]==k的点对数量)
poj2114 Boatherds Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 1195 Accepted: 387 ...
- OA之为用户设置角色和为用户设置权限
1.为用户设置角色 { Layout = null; } @using OA.Model <!DOCTYPE html> <html> <head> <met ...
- oracle之用户名密码包含特殊字符时候怎么使用sqlplus登录
oracle有时候用户密码包含一些特殊字符直接登录会报错,需要使用以下方式登录sqlplus sqlplus 'username/"password"' PS:整体使用单引号括起来 ...
- Mybatis解决sql中like通配符模糊匹配 构造方法覆盖 mybits 增删改
<select id="getRecByNameWildcard" parameterType="Student" resultMap="res ...
- Django - 路由层(URLconf)
一.django 静态文件配置 /mysite1/settings.py STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_ ...
- window 安装 Twisted 遇到的问题
scapy 需要Twisted17.1.0, Twisted报错 building 'twisted.test.raiser' extension 用Twisted 16.1.0 可以安装,使用时 I ...
- qt——类大全
qt类总结地址 http://www.kuqin.com/qtdocument/ QWidget.QDialog及QMainWindow的区别 QWidget类是所有用户界面对象的基类. 窗口部件是用 ...
- Tensorflow中卷积的padding方式
根据tensorflow中的Conv2D函数,先定义几个基本符号: 输入矩阵W*W,这里只考虑输入宽高相等的情况,如果不相等,推导方法一样 filter矩阵F*F,卷积核 stride值S,步长 输出 ...