3D picking lwjgl



我写了一些代码来执行3D拾取,由于某种原因不能完全正确地工作!(我使用LWJGL只是为了让你知道)

代码是这样的:

if(Mouse.getEventButton() == 1) {
  if (!Mouse.getEventButtonState()) {
    Camera.get().generateViewMatrix();
    float screenSpaceX = ((Mouse.getX()/800f/2f)-1.0f)*Camera.get().getAspectRatio();
    float screenSpaceY = 1.0f-(2*((600-Mouse.getY())/600f));
    float displacementRate = (float)Math.tan(Camera.get().getFovy()/2);
    screenSpaceX *= displacementRate;
    screenSpaceY *= displacementRate;
    Vector4f cameraSpaceNear = new Vector4f((float) (screenSpaceX * Camera.get().getNear()), (float) (screenSpaceY * Camera.get().getNear()), (float) (-Camera.get().getNear()), 1);
    Vector4f cameraSpaceFar = new Vector4f((float) (screenSpaceX * Camera.get().getFar()), (float) (screenSpaceY * Camera.get().getFar()), (float) (-Camera.get().getFar()), 1);
    Matrix4f tmpView = new Matrix4f();
    Camera.get().getViewMatrix().transpose(tmpView);
    Matrix4f invertedViewMatrix = (Matrix4f)tmpView.invert();
    Vector4f worldSpaceNear = new Vector4f();
    Matrix4f.transform(invertedViewMatrix, cameraSpaceNear, worldSpaceNear);
    Vector4f worldSpaceFar = new Vector4f();
    Matrix4f.transform(invertedViewMatrix, cameraSpaceFar, worldSpaceFar);

    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();
    Ray clickRay = new Ray(rayPosition, rayDirection);
    Vector tMin = new Vector(), tMax = new Vector(), tempPoint;
    float largestEnteringValue, smallestExitingValue, temp, closestEnteringValue = Camera.get().getFar()+0.1f;
    Drawable closestDrawableHit = null;
    for(Drawable d : this.worldModel.getDrawableThings()) {
        // Calcualte AABB for each object... needs to be moved later...
        firstVertex = true;
        for(Surface surface : d.getSurfaces()) {
            for(Vertex v : surface.getVertices()) {
                worldPosition.x = (v.x+d.getPosition().x)*d.getScale().x;
                worldPosition.y = (v.y+d.getPosition().y)*d.getScale().y;
                worldPosition.z = (v.z+d.getPosition().z)*d.getScale().z;
                worldPosition = worldPosition.rotate(d.getRotation());
                if (firstVertex) {
                    maxX = worldPosition.x; maxY = worldPosition.y; maxZ = worldPosition.z;
                    minX = worldPosition.x; minY = worldPosition.y; minZ = worldPosition.z;
                    firstVertex = false;
                } else {
                    if (worldPosition.x > maxX) {
                        maxX = worldPosition.x;
                    }
                    if (worldPosition.x < minX) {
                        minX = worldPosition.x;
                    }
                    if (worldPosition.y > maxY) {
                        maxY = worldPosition.y;
                    }
                    if (worldPosition.y < minY) {
                        minY = worldPosition.y;
                    }
                    if (worldPosition.z > maxZ) {
                        maxZ = worldPosition.z;
                    }
                    if (worldPosition.z < minZ) {
                        minZ = worldPosition.z;
                    }
                }
            }
        }
        // ray/slabs intersection test...

        // clickRay.getOrigin().x + clickRay.getDirection().x * f = minX
        // clickRay.getOrigin().x - minX = -clickRay.getDirection().x * f
        // clickRay.getOrigin().x/-clickRay.getDirection().x - minX/-clickRay.getDirection().x = f
        // -clickRay.getOrigin().x/clickRay.getDirection().x + minX/clickRay.getDirection().x = f

        largestEnteringValue = -clickRay.getOrigin().x/clickRay.getDirection().x + minX/clickRay.getDirection().x;
        temp = -clickRay.getOrigin().y/clickRay.getDirection().y + minY/clickRay.getDirection().y;
        if(largestEnteringValue < temp) {
            largestEnteringValue = temp;
        }
        temp = -clickRay.getOrigin().z/clickRay.getDirection().z + minZ/clickRay.getDirection().z;
        if(largestEnteringValue < temp) {
            largestEnteringValue = temp;
        }
        smallestExitingValue = -clickRay.getOrigin().x/clickRay.getDirection().x + maxX/clickRay.getDirection().x;
        temp = -clickRay.getOrigin().y/clickRay.getDirection().y + maxY/clickRay.getDirection().y;
        if(smallestExitingValue > temp) {
            smallestExitingValue = temp;
        }
        temp = -clickRay.getOrigin().z/clickRay.getDirection().z + maxZ/clickRay.getDirection().z;
        if(smallestExitingValue < temp) {
            smallestExitingValue = temp;
        }

            if(largestEnteringValue > smallestExitingValue) {
                //System.out.println("Miss!");
            } else {
                if (largestEnteringValue < closestEnteringValue) {
                    closestEnteringValue = largestEnteringValue;
                    closestDrawableHit = d;
                }
            }
    }
    if(closestDrawableHit != null) {
        System.out.println("Hit at: (" + clickRay.setDistance(closestEnteringValue).x + ", " + clickRay.getCurrentPosition().y + ", " + clickRay.getCurrentPosition().z);
        this.worldModel.removeDrawableThing(closestDrawableHit);    
    }
  }
}

