Help end child hunger

Geometric Approach – Implementation

 
Prev: Geometric Approach - Extracting the Planes Next: Testing Points and Spheres
 

A class to implement the frustum functionality is now presented. Note that the code itself is not optimized in any way, as the goal is only to serve as the tutorial companion. The class contains an array of six planes, plus other variables such as the 8 corners of the frustum and the camera definitions. Only the planes are needed for the culling operation, but the remaining variables are useful for debug and visualization purposes.

The class header file

class FrustumG {

private:

	enum {
		TOP = 0, BOTTOM, LEFT,
		RIGHT, NEARP, FARP
	};

public:

	static enum {OUTSIDE, INTERSECT, INSIDE};
	Plane pl[6];
	Vec3 ntl,ntr,nbl,nbr,ftl,ftr,fbl,fbr;
	float nearD, farD, ratio, angle,tang;
	float nw,nh,fw,fh;

	FrustumG::FrustumG();
	FrustumG::~FrustumG();

	void setCamInternals(float angle, float ratio, float nearD, float farD);
	void setCamDef(Vec3 &p, Vec3 &l, Vec3 &u);
	int pointInFrustum(Vec3 &p);
	int sphereInFrustum(Vec3 &p, float raio);
	int boxInFrustum(AABox &b);
};

The Methods: setCamInternals

This function takes exactly the same parameters as the function gluPerspective. Each time the perspective definitions change, for instance when a window is resized, this function should be called as well.

#define ANG2RAD 3.14159265358979323846/180.0

void FrustumG::setCamInternals(float angle, float ratio, float nearD, float farD) {

	// store the information
	this->ratio = ratio;
	this->angle = angle;
	this->nearD = nearD;
	this->farD = farD;

	// compute width and height of the near and far plane sections
	tang = (float)tan(ANG2RAD * angle * 0.5) ;
	nh = nearD * tang;
	nw = nh * ratio;
	fh = farD  * tang;
	fw = fh * ratio;
}

The function stores all the information, and computes the width and height of the rectangular sections of the near and far plane and stores them in nh (near height), nh (near width), fh (far height), and fw (far width).

The Methods: setCamDef

This function takes three vectors that contain the information for the gluLookAt function: the position of the camera, a point to where the camera is pointing and the up vector. Each time the camera position or orientation changes, this function should be called as well.

void FrustumG::setCamDef(Vec3 &p, Vec3 &l, Vec3 &u) {

	Vec3 dir,nc,fc,X,Y,Z;

	// compute the Z axis of camera
	// this axis points in the opposite direction from
	// the looking direction
	Z = p - l;
	Z.normalize();

	// X axis of camera with given "up" vector and Z axis
	X = u * Z;
	X.normalize();

	// the real "up" vector is the cross product of Z and X
	Y = Z * X;

	// compute the centers of the near and far planes
	nc = p - Z * nearD;
	fc = p - Z * farD;

	// compute the 4 corners of the frustum on the near plane
	ntl = nc + Y * nh - X * nw;
	ntr = nc + Y * nh + X * nw;
	nbl = nc - Y * nh - X * nw;
	nbr = nc - Y * nh + X * nw;

	// compute the 4 corners of the frustum on the far plane
	ftl = fc + Y * fh - X * fw;
	ftr = fc + Y * fh + X * fw;
	fbl = fc - Y * fh - X * fw;
	fbr = fc - Y * fh + X * fw;

	// compute the six planes
	// the function set3Points assumes that the points
	// are given in counter clockwise order
	pl[TOP].set3Points(ntr,ntl,ftl);
	pl[BOTTOM].set3Points(nbl,nbr,fbr);
	pl[LEFT].set3Points(ntl,nbl,fbl);
	pl[RIGHT].set3Points(nbr,ntr,fbr);
	pl[NEARP].set3Points(ntl,ntr,nbr);
	pl[FARP].set3Points(ftr,ftl,fbl);
}

The function computed the eight corners of the frustum and the six planes according to the algorithm defined in the previous section.

The following (more efficient) alternative may be used to replace the computation of the eight corners and the six planes in the function above.

	pl[NEARP].setNormalAndPoint(-Z,nc);
	pl[FARP].setNormalAndPoint(Z,fc);

	Vec3 aux,normal;

	aux = (nc + Y*nh) - p;
	aux.normalize();
	normal = aux * X;
	pl[TOP].setNormalAndPoint(normal,nc+Y*nh);

	aux = (nc - Y*nh) - p;
	aux.normalize();
	normal = X * aux;
	pl[BOTTOM].setNormalAndPoint(normal,nc-Y*nh);

	aux = (nc - X*nw) - p;
	aux.normalize();
	normal = aux * Y;
	pl[LEFT].setNormalAndPoint(normal,nc-X*nw);

	aux = (nc + X*nw) - p;
	aux.normalize();
	normal = Y * aux;
	pl[RIGHT].setNormalAndPoint(normal,nc+X*nw);

 

Prev: Geometric Approach - Extracting the Planes Next: Testing Points and Spheres
 

  6 Responses to “Geometric Approach – Implementation”

  1. ive got a question for FrustumG::setCamDef(),
    why is it that the Z axis points in the opposite direction from look direction?
    i mean is it inverted or negated?

    Thanks

  2. setCamDef code is not in paralled with your theory explanation.
    It should be something like

    X = l * u; // right vector
    X.normalize();

    nc = p + l * nearD;
    fc = p + l * farD;

    ntl = nc + u * nh/2 – X * nw/2;
    ntr = nc + u * nh/2 + X * nw/2;
    nbl = nc – u * nh/2 – X * nw/2;
    nbr = nc – u * nh/2 + X * nw/2;

    ftl = fc + u * fh/2 – X * fw/2;
    ftr = fc + u * fh/2 + X * fw/2;
    fbl = fc – u * fh/2 – X * fw/2;
    fbr = fc – u * fh/2 + X * fw/2;

    • that was assuming u to be lookAt direction

    • Hi Hakan,

      In the “theory” page, “up” is the real up vector. When using gluLookAt, the vector we call up is not necessarily the up vector, hence we have to compute it, and that’s what I do with Y.

      I’m not sure if this was what you we’re pointing out, though. Please let me know if I did not get your comment right.

      • Seems I was a bit confused when I posted that, your implementation seems correct.
        Thank you for the cool tutorial

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: