圖形引擎繪製任何睇不見(視錐截體之外)的任何物體,都會浪費保貴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; // 在視錐體內
}
你必須登入才能發表留言。