我在 JOGL 中编写了一个简单的拾取演示。有 27 个立方体,放置在魔方中。但问题是采摘很少选择好的立方体。那么对于这种情况,光线投射是最好的方法吗?否则,如果立方体有不同的旋转(例如,并不总是蓝色面对我们),我应该使用哪种方法?
TheGLEventListener.java(我删除了一些实现的方法,因为它们没有被使用)
package com.gmail.bernabe.laurent.java_opengl.picking_test.views;
public class TheGLEventListener implements GLEventListener,MouseListener,MouseMotionListener {
@Override
public void init(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glEnable(GL2.GL_DEPTH_TEST);
gl.glClearColor(0.67f, 0.16f, 0.51f, 0.0f);
cubesLocations = new ArrayList<>();
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
for (int k = 0; k < 3; k++){
cubesLocations.add(new Vector3f((float) (-3.0 + 3.0 * i), (float) (-3.0 + 3.0 * j), (float) (-3.0 + 3.0 * k)));
}
}
}
}
@Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
glu.gluLookAt(
0.0f, 0.0f, 20.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f
);
if (pendingEvent != null){
// Mouse pressed event
if (pendingEvent.getID() == MouseEvent.MOUSE_PRESSED) {
Ray rayFromMousePointer = Ray.fromMouseCoords(pendingEvent.getX(), pendingEvent.getY(),
gl);
Vector3f cubeCenter = null;
mouseIntersection = null;
// for each cube, we look for intersection
for (int cubeIndex = 0; cubeIndex < cubesLocations.size(); cubeIndex++) {
cubeCenter = cubesLocations.get(cubeIndex);
// ray cast intersection computed here
mouseIntersection = rayFromMousePointer.intersectionWithSphere(cubeCenter, 1.0f * 1.414f);
// if there is an intersection
if (mouseIntersection != null){
selectedCubeIndex = cubeIndex;
mouseOffsetInCube = mouseIntersection.clone().sub(cubeCenter);
break;
}
}
pendingEvent = null;
}
else if (pendingEvent.getID() == MouseEvent.MOUSE_DRAGGED){
if (selectedCubeIndex >= 0) {
Ray rayFromMousePointer = Ray.fromMouseCoords(
pendingEvent.getX(), pendingEvent.getY(), gl);
Vector3f newLocation = rayFromMousePointer
.getPointWhoseZ_Is(mouseIntersection.z);
mouseIntersection = newLocation;
cubesLocations.set(selectedCubeIndex, mouseIntersection.clone().sub(mouseOffsetInCube));
}
}
else if (pendingEvent.getID() == MouseEvent.MOUSE_RELEASED){
selectedCubeIndex = -1;
}
}
drawScene(gl);
}
private void drawScene(GL2 gl) {
for (Vector3f place : cubesLocations) {
gl.glPushMatrix();
gl.glTranslatef(place.x, place.y, place.z);
GLLittleCube.draw(gl);
gl.glPopMatrix();
}
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width,
int height) {
GL2 gl = drawable.getGL().getGL2();
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(60.0, width * 1.0f / height, 0.1, 40);
gl.glMatrixMode(GL2.GL_MODELVIEW);
}
private GLU glu = new GLUgl2();
@Override
public void mousePressed(MouseEvent e) {
pendingEvent = e;
}
@Override
public void mouseReleased(MouseEvent e) {
pendingEvent = e;
}
@Override
public void mouseDragged(MouseEvent e) {
pendingEvent = e;
}
private MouseEvent pendingEvent;
private ArrayList<Vector3f> cubesLocations;
private int selectedCubeIndex = -1;
/**
* Distance from the center of the selected cube, to its
* clicked point.
*/
private Vector3f mouseOffsetInCube;
/**
* The original clicked point, in the selected cube : it is this point which is
* moved when dragging mouse (and not the cube center) !!! Otherwise, I get a strange effect
* in the beginning.
*/
private Vector3f mouseIntersection;
}
GLLittleCube.java(我只是放了一张脸的代码)
package com.gmail.bernabe.laurent.java_opengl.picking_test.logic;
import javax.media.opengl.GL2;
public class GLLittleCube {
/**
* Draws a little cube bounds within [-1.0 and 1.0] for the three directions x,y and z.
* @param gl - GL2
*/
public static void draw(GL2 gl){
gl.glBegin(GL2.GL_QUADS);
//FRONT face (BLUE)
gl.glColor3f(0.0f, 0.13f, 0.66f);
gl.glVertex3f(-1.0f, -1.0f, +1.0f);
gl.glVertex3f(+1.0f, -1.0f, +1.0f);
gl.glVertex3f(+1.0f, +1.0f, +1.0f);
gl.glVertex3f(-1.0f, +1.0f, +1.0f);
// similar code for other faces
gl.glEnd();
}
}
雷.java
package com.gmail.bernabe.laurent.java_opengl.picking_test.logic;
import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.gl2.GLUgl2;
public class Ray {
private Vector3f origin, direction;
public Ray(Vector3f origin, Vector3f direction) {
this.origin = origin;
this.direction = direction;
}
public Vector3f getOrigin() {
return origin;
}
public Vector3f getDirection() {
return direction;
}
/**
* Returns the point, belonging to this ray, whose x coordinate is given.
* @param soughtX - float
* @return - Vector3f
*/
public Vector3f getPointWhoseX_Is(float soughtX){
float period = (float) (soughtX - origin.x) / direction.x;
return origin.clone().add(direction.clone().scale(period));
}
/**
* Returns the point, belonging to this ray, whose y coordinate is given.
* @param soughtY - float
* @return - Vector3f
*/
public Vector3f getPointWhoseY_Is(float soughtY){
float period = (float) (soughtY - origin.y) / direction.y;
return origin.clone().add(direction.clone().scale(period));
}
/**
* Returns the point, belonging to this ray, whose z coordinate is given.
* @param soughtZ - float
* @return - Vector3f
*/
public Vector3f getPointWhoseZ_Is(float soughtX){
float period = (float) (soughtX - origin.z) / direction.z;
return origin.clone().add(direction.clone().scale(period));
}
/**
* Returns the intersection point : the closest one from the ray origin.
* @param sphereCenter - Vector3f
* @param sphereRadius - float
* @return Vector3f
*/
public Vector3f intersectionWithSphere(Vector3f sphereCenter, float sphereRadius){
// sphere center values
double scx, scy, scz;
// sphere radius
double sr;
//ray direction values
double rdx, rdy, rdz;
//ray origin values
double rox, roy, roz;
//quadratic equation values
double b,c, delta, t0, t1;
sr = sphereRadius;
scx = sphereCenter.x;
scy = sphereCenter.y;
scz = sphereCenter.z;
rdx = direction.x;
rdy = direction.y;
rdz = direction.z;
rox = origin.x;
roy = origin.y;
roz = origin.z;
b = 2 * (rdx * (rox - scx) + rdy * (roy - scy) + rdz * (roz - scz));
c = (rox - scx)*(rox - scx) + (roy - scy)*(roy - scy) + (roz - scz)*(roz - scz) - sr*sr;
delta = b*b - 4*c;
if (delta < 0){
return null;
}
else {
t0 = (-b-Math.sqrt(delta))/2;
t1 = (+b-Math.sqrt(delta))/2;
// Must be the smallest POSITIVE root between t0 and t1
float t;
if (t0 < 0 && t1 < 0)
return null;
else if (t0 > 0 && t1 > 0){
t = t0 > t1 ? (float) t0 : (float) t1;
}
else if (t0 > 0)
t = (float) t0;
else
t = (float) t1;
return origin.clone().add(direction.clone().scale(t));
}
}
public static Ray fromMouseCoords(int mouseX, int mouseY, GL2 gl){
int viewport [] = new int[4];
float modelViewMatrix [] = new float[16];
float projectionMatrix [] = new float[16];
float objectPos[] = new float[3];
gl.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0);
gl.glGetFloatv(GL2.GL_MODELVIEW_MATRIX, modelViewMatrix, 0);
gl.glGetFloatv(GL2.GL_PROJECTION_MATRIX, projectionMatrix, 0);
float winY = viewport[3] * 1.0f - mouseY;
float winX = mouseX * 1.0f;
glu.gluUnProject(winX, winY, 0.0f, modelViewMatrix, 0, projectionMatrix, 0, viewport, 0, objectPos, 0);
Vector3f tempOrigin = new Vector3f(objectPos[0], objectPos[1], objectPos[2]);
glu.gluUnProject(winX, winY, 1.0f, modelViewMatrix, 0, projectionMatrix, 0, viewport, 0, objectPos, 0);
Vector3f tempDest = new Vector3f(objectPos[0], objectPos[1], objectPos[2]);
Vector3f tempDirection = tempDest.clone().sub(tempOrigin).normalize();
return new Ray(tempOrigin, tempDirection);
}
public String toString(){
return String.format("Ray{\norigin = %s,\ndirection = %s\n}", origin, direction);
}
private static GLU glu = new GLUgl2();
}