视截体是计算机图形学的关键内容之一,涉及裁剪、投影等内容,视截体分为长方体和平头椎体两种,前者关乎平行投影,后者关乎透视投影。但不论是哪一种,视截体都有六个面,在上节内容中,我介绍了视截体6个面求解的数学推导,这一节实现视截体的实际编程。
视截体类应该至少包含计算6个面的函数,判断一个正方体,球体,点是否在视截体内部的函数,平面法线标准化的函数,以及存储视截体各个面的系数的二维矩阵。
下面是视截体类的简单定义:
/*****************************************************************************************
* Copyright Reserved By QingeSoftware
* Author : QinGe
* Filename : Frustum.h 1.0
* Date: 2008-01-10
****************************************************************************************/
#pragma once
enum FrustumSide
{
RIGHT = 0, //Right side
LEFT = 1, //left side
BOTTOM= 2, //top side
TOP = 3, // back side
BACK = 4, //back side
FRONT = 5 // front side
};
enum PlaneNormal
{
A = 0, // x value of normal ;
B = 1, // y value of normal;
C = 2, // z value of normal
D = 3 // distance of point to plane.
} ;
class CFrustum
{
public:
CFrustum(void);
virtual ~CFrustum(void);
public:
void CalculateFrustum(); //计算视截体的6个面。
BOOL CubeInFrustum(float x, float y, float z, float size); //判断一个正方体是否在视截体内
void NormalizePlane(float Frustum[6][4],int side); //把法线标准化
private:
float m_Frustum[6][4]; //视截体6个面的方程
};
视截体类的实现文件:
/*****************************************************************************************
* Copyright Reserved By QingeSoftware
* Author : QinGe
* Filename : Frustum.cpp 1.0
* Date: 2008-01-10
****************************************************************************************/
#include "StdAfx.h"
#include "Frustum.h"
#include
CFrustum::CFrustum(void)
{
}
CFrustum::~CFrustum(void)
{
}
void CFrustum::NormalizePlane(float Frustum[6][4],int side)
{
//获取法线的模
float manatude =sqrtf(Frustum[side][0]*Frustum[side][0]+
Frustum[side][1]*Frustum[side][1]+
Frustum[side][2]*Frustum[side][2]);
//单位化
Frustum[side][0]/=manatude;
Frustum[side][1]/=manatude;
Frustum[side][2]/=manatude;
Frustum[side][3]/=manatude;
}
//判断正方体是否在视截体内
BOOL CFrustum::CubeInFrustum(float x, float y, float z, float size)
{
//循环判断正方体的八个顶点是否全部在视截体一个面的后部
//如果是,那么正方体肯定在视截体外部,返回FALSE;循环遍历六个面,如果找不到这样的面
//则说明正方体至少有一个点在视截体内部。返回TRUE。
for(int i = 0; i < 6; i++ )
{
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0)
continue;
// If we get here, it isn't in the frustum
return false;
}
return true;
}
void CFrustum::CalculateFrustum()
{
/* 这部分是我自己写的,但是运行效率较低,请高手指教!
float ModelView[4][4];
float Projective[4][4];
float Clip[4][4];
glGetFloatv(GL_MODELVIEW_MATRIX,*(ModelView+0));
glGetFloatv(GL_PROJECTION_MATRIX,*(Projective+0));
for(int i=0; i<4; i++) //M=模型矩阵A*投影矩阵B。
{
for(int j=0;j<4; j++)
{
Clip[i][j]=0;
for( int k=0; k<4; k++)
{
Clip[i][j] += ModelView[i][k]*Projective[k][j];
}
}
}
//数学推导出来的东西,没什么好注释的。想说明白的话,注释肯定得比代码还多
//建议看看3D数学。
m_Frustum[RIGHT][0] = Clip[0][3] - Clip[0][0];
m_Frustum[RIGHT][1] = Clip[1][3] - Clip[1][0];
m_Frustum[RIGHT][2] = Clip[2][3] - Clip[2][0];
m_Frustum[RIGHT][3] = Clip[3][3] - Clip[3][0];
NormalPlane(m_Frustum,RIGHT);
m_Frustum[LEFT][0] = Clip[0][3] + Clip[0][0];
m_Frustum[LEFT][1] = Clip[1][3] + Clip[1][0];
m_Frustum[LEFT][2] = Clip[2][3] + Clip[2][0];
m_Frustum[LEFT][3] = Clip[3][3] + Clip[3][0];
NormalPlane(m_Frustum,LEFT);
m_Frustum[BOTTOM][0] = Clip[0][3] + Clip[0][1];
m_Frustum[BOTTOM][1] = Clip[1][3] + Clip[1][1];
m_Frustum[BOTTOM][2] = Clip[2][3] + Clip[2][1];
m_Frustum[BOTTOM][3] = Clip[3][3] + Clip[3][1];
NormalPlane(m_Frustum,BOTTOM);
m_Frustum[TOP][0] = Clip[0][3] - Clip[0][1];
m_Frustum[TOP][1] = Clip[1][3] - Clip[1][1];
m_Frustum[TOP][2] = Clip[2][3] - Clip[2][1];
m_Frustum[TOP][3] = Clip[3][3] - Clip[3][1];
NormalPlane(m_Frustum,TOP);
m_Frustum[BACK][0] = Clip[0][3] - Clip[0][2];
m_Frustum[BACK][1] = Clip[1][3] - Clip[1][2];
m_Frustum[BACK][2] = Clip[2][3] - Clip[2][2];
m_Frustum[BACK][3] = Clip[3][3] - Clip[3][2];
NormalPlane(m_Frustum,BACK);
m_Frustum[FRONT][0] = Clip[0][3] + Clip[0][2];
m_Frustum[FRONT][1] = Clip[1][3] + Clip[1][2];
m_Frustum[FRONT][2] = Clip[2][3] + Clip[2][2];
m_Frustum[FRONT][3] = Clip[3][3] + Clip[3][2];
NormalPlane(m_Frustum,FRONT);
*/ 自己写的部分到此结束,
//由于自己写的部分运行效率较低,故下面采用书上的代码,效率明显提高!
float proj[16]; // This will hold our projection matrix
float modl[16]; // This will hold our modelview matrix
float clip[16]; // This will hold the clipping planes
//获得投影矩阵,模型变换矩阵
glGetFloatv( GL_PROJECTION_MATRIX, proj );
glGetFloatv( GL_MODELVIEW_MATRIX, modl );
//利用前面两个矩阵得到世界变幻矩阵,利用该矩阵得到视截体6个面的系数
//视截体平面的法向量都是指向视截体内部的
clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12];
clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13];
clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14];
clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15];
clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12];
clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13];
clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14];
clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15];
clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12];
clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13];
clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14];
clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15];
clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12];
clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13];
clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14];
clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15];
// Now we actually want to get the sides of the frustum. To do this we take
// the clipping planes we received above and extract the sides from them.
// This will extract the RIGHT side of the frustum
m_Frustum[RIGHT][A] = clip[ 3] - clip[ 0];
m_Frustum[RIGHT][B] = clip[ 7] - clip[ 4];
m_Frustum[RIGHT][C] = clip[11] - clip[ 8];
m_Frustum[RIGHT][D] = clip[15] - clip[12];
// Now that we have a normal (A,B,C) and a distance (D) to the plane,
// we want to normalize that normal and distance.
// Normalize the RIGHT side
NormalizePlane(m_Frustum, RIGHT);
// This will extract the LEFT side of the frustum
m_Frustum[LEFT][A] = clip[ 3] + clip[ 0];
m_Frustum[LEFT][B] = clip[ 7] + clip[ 4];
m_Frustum[LEFT][C] = clip[11] + clip[ 8];
m_Frustum[LEFT][D] = clip[15] + clip[12];
// Normalize the LEFT side
NormalizePlane(m_Frustum, LEFT);
// This will extract the BOTTOM side of the frustum
m_Frustum[BOTTOM][A] = clip[ 3] + clip[ 1];
m_Frustum[BOTTOM][B] = clip[ 7] + clip[ 5];
m_Frustum[BOTTOM][C] = clip[11] + clip[ 9];
m_Frustum[BOTTOM][D] = clip[15] + clip[13];
// Normalize the BOTTOM side
NormalizePlane(m_Frustum, BOTTOM);
// This will extract the TOP side of the frustum
m_Frustum[TOP][A] = clip[ 3] - clip[ 1];
m_Frustum[TOP][B] = clip[ 7] - clip[ 5];
m_Frustum[TOP][C] = clip[11] - clip[ 9];
m_Frustum[TOP][D] = clip[15] - clip[13];
// Normalize the TOP side
NormalizePlane(m_Frustum, TOP);
// This will extract the BACK side of the frustum
m_Frustum[BACK][A] = clip[ 3] - clip[ 2];
m_Frustum[BACK][B] = clip[ 7] - clip[ 6];
m_Frustum[BACK][C] = clip[11] - clip[10];
m_Frustum[BACK][D] = clip[15] - clip[14];
// Normalize the BACK side
NormalizePlane(m_Frustum, BACK);
// This will extract the FRONT side of the frustum
m_Frustum[FRONT][A] = clip[ 3] + clip[ 2];
m_Frustum[FRONT][B] = clip[ 7] + clip[ 6];
m_Frustum[FRONT][C] = clip[11] + clip[10];
m_Frustum[FRONT][D] = clip[15] + clip[14];
// Normalize the FRONT side
NormalizePlane(m_Frustum, FRONT);
}
//可以加入判断别的形体是否在视截体内部的函数,在此仅举正方体的例子!