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;
}
}