我只是不明白出了什么问题,射线正在射击,我确实击中了被删除的东西,但射线的结果非常奇怪,有时它删除了我点击的东西,有时它删除了甚至不接近我点击的东西,有时它什么也不删除。

编辑:

好的,所以我继续搜索错误,并通过调试光线(通过绘制它行进的小点),我现在可以看到我发出的光线有明显的错误…它的原点在世界中心附近,无论我把相机对准哪里,它总是拍到同一个位置。

我最初的问题是,可能有一些错误的方式我计算我的viewMatrix(因为它是不可能从glulook方法在lwjgl获得viewMatrix;我必须自己建立它,我想这就是问题所在)…

Edit2:

这是我目前如何计算的:

private double[][] viewMatrixDouble = {{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,1}};
public Vector getCameraDirectionVector() {
    Vector actualEye = this.getActualEyePosition();
    return new Vector(lookAt.x-actualEye.x, lookAt.y-actualEye.y, lookAt.z-actualEye.z);
}
public Vector getActualEyePosition() {
    return eye.rotate(this.getRotation());
}
public void generateViewMatrix() {
    Vector cameraDirectionVector = getCameraDirectionVector().normalize();
    Vector side = Vector.cross(cameraDirectionVector, this.upVector).normalize();
    Vector up = Vector.cross(side, cameraDirectionVector);
    viewMatrixDouble[0][0] = side.x;                    viewMatrixDouble[0][1] = up.x;                  viewMatrixDouble[0][2] = -cameraDirectionVector.x;                 
    viewMatrixDouble[1][0] = side.y;                    viewMatrixDouble[1][1] = up.y;                  viewMatrixDouble[1][2] = -cameraDirectionVector.y;                 
    viewMatrixDouble[2][0] = side.z;                    viewMatrixDouble[2][1] = up.z;                  viewMatrixDouble[2][2] = -cameraDirectionVector.z;                 

    /*
    Vector actualEyePosition = this.getActualEyePosition();
    Vector zaxis = new Vector(this.lookAt.x - actualEyePosition.x, this.lookAt.y - actualEyePosition.y, this.lookAt.z - actualEyePosition.z).normalize();
    Vector xaxis = Vector.cross(upVector, zaxis).normalize();
    Vector yaxis = Vector.cross(zaxis, xaxis);
    viewMatrixDouble[0][0] = xaxis.x;                   viewMatrixDouble[0][1] = yaxis.x;                   viewMatrixDouble[0][2] = zaxis.x;                  
    viewMatrixDouble[1][0] = xaxis.y;                   viewMatrixDouble[1][1] = yaxis.y;                   viewMatrixDouble[1][2] = zaxis.y;                  
    viewMatrixDouble[2][0] = xaxis.z;                   viewMatrixDouble[2][1] = yaxis.z;                   viewMatrixDouble[2][2] = zaxis.z;                  
    viewMatrixDouble[3][0] = -Vector.dot(xaxis, actualEyePosition); viewMatrixDouble[3][1] =-Vector.dot(yaxis, actualEyePosition);  viewMatrixDouble[3][2] = -Vector.dot(zaxis, actualEyePosition);
    */
    viewMatrix = new Matrix4f();
    viewMatrix.load(getViewMatrixAsFloatBuffer());
}

如果有人能验证这是错还是对,如果是错的,那就太好了;告诉我做这件事的正确方法……我已经读了很多关于这个的线程和文档,但我不能接缝来包装我的头围绕它…

我只是不明白是怎么回事,射线正在射击,我确实击中了被移除的东西,但我在屏幕上按下的地方并没有消失。

OpenGL不是一个场景图,它是一个绘图库。所以在从你的内部表现中删除一些内容后,你必须重新绘制场景。并且您的代码缺少对触发重绘的函数的调用

在gamedev和一个朋友的帮助下,我终于解决了这个问题,这里是我发布代码的答案的链接!

最新更新