I've read so many answers on here about how to do Ray Picking, that I thought I had the idea of it down. But when I try to implement it in my game, I get garbage.
I'm working with LWJGL.
Here's the code:
public static Ray getPick(int mouseX, int mouseY){
glPushMatrix();
//Setting up the Mouse Clip
Vector4f mouseClip = new Vector4f((float)mouseX * 2 / 960f - 1, 1 - (float)mouseY * 2 / 640f ,0 ,1);
//Loading Matrices
FloatBuffer modMatrix = BufferUtils.createFloatBuffer(16);
FloatBuffer projMatrix = BufferUtils.createFloatBuffer(16);
glGetFloat(GL_MODELVIEW_MATRIX, modMatrix);
glGetFloat(GL_PROJECTION_MATRIX, projMatrix);
//Assigning Matrices
Matrix4f proj = new Matrix4f();
Matrix4f model = new Matrix4f();
model.load(modMatrix);
proj.load(projMatrix);
//Multiplying the Projection Matrix by the Model View Matrix
Matrix4f tempView = new Matrix4f();
Matrix4f.mul(proj, model, tempView);
tempView.invert();
//Getting the Camera Position in World Space. The 4th Column of the Model View Matrix.
model.invert();
Point cameraPos = new Point(model.m30, model.m31, model.m32);
//Theoretically getting the vector the Picking Ray goes
Vector4f rayVector = new Vector4f();
Matrix4f.transform(tempView, mouseClip, rayVector);
rayVector.translate((float)-cameraPos.getX(),(float) -cameraPos.getY(),(float) -cameraPos.getZ(), 0f);
rayVector.normalise();
glPopMatrix();
//This Basically Spits out a value that changes as the Camera moves.
//When the Mouse moves, the values change around 0.001 points from screen edge to edge.
System.out.format("Vector: %f %f %f%n", rayVector.x, rayVector.y, rayVector.z);
//return new Ray(cameraPos, rayVector);
return null;
}
I don't really know why this isn't working. I was hoping some more experienced eyes might be able to help me out.
I can get the camera position like a champ, it's the vector the rays going in that I can't seem to get right.
Thanks.
EDIT:
knight666,
I translated your code, and here's what I came up with:
public static Ray getPick(int mouseX, int mouseY){
glPushMatrix();
//Setting up the Mouse Clip
Vector2f mouseClip = new Vector2f((mouseX / 960f) * 2f - 1f, (mouseY / 640f) * 2f - 1f);
//Loading Matrices
FloatBuffer modMatrix = BufferUtils.createFloatBuffer(16);
FloatBuffer projMatrix = BufferUtils.createFloatBuffer(16);
glGetFloat(GL_MODELVIEW_MATRIX, modMatrix);
glGetFloat(GL_PROJECTION_MATRIX, projMatrix);
//Assigning Matrices
Matrix4f proj = new Matrix4f();
Matrix4f model = new Matrix4f();
model.load(modMatrix);
proj.load(projMatrix);
//Multiplying the Projection Matrix by the Model View Matrix
Matrix4f tempView = new Matrix4f();
Matrix4f.mul(proj, model, tempView);
tempView.invert();
//Getting the Camera Position in World Space. The 4th Column of the Model View Matrix.
//model.invert();
Point cameraPos = new Point(-model.m30, -model.m31, -model.m32);
//Getting Mouse Position in 3D Space
Vector4f pOne = new Vector4f(mouseClip.x, mouseClip.y, -1f, 1f);
Vector4f pTwo = new Vector4f(mouseClip.x, mouseClip.y, 1f, 1f);
Vector4f nearPoint = new Vector4f();
Vector4f farPoint = new Vector4f();
Matrix4f.transform(tempView, pOne, nearPoint);
Matrix4f.transform(tempView, pTwo, farPoint);
Vector3f pOneNorm = new Vector3f(pOne.x / pOne.w, pOne.y / pOne.w, pOne.z / pOne.w);
Vector3f pTwoNorm = new Vector3f(pTwo.x / pTwo.w, pTwo.y / pTwo.w, pTwo.z / pTwo.w);
Vector3f rayVector = new Vector3f();
Vector3f.sub(pTwoNorm, pOneNorm, rayVector);
rayVector.normalise();
glPopMatrix();
return new Ray(cameraPos, rayVector);
//return null;
}
Unfortunately, I'm getting a ray with the vector of (0,0,-2) that doesn't change when I move the mouse.
As I understood it, Matrix4f.transform(matrix, vector, newVector) is the same as newVector = matrix * vector. Unfortunately in LWJGL, I can't use the former. I'm not sure if that's the case of what's going wrong or not.
I appreciate the help!
Edit #2
Here's the current method, with Byte56's updates:
public static Ray getPick(int mouseX, int mouseY){
float windowWidth = 960f;
float windowHeight = 640f;
float aspectRatio = windowWidth / windowHeight;
//get the mouse position in screenSpace coords
double screenSpaceX = ((float) mouseX / (windowWidth / 2) - 1.0f) * aspectRatio;
double screenSpaceY = (1.0f - (float) mouseY / (windowHeight / 2));
double viewRatio = Math.tan((float) Math.PI / 4.0f / 2.00f);
screenSpaceX = screenSpaceX * viewRatio;
screenSpaceY = screenSpaceY * viewRatio;
float NearPlane = 1f;
float FarPlane = 100f;
//Find the far and near camera spaces
Vector4f cameraSpaceNear = new Vector4f((float) (screenSpaceX * NearPlane), (float) (screenSpaceY * NearPlane), (float) (-NearPlane), 1);
Vector4f cameraSpaceFar = new Vector4f((float) (screenSpaceX * FarPlane), (float) (screenSpaceY * FarPlane), (float) (-FarPlane), 1);
//Unproject the 2D window into 3D to see where in 3D we're actually clicking
//Loading Matrices
FloatBuffer modMatrix = BufferUtils.createFloatBuffer(16);
FloatBuffer projMatrix = BufferUtils.createFloatBuffer(16);
glGetFloat(GL_MODELVIEW_MATRIX, modMatrix);
glGetFloat(GL_PROJECTION_MATRIX, projMatrix);
// Assigning Matrices
Matrix4f proj = new Matrix4f();
Matrix4f model = new Matrix4f();
model.load(modMatrix);
proj.load(projMatrix);
// Multiplying the Projection Matrix by the Model View Matrix
Matrix4f tempView = new Matrix4f();
Matrix4f.mul(proj, model, tempView);
tempView.invert();
Vector4f worldSpaceNear = new Vector4f();
Matrix4f.transform(tempView, cameraSpaceNear, worldSpaceNear);
Vector4f worldSpaceFar = new Vector4f();
Matrix4f.transform(tempView, cameraSpaceFar, worldSpaceFar);
//calculate the ray position and direction
Vector3f rayPosition = new Vector3f(worldSpaceNear.x, worldSpaceNear.y, worldSpaceNear.z);
Vector3f rayDirection = new Vector3f(worldSpaceFar.x - worldSpaceNear.x, worldSpaceFar.y - worldSpaceNear.y, worldSpaceFar.z - worldSpaceNear.z);
rayDirection.normalise();
Point p = new Point(rayPosition.x, rayPosition.y, rayPosition.z);
return new Ray(p, rayDirection);
}
Here is my gluPerspective method call:
FIELD_OF_VIEW = 30;
gluPerspective(FIELD_OF_VIEW, 960f / 640f, 1f, 100f);
And my glLookAt method call. The method references a custom Point Object that moves with the player. The matrix is Pushed, LookedAt, and Popped every Render. The values u, v, i, & j are determined and changed to be able to rotate the "camera" around the point. The gluLookAt Call always comes before the attempt to make a ray.:
gluLookAt(
//Camera Set Above the Reference Point
(float) (lookAtPoint.getX() + u), (float)(lookAtPoint.getY() + v), (float) lookAtPoint.getZ() + zZoom,
//The Reference Point
(float) lookAtPoint.getX(), (float) lookAtPoint.getY(), (float) lookAtPoint.getZ(),
//Setting the Up Vector
i, j, 0f);
If I negate the vector, it's still off. The X axis is counting what should be a movement of 1 as a movement of approx 0.03.