Frustum Culling

Started by apamment, July 11, 2013, 08:43:18 AM

Previous topic - Next topic

apamment

Hi,

I'm trying to implement frustum culling explained here: http://www.lighthouse3d.com/tutorials/view-frustum-culling/ in my minecraft-like experiment.

The problem is it doesn't seem to be allowing any chunks partially in the frustum to show up. I have attached a screenshot.

I'm calling it like this to set up the frustum object:

frustum.SetCamera(world.getCamera().getPosition(), world.getCamera().getUpVector(), world.getCamera().getDirection());
frustum.SetFrustum(world.getCamera().getYFOV(),  800 / 600, 1, 1500);


then as I add chunks to the scene i test there visibility. I've been at this a couple of days and don't understand what I'm doing wrong. Please if anyone can help it would be much appreciated.

Thanks,



import com.threed.jpct.*;

public class Frustum {
private SimpleVector cameraPos;
private SimpleVector cameraUp;
private SimpleVector cameraDir;

private float frustumAngle;
private float frustumRatio;
private float frustumNearD;
private float frustumFarD;

final float ANG2RAD = 3.14159265358979323846f/180.0f;

private Plane planes[] = new Plane[6];

    public void SetCamera(SimpleVector pos, SimpleVector up, SimpleVector dir) {
cameraPos = pos; //.calcSub(dir);
//cameraPos.normalize();

cameraDir = dir;//up.calcCross(cameraPos);
//cameraDir.normalize();

cameraUp = up; //cameraPos.calcCross(cameraDir);

//cameraRight = up.calcCross(dir);
//cameraRight.normalize();
}

public void SetFrustum(float angle, float ratio, float nearD, float farD) {
frustumAngle = angle;
frustumRatio = ratio;
frustumNearD = nearD;
frustumFarD = farD;

calculateFrustum();

}


private void calculateFrustum() {
float tang = (float)Math.tan(ANG2RAD * frustumAngle * 0.5) ;
float nh = frustumNearD * tang;
float nw = nh * frustumRatio;
float fh = frustumFarD  * tang;
float fw = fh * frustumRatio;


SimpleVector nearCenter = new SimpleVector();

nearCenter = cameraPos.calcAdd(new SimpleVector(cameraDir.x * frustumNearD, cameraDir.y * frustumNearD, cameraDir.z * frustumNearD));

// nearCenter.x = cameraPos.x + cameraDir.x * frustumNearD;// Z.x * frustumNearD;
// nearCenter.y = cameraPos.y + cameraDir.y * frustumNearD;//- Z.y * frustumNearD;
// nearCenter.z = cameraPos.z + cameraDir.z * frustumNearD; //Z.z * frustumNearD;

SimpleVector farCenter = new SimpleVector();

farCenter = cameraPos.calcAdd(new SimpleVector(cameraDir.x * frustumFarD, cameraDir.y * frustumFarD, cameraDir.z * frustumFarD));

// farCenter.x = cameraPos.x + cameraDir.x * frustumFarD; //- Z.x * frustumFarD;
// farCenter.y = cameraPos.y + cameraDir.y * frustumFarD;//- Z.y * frustumFarD;
// farCenter.z = cameraPos.z + cameraDir.z * frustumFarD;// Z.z * frustumFarD;

SimpleVector yNH, xNW, yFH, xFW;
yNH = new SimpleVector();
xNW = new SimpleVector();
yFH = new SimpleVector();
xFW = new SimpleVector();

yNH.x = cameraUp.x * nh;
yNH.y = cameraUp.y * nh;
yNH.z = cameraUp.z * nh;

yFH.x = cameraUp.x * fh;
yFH.y = cameraUp.y * fh;
yFH.z = cameraUp.z * fh;

xNW.x = cameraDir.x * nw;
xNW.y = cameraDir.y * nw;
xNW.z = cameraDir.z * nw;

xFW.x = cameraDir.x * fw;
xFW.y = cameraDir.y * fw;
xFW.z = cameraDir.z * fw;


SimpleVector ntl = nearCenter.calcAdd(yNH).calcSub(xNW);
SimpleVector ntr = nearCenter.calcAdd(yNH).calcAdd(xNW);
SimpleVector nbl = nearCenter.calcSub(yNH).calcSub(xNW);
SimpleVector nbr = nearCenter.calcSub(yNH).calcAdd(xNW);

SimpleVector ftl = farCenter.calcAdd(yFH).calcSub(xFW);
SimpleVector ftr = farCenter.calcAdd(yFH).calcAdd(xFW);
SimpleVector fbl = farCenter.calcSub(yFH).calcSub(xFW);
SimpleVector fbr = farCenter.calcSub(yFH).calcAdd(xFW);

planes[0] = new Plane(ntr,ntl,ftl);
planes[1] = new Plane(nbl,nbr,fbr);
planes[2] = new Plane(ntl,nbl,fbl);
planes[3] = new Plane(nbr,ntr,fbr);
planes[4] = new Plane(ntl,ntr,nbr);
planes[5] = new Plane(ftr,ftl,fbl);
/*
planes[0] = new Plane();
planes[1] = new Plane();
planes[2] = new Plane();
planes[3] = new Plane();
planes[4] = new Plane();
planes[5] = new Plane();


SimpleVector aux,normal, yNH, xNW;
yNH = new SimpleVector();
xNW = new SimpleVector();

yNH.x = cameraUp.x * nh;
yNH.y = cameraUp.y * nh;
yNH.z = cameraUp.z * nh;

xNW.x = cameraDir.x * nw;
xNW.y = cameraDir.y * nw;
xNW.z = cameraDir.z * nw;

aux = nearCenter.calcAdd(yNH).calcSub(cameraPos);
aux.normalize();
normal = cameraDir.calcCross(aux);
planes[0].setTo(nearCenter.calcAdd(yNH), normal);

aux = nearCenter.calcSub(yNH).calcSub(cameraPos);
aux.normalize();
normal = aux.calcCross(cameraDir);
planes[1].setTo(nearCenter.calcSub(yNH), normal);

aux = nearCenter.calcSub(xNW).calcSub(cameraPos);
aux.normalize();
normal = cameraUp.calcCross(aux);
planes[2].setTo(nearCenter.calcSub(xNW), normal);

aux = nearCenter.calcAdd(xNW).calcSub(cameraPos);
aux.normalize();
normal = aux.calcCross(cameraUp);
planes[3].setTo(nearCenter.calcAdd(xNW), normal);

planes[4].setTo(nearCenter, cameraDir);
planes[5].setTo(farCenter, new SimpleVector(-cameraDir.x, -cameraDir.y, -cameraDir.z));
*/
}

public boolean chunkInFrustum(Chunk c) {
boolean result = true;
int out, in;

float chunk_x = c.GetX() * Chunk.CHUNK_SIZE * Chunk.BLOCK_SIZE;
float chunk_y = c.GetY() * Chunk.CHUNK_SIZE * Chunk.BLOCK_SIZE;
float chunk_z = c.GetZ() * Chunk.CHUNK_SIZE * Chunk.BLOCK_SIZE;

float boxWidth = Chunk.CHUNK_SIZE * Chunk.BLOCK_SIZE;

for (int i=0;i<6;i++) {
in = 0;
out = 0;

if (planes[i].distanceTo(new SimpleVector(chunk_x, chunk_y, chunk_z)) < 0) {
out++;
} else {
in++;
}

if (planes[i].distanceTo(new SimpleVector(chunk_x + boxWidth, chunk_y, chunk_z)) < 0) {
out++;
} else {
in++;
}

if (planes[i].distanceTo(new SimpleVector(chunk_x, chunk_y, chunk_z + boxWidth)) < 0) {
out++;
} else {
in++;
}
if (planes[i].distanceTo(new SimpleVector(chunk_x, chunk_y + boxWidth, chunk_z)) < 0) {
out++;
} else {
in++;
}
if (planes[i].distanceTo(new SimpleVector(chunk_x + boxWidth, chunk_y + boxWidth, chunk_z)) < 0) {
out++;
} else {
in++;
}
if (planes[i].distanceTo(new SimpleVector(chunk_x, chunk_y + boxWidth, chunk_z + boxWidth)) < 0) {
out++;
} else {
in++;
}
if (planes[i].distanceTo(new SimpleVector(chunk_x + boxWidth, chunk_y + boxWidth, chunk_z + boxWidth)) < 0) {
out++;
} else {
in++;
}

if (planes[i].distanceTo(new SimpleVector(chunk_x + boxWidth, chunk_y, chunk_z + boxWidth)) < 0) {
out++;
} else {
in++;
}
if (in == 0) {
System.out.println("All out... " + i);
return false;
} else if (out != 0) {
System.out.println("Some out... " + i);
result = true;
}
}


return result;
}
}


[attachment deleted by admin]

EgonOlsen

What exactly are these chunk_?-values? The coordinates of the right upper part of a chunk? If so, in which space?

apamment

It's supposed to be the top right corner of the chunk in world space. c.getX, c.getY and c.getZ return the position of a chunk like: 0,0,0 is a chunk in the middle of the world  and 1,0,0 is a chunk next to it.


EgonOlsen

So it's the top LEFT corner, actually? If it's in world space...have you taken into account that the view frustum rotates in world space? I.e. if you turn 180 degrees around Y, your x+boxWidth should actually be x-boxWidth!?

apamment

You're right sorry, it is the top left corner.

I'm not understanding why the position of the chunks would change if the frustum rotates? The idea of the chunk_x + blockwidth is to get the vertices of the four corners of an imaginary bounding box of the chunk mesh and then test if the vertices are all on the wrong side of each plane. If all the vertices are on the wrong side of the same plane it's out of view and doesn't need to be rendered.

The behaviour I'm experiencing is the chunks appear when rotating the camera only in the middle of the view. The ones that are rendered are all where they should be.

Thanks for your help.

apamment

#5
I've got it working, thanks :)