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左右,固然還要看觀察場景的角度。若是從某個角度看大部分球體都被遮擋了,性能的提高更大。

相關文章
相關標籤/搜索