问题描述
我正在尝试对我的体素引擎实施平截头剔除,基本上是在渲染块,我想剔除相机平截头锥体之外的每个块。我尝试了很多在Web上找到的不同方法和代码,但仍然无法正常工作。该算法分为两个部分:
•首先,我要从projectionview矩阵中提取视锥平面。
•然后,我要检查每个块是否在内部或与视锥相撞。
行为通常是相同的:当从原点向正方向看时,它似乎起作用,但是当向负方向看时,它没有作用;当我离开原点时,它开始断裂并开始工作废话。另外,在上下选择时,也很奇怪。
这是我的视锥平面提取:
public static Plane[] frustumPlanes(Matrix4f mat,boolean normalize)
{
Plane[] p = new Plane[6];
p[0] = normalizePlane(mat.m30 + mat.m00,mat.m31 + mat.m01,mat.m32 + mat.m02,mat.m33 + mat.m03); // left
p[1] = normalizePlane(mat.m30 - mat.m00,mat.m31 - mat.m01,mat.m32 - mat.m02,mat.m33 - mat.m03); // right
p[2] = normalizePlane(mat.m30 - mat.m10,mat.m31 - mat.m11,mat.m32 - mat.m12,mat.m33 - mat.m13); // top
p[3] = normalizePlane(mat.m30 + mat.m10,mat.m31 + mat.m11,mat.m32 + mat.m12,mat.m33 + mat.m13); // bottom
p[4] = normalizePlane(mat.m30 + mat.m20,mat.m31 + mat.m21,mat.m32 + mat.m22,mat.m33 + mat.m23); // near
p[5] = normalizePlane(mat.m30 - mat.m20,mat.m31 - mat.m21,mat.m32 - mat.m22,mat.m33 - mat.m23); // far
return p;
}
public static Plane normalizePlane(float A,float B,float C,float D) {
float nf = 1.0f / (float)Math.sqrt(A * A + B * B + C * C);
return new Plane(new Vector3f(nf * A,nf * B,nf * C),nf * D);
}
mat是投影视图矩阵,这是投影矩阵:
private void createProjectionMatrix() {
float aspectRatio = (float) displayManager.WIDTH / (float) displayManager.HEIGHT;
float y_scale = (float) ((1f / Math.tan(Math.toradians(FOV / 2f))));
float x_scale = y_scale / aspectRatio;
float frustum_length = FAR_PLANE - NEAR_PLANE;
projectionMatrix = new Matrix4f();
projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * NEAR_PLANE * FAR_PLANE) / frustum_length);
projectionMatrix.m33 = 0;
}
以下是视图矩阵:
public static Matrix4f createViewMatrix(Camera camera) {
Matrix4f viewMatrix = new Matrix4f();
viewMatrix.setIdentity();
Matrix4f.rotate((float) Math.toradians(camera.getRotation().x),new Vector3f(1,0),viewMatrix,viewMatrix);
Matrix4f.rotate((float) Math.toradians(camera.getRotation().y),new Vector3f(0,1,viewMatrix);
Matrix4f.rotate((float) Math.toradians(camera.getRotation().z),1),viewMatrix);
Vector3f cameraPos = camera.getPosition();
Vector3f negativeCameraPos = new Vector3f(-cameraPos.x,-cameraPos.y,-cameraPos.z);
Matrix4f.translate(negativeCameraPos,viewMatrix);
return viewMatrix;
}
以下是碰撞检测代码aabb vs plane:
public static int BoxToPlaneCollision(Plane plane,Vector3f[] minMax)
{
int result = 2; //Inside
// planes have unit-length normal,offset = -dot(normal,point on plane)
int nx = plane.normal.x > 0?1:0;
int ny = plane.normal.y > 0?1:0;
int nz = plane.normal.z > 0?1:0;
// getMinMax(): 0 = return min coordinate. 1 = return max.
float dot = (plane.normal.x*minMax[nx].x) + (plane.normal.y*minMax[nx].y) + (plane.normal.z*minMax[nx].z);
if ( dot < -plane.offset )
return 0; //Outside
float dot2 = (plane.normal.x*minMax[1-nx].x) + (plane.normal.y*minMax[1-nx].y) + (plane.normal.z*minMax[1-nx].z);
if ( dot2 <= -plane.offset )
result = 1; //Intersect
return result;
}
最后,这就是所有的内容:
public boolean chunkInsideFrustum(Vector3f chunkPos) {
Vector3f chunkPosMax = new Vector3f(chunkPos.x + Terrain.CHUNK_SIZE,Terrain.CHUNK_HEIGHT,chunkPos.z + Terrain.CHUNK_SIZE);
for (int i = 0; i < 6; i++) {
if(Collider.BoxToPlaneCollision(frustumPlanes[i],new Vector3f[] {chunkPos,chunkPosMax}) == 0)
return false;
}
return true;
}
我正在将openGL与LWJGL 2(Java)一起使用。
我的问题是: 问题出在哪里?在截锥体平面中提取代码?在碰撞检测中?
和
我看到人们用投影和modelview矩阵计算平截头体,这种技术呢?更好吗?
非常感谢您的帮助!
编辑:
对于第二个问题,我在这里Extracting View Frustum Planes (Gribb & Hartmann method)看到有人发帖:
缺少的部分:
comboMatrix = projection_matrix * Matrix4_Transpose(modelview_matrix)
然后他执行了与提取平面完全相同的算法,但是modelview_matrix是什么?我应该使用哪种型号?
解决方法
在构建链接的pdf的opengl部分中看到的平截头平面时,我发现pdf表示要使用projection*view
[注意顺序非常重要]矩阵的列,但对我有用而是改用行(请注意,是只是我还是编辑器部分出现错误)。在LWJGL中,这是Matrix4f的顺序
m00 m10 m20 m30
m01 m11 m21 m31
m02 m12 m22 m32
m03 m13 m23 m33
假设这是您的AABB课程
public final class AABB
{
final Vector3f
lower=new Vector3f(),upper=new Vector3f();
public AABB()
{
lower.set(-0.5f,-0.5f,-0.5f);
upper.set(0.5f,0.5f,0.5f);
}
//Note the methods getHalfDimensions,getCenter and getAxises can be exactly implemented by an OOBB as well to check against plane collision
public float[] getHalfDimensions(float[] dims)
{
if(dims==null){dims=new float[]{0,0};}
dims[0]=(upper.x-lower.x)/2.0f;
dims[1]=(upper.y-lower.y)/2.0f;
dims[2]=(upper.z-lower.z)/2.0f;
return dims;
}
public Vector3f getCenter(Vector3f result)
{
if(result==null){result=new Vector3f();}
result.set(
(lower.x+upper.x)/2.0f,(lower.y+upper.y)/2.0f,(lower.z+upper.z)/2.0f
);
return result;
}
public Vector3f[] getAxises(Vector3f axises[])
{
if(axises==null){axises=new Vector3f[3];}
axises[0]=new Vector3f(1,0);
axises[1]=new Vector3f(0,1,0);
axises[2]=new Vector3f(0,1);
return axises;
}
}
假设这是您的飞机课
public final class Plane
{
//I prefer to have all the values of an plane but you can decide what works for you
public final Vector3f origin=new Vector3f();
public final Vector3f normal=new Vector3f();
public float distance;
private Plane(Point3f point,Vector3f normal,float distance)
{
this.origin.set(point);
VecMathUtils.normalize(normal,this.normal);
this.distance=distance;
}
public static Plane of(Vector4f plane){return new Plane(new Point3f(),new Vector3f(plane.x,plane.y,plane.z),plane.w);}}
//-distance or +distance can depend see the frustum code for comments
public float distance(Vector3f point){return Vector3f.dot(normal,Vector3f.sub(point,origin,new Vector3f()))-distance;}
public boolean intersect(AABB box)
{
Point3f center=box.getCenter(null);
float[] halfDims=box.getHalfDimensions(null);
Vector3f axises[]=box.getAxises(null,false);
float separation=distance(center)-
(
halfDims[0]*Math.abs(Vector3f.dot(normal,axises[0]))+
halfDims[1]*Math.abs(Vector3f.dot(normal,axises[1]))+
halfDims[2]*Math.abs(Vector3f.dot(normal,axises[2]))
);
return separation>0 //or greater than some epslon
}
}
最后是您的(或至少是我的可行版本)Frustum
public final class Frustum
{
private final ArrayList<Plane> fPlanes=new ArrayList();
public Frustum(Matrix4f viewProj){set(viewProj);}
public Frustum set(Matrix4f viewProj)
{
Vector4f planes[]=//Initialize the 6 planes
//Left
planes[0].x=viewProj.m03+viewProj.m00;
planes[0].y=viewProj.m13+viewProj.m10;
planes[0].z=viewProj.m23+viewProj.m20;
planes[0].w=viewProj.m33+viewProj.m30;
//Right
planes[1].x=viewProj.m03-viewProj.m00;
planes[1].y=viewProj.m13-viewProj.m10;
planes[1].z=viewProj.m23-viewProj.m20;
planes[1].w=viewProj.m33-viewProj.m30;
//Top
planes[2].x=viewProj.m03-viewProj.m01;
planes[2].y=viewProj.m13-viewProj.m11;
planes[2].z=viewProj.m23-viewProj.m21;
planes[2].w=viewProj.m33-viewProj.m31;
//Bottom
planes[3].x=viewProj.m03+viewProj.m01;
planes[3].y=viewProj.m13+viewProj.m11;
planes[3].z=viewProj.m23+viewProj.m21;
planes[3].w=viewProj.m33+viewProj.m31;
//Near
planes[4].x=viewProj.m03+viewProj.m02;
planes[4].y=viewProj.m13+viewProj.m12;
planes[4].z=viewProj.m23+viewProj.m22;
planes[4].w=viewProj.m33+viewProj.m32;
//Far
planes[5].x=viewProj.m03-viewProj.m02;
planes[5].y=viewProj.m13-viewProj.m12;
planes[5].z=viewProj.m23-viewProj.m22;
planes[5].w=viewProj.m33-viewProj.m32;
for(Vector4f plane:planes)
{
float length=1.0f/new Vector3f(plane.x,plane.z).length(); //normalization of the plane can produce varying results. I found that for AABB or OOBB it can be very sensitive[i.e when the edges of the box are touching either sides of the screen it returns false and the box dissapears even though you can still see some parts of it and it appears the box dissapears whereas for sphere it works smoothly]
plane.x*=length;
plane.y*=length;
plane.z*=length;
plane.w*=-length; //either negate this w component or in the plane point distance computation change -distance to +distance,either works
fPlanes.add(Plane.of(plane));
}
return this;
}
public boolean contained(AABB box)
{
for(Plane plane:fPlanes)
{
if(!plane.intersect(box))
{
return false;
}
}
return true;
}
}
这也是我计算矩阵的方式
public class MatrixMath4f
{
public static Matrix4f perspective(float fov,float znear,float zfar,float aspectRatio,Matrix4f dst)
{
if(dst==null){dst=Matrix4f.setZero(new Matrix4f());}
float cot=(float)(1.0f/Math.tan(Math.toRadians(fov/2)));
float zp=zfar+znear;
float zm=zfar-znear;
dst.m00=cot/aspectRatio;
dst.m01=0;
dst.m02=0;
dst.m03=0;
dst.m10=0;
dst.m11=cot;
dst.m12=0;
dst.m13=0;
dst.m20=0;
dst.m21=0;
dst.m22=-zp/zm;
dst.m23=-1f;
dst.m30=0;
dst.m31=0;
dst.m32=-(2*zfar*znear)/zm;
dst.m33=0;
return dst;
}
private static Vector3f normalize(Vector3f src,Vector3f dst)
{
if(dst==null){dst=new Vector3f();}
dst.set(src);
float length=dst.length();
if(length>0){dst.scale(1.0f/length);}
return dst;
}
//for look at matrix
public static Matrix4f lookAt(Point3f eyepos,Point3f center,Vector3f upVector,Matrix4f dst)
{
dst=Matrix4f.setZero(dst==null?new Matrix4f():dst);
Vector3f
lookAt=new Vector3f(),right=new Vector3f(1,0),up=new Vector3f(0,0);
normalize(Point3f.sub(center,eyepos,lookAt),lookAt);
normalize(upVector,up);
normalize(Vector3f.cross(lookAt,up,right),right);
normalize(Vector3f.cross(right,lookAt,up),up);
dst.m00=right.x;
dst.m01=up.x;
dst.m02=-lookAt.x;
dst.m03=0f;
dst.m10=right.y;
dst.m11=up.y;
dst.m12=-lookAt.y;
dst.m13=0f;
dst.m20=right.z;
dst.m21=up.z;
dst.m22=-lookAt.z;
dst.m23=0f;
dst.translate((Vector3f)eyepos.toVector(null).scale(-1));
dst.m33=1;
return dst;
}
//For Euler angles and position
public static Matrix4f viewTransform(Point3f location,Vector3f eulers,Matrix4f dst)
{
dst=Matrix4f.setIdentity(dst==null?new Matrix4f():dst);
dst.rotate((float)Math.toRadians(eulers.x),xaxis);
dst.rotate((float)Math.toRadians(eulers.y),yaxis);
dst.rotate((float)Math.toRadians(eulers.z),zaxis);
dst.translate(location.negate(new Vector3f()));
return dst;
}
}