0

我已经编写了一些代码来执行 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 的方式可能存在一些错误(因为无法从 lwjgl 中的 glulookat 方法获取 viewmatrix;我必须自己构建它,我猜这就是问题所在) ...

编辑2:

这是我目前的计算方式:

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

如果有人能验证这是对还是错,以及是否错了,那就太好了;为我提供正确的方法......我已经阅读了很多关于这个的线程和文档,但我无法缝合我的头......

4

2 回答 2

0

好的,所以我最终在gamedev的人和朋友的帮助下解决了这个问题,这是我发布代码的答案的链接!

于 2012-01-06T00:32:53.570 回答
0

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

OpenGL 不是场景图,它是一个绘图库。因此,从内部表示中删除某些内容后,您必须重新绘制场景。而且您的代码缺少对触发重绘的函数的一些调用。

于 2012-01-03T17:33:14.803 回答