4

我正在尝试使用Java 和 OpenGL ( LWJGL )中的剪辑空间方法来实现截锥体剔除。对于投影矩阵,我使用以下方法:

private Matrix4f createPerspectiveProjection(final float fov, final float aspect, final float zNear, final float zFar) {
    Matrix4f mat = new Matrix4f();

    float yScale = 1f / (float) Math.tan(Math.toRadians(fov / 2f));
    float xScale = yScale / aspect;
    float frustumLength = zFar - zNear;

    mat.m00 = xScale;
    mat.m11 = yScale;
    mat.m22 = -((zFar + zNear) / frustumLength);
    mat.m23 = -1;
    mat.m32 = -((2 * zFar * zNear) / frustumLength);
    mat.m33 = 0;

    return mat;
}

截锥体的系数是这样提取的:

Vector4f[] coefficients = new Vector4f[6];
Matrix4f a = Matrix4f.mul(projection, view, null);

// left
coefficients[0] = new Vector4f(
    a.m00 + a.m03, 
    a.m10 + a.m13, 
    a.m20 + a.m23, 
    a.m30 + a.m33);
// right
coefficients[1] = new Vector4f(
    -a.m00 + a.m03, 
    -a.m10 + a.m13, 
    -a.m20 + a.m23, 
    -a.m30 + a.m33);

// bottom
coefficients[2] = new Vector4f(
    a.m01 + a.m03, 
    a.m11 + a.m13, 
    a.m21 + a.m23, 
    a.m31 + a.m33);
// top
coefficients[3] = new Vector4f(
    -a.m01 + a.m03, 
    -a.m11 + a.m13, 
    -a.m21 + a.m23, 
    -a.m31 + a.m33);

// near
coefficients[4] = new Vector4f(
    a.m02 + a.m03, 
    a.m12 + a.m13, 
    a.m22 + a.m23, 
    a.m32 + a.m33);
// far
coefficients[5] = new Vector4f(
    -a.m02 + a.m03, 
    -a.m12 + a.m13, 
    -a.m22 + a.m23, 
    -a.m32 + a.m33);

for (int i = 0; i < 6; i++) {
    coefficients[i].normalise();
}

应使用以下方法测试平截头体是否包含给定点。

public boolean contains(final Vector4f v) {
    for (int i = 0; i < 6; i++) {
        float value = Vector4f.dot(coefficients[i], v);
        if (value < 0.0f) {
            return false;
        }
    }

    return true;
} 

问题是此方法返回的结果与预期不符。所以有时它是错误的,尽管点在截锥体的中间......

我希望你能帮助我。

这是一个演示问题的小型测试程序。类 FrustumTest:

package frustum;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

public class FrustumTest {

    private final int WIDTH = 800;
    private final int HEIGHT = 600;
    private final float SPEED = 2.0f;

    private Matrix4f projection;
    private Matrix4f camera;
    private Matrix4f viewProjection;
    private Frustum frustum;

    private Vector3f[] quad;
    private Vector3f position = new Vector3f();

    public void create() {
        projection = createPerspectiveProjection(45.0f, 1.0f, 0.1f, 100.0f);
        camera = new Matrix4f();
        frustum = new Frustum(projection);

        quad = new Vector3f[4];
        quad[0] = new Vector3f(-0.5f, 0.5f, -2.0f);
        quad[1] = new Vector3f(-0.5f, -0.5f, -2.0f);
        quad[2] = new Vector3f(0.5f, -0.5f, -2.0f);
        quad[3] = new Vector3f(0.5f, 0.5f, -2.0f);
    }

    public void update(final float deltaTime) {
        // Position
        Vector3f deltaPosition = new Vector3f();
        boolean changed = false;
        if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
            deltaPosition.translate(0, 0, -SPEED * deltaTime);
            changed = true;
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
            deltaPosition.translate(0, 0, SPEED * deltaTime);
            changed = true;
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
            deltaPosition.translate(-SPEED * deltaTime, 0, 0);
            changed = true;
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
            deltaPosition.translate(SPEED * deltaTime, 0, 0);
            changed = true;
        }

