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();
}

image

如今咱们在每个单元格中,放置一个高度分挌化的纹理球体。这些球体多是被遮挡物,也多是遮挡物。测试

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左右。ui

image

包围体

在遮挡查询中,若是一个物体的边界都是不可见的,那么就表明这个物体不可见。因此咱们只需检测物体外围的包围体可见,就能够判断物体是否被遮挡。物体外围的包围体包含着整个物体,这也就意味着包围体的体积是大于等于物体的体积的。对于一个球体来讲,包围体能够有不少种,最多见的就是立方盒子,四面体等。spa

image

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

遮挡查询

遮挡查询的步骤:code

  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); 
  } 
}

image

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

相关文章
相关标签/搜索