OpenGL之視錐體裁剪

OpenGL之視錐體裁剪

圖形引擎繪製任何睇不見(視錐截體之外)的任何物體,都會浪費保貴GPU資源.相機的視錐體定義你所能夠梯到的3D世界空間.視錐體由相機的位置和視野組成.視錐體由六個平面組成.只要進入視錐體內的3D模型都應被渲染.(被其它3D模型遮檔除外).位於視錐體之外則不必渲染.

OpenGL會自動丟棄在視錐體之外的三角形.但OpenGL是逐個三角形進行視錐體裁剪.當你有大量(幾萬個)的三角形進入OpenGL裁剪隊列時.會大大地降低程式性能.

最簡單的優化方式是定義3D球形容器,用於包裹你的3D模型.然後使用視錐體與3D球形進行測試.如果3D球形完全在視錐體之外.即可放棄渲染3D模型.如果3D模型部分或全部進入視錐體內,則交由OpenGL繼序進行三角形裁剪

演示程式中有幾百個隨機移動球體,按空格鍵啟用或禁用『視錐體裁剪』.未啟用之前不到200幀.一但啟動『視錐體裁剪』則超過1000幀.性能大幅提高五倍(視渲染球體的個數而定).程式下載:

 

定義3D球體

typedef struct SPHERE3D{

float    x, y, z;// 中心點

float    radius;// 球體半徑

}SPHERE3D,* SPHERE3D_PTR;

 

定義3D平面

typedef struct PLANE3D_TYP{

float A, B, C;// 平面法線向量

float D;//平面到原點最近距離

}PLANE3D,*PLANE3D_PTR;

 

定義視錐體

typedef struct FRUSTUM_TYP {

PLANE3D planes[6];//6個平面

}FRUSTUM, *FRUSTUM_PTR;

 

1.平面方程式定義:

Ax + By + Cz + D = 0

A,B,C:平面的法線向量

D:從平面到原點的距離

x,y,z:平面上的任意點

結果為零:該點落在平面上

結果為正:該點為于平面的前方

結果為負:該點為於平面的後方

 

2.確定視錐體尺寸,獲取投影矩陣和模型視口矩陣

glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection);

glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview);

 

3.投影矩陣乘以模型視口矩陣

glPushMatrix();

glLoadMatrixf((GLfloat*)&projection);

glMultMatrixf((GLfloat*)&modelview);

glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview);

glPopMatrix();

 

4.提取視錐體的六個平面

int scale = (row < 0) ? -1 : 1;

row = abs(row) – 1;

plane->A = mat->M[0][3] + scale * mat->M[0][row];

plane->B = mat->M[1][3] + scale * mat->M[1][row];

plane->C = mat->M[2][3] + scale * mat->M[2][row];

plane->D = mat->M[3][3] + scale * mat->M[3][row];

平面(plane) 行(row)
左則 1
右則 -1
底部 2
頂部 -2
近端 3
遠端 -3

 

5.平面歸一化

float length = sqrtf(plane->A * plane->A + plane->B * plane->B + plane->C * plane->C);

plane->A /= length;

plane->B /= length;

plane->C /= length;

plane->D /= length;

 

6.檢查3D球體是否為於視錐體內

frustum:視錐體

sphere:球體

返回值:若在視錐體內返回TRUE,否則反回FALSE

bool Compute_Sphere_In_Frustum(FRUSTUM_PTR frustum, SPHERE3D_PTR sphere)

{

float dist;

for (int i=0;i<6;++i)

{// 判斷球體是否為與六個平面之前

PLANE3D_PTR plane = &frustum->planes[i];

// 計算點與平面的距離,若為正在視錐體內,若為負在視錐體外

dist = plane->A * sphere->x + plane->B * sphere->y + plane->C * sphere->z + plane->D;

if (dist <= -sphere->radius)

return false;// 在視錐體外

}

return true; // 在視錐體內

}

 

評論