        if (changed || viewProjection == null) {
            position = Vector3f.add(position, deltaPosition, null);

            camera.setIdentity();
            camera.translate(new Vector3f(position));

            Matrix4f view = Matrix4f.invert(camera, null);
            viewProjection = Matrix4f.mul(projection, view, null);

            frustum.update(view);
            System.out.println(position);
        }
    }

    public void render() {
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        GL11.glBegin(GL11.GL_QUADS);
        for (int i = 0; i < 4; i++) {
            Vector4f v = Matrix4f.transform(viewProjection, new Vector4f(quad[i].x, quad[i].y, quad[i].z, 1.0f), null);

            if (frustum.contains(v)) {
                GL11.glColor3f(0, 1.0f, 0);
            } else {
                GL11.glColor3f(1.0f, 0, 0);
            }
            GL11.glVertex3f(v.x / v.w, v.y / v.w, v.z / v.w);
        }
        GL11.glEnd();

    }

    private Matrix4f createPerspectiveProjection(final float fov, final float aspect, final float zNear, final float zFar) {
        Matrix4f mat = new Matrix4f();

        float yScale = 1f / (float) Math.tan(Math.toRadians(fov / 2f));
        float xScale = yScale / aspect;
        float frustumLength = zFar - zNear;

        mat.m00 = xScale;
        mat.m11 = yScale;
        mat.m22 = -((zFar + zNear) / frustumLength);
        mat.m23 = -1;
        mat.m32 = -((2 * zFar * zNear) / frustumLength);
        mat.m33 = 0;

        return mat;
    }

    private void run() throws LWJGLException {
        Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
        Display.create();

        GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GL11.glViewport(0, 0, WIDTH, HEIGHT);

        create();

        long lastTime = System.currentTimeMillis();
        while (!Display.isCloseRequested()) {
            long currentTime = System.currentTimeMillis();
            float deltaTime = (currentTime - lastTime) / 1000.0f;
            lastTime = currentTime;
            update(deltaTime);
            render();

            if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
                System.exit(0);
            }

            Display.update();
            Display.sync(60);
        }
        Display.destroy();
    }

    public static void main(final String[] args) {
        try {
            new FrustumTest().run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

类截锥体:

package frustum;

import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

public class Frustum {

    private final Matrix4f projection;
    private final Vector4f[] coefficients = new Vector4f[6];

    public Frustum(final Matrix4f projection) {
        this.projection = projection;
    }

    public void update(final Matrix4f view) {
        Matrix4f a = Matrix4f.mul(projection, view, null);

        // left
        coefficients[0] = new Vector4f(a.m00 + a.m03, a.m10 + a.m13, a.m20 + a.m23, a.m30 + a.m33);
        // right
        coefficients[1] = new Vector4f(-a.m00 + a.m03, -a.m10 + a.m13, -a.m20 + a.m23, -a.m30 + a.m33);

        // bottom
        coefficients[2] = new Vector4f(a.m01 + a.m03, a.m11 + a.m13, a.m21 + a.m23, a.m31 + a.m33);
        // top
        coefficients[3] = new Vector4f(-a.m01 + a.m03, -a.m11 + a.m13, -a.m21 + a.m23, -a.m31 + a.m33);

        // near
        coefficients[4] = new Vector4f(a.m02 + a.m03, a.m12 + a.m13, a.m22 + a.m23, a.m32 + a.m33);
        // far
        coefficients[5] = new Vector4f(-a.m02 + a.m03, -a.m12 + a.m13, -a.m22 + a.m23, -a.m32 + a.m33);

        for (int i = 0; i < 6; i++) {
            coefficients[i].normalise();
        }
    }

    public boolean contains(final Vector3f v) {
        return contains(new Vector4f(v.x, v.y, v.z, 1));
    }

    public boolean contains(final Vector4f v) {
        for (int i = 0; i < 6; i++) {
            float value = Vector4f.dot(coefficients[i], v);
            if (value < 0.0f) {
                return false;
            }
        }

        return true;
    }

}
4

0 回答 0