0

我对 OpenGL 和 3D 编程相当陌生,但我已经开始根据http://www.cprogramming.com/tutorial/3d/quaternions.html的教程使用四元数来实现相机旋转。这都是使用 JOGL 用 Ja​​va 编写的。

我意识到这类问题被问了很多,但我一直在寻找,找不到有效的解决方案,所以我认为这可能是我的代码的问题。

所以问题是如果我在一个或多个轴上进行两次不同的连续旋转,就会出现抖动和奇怪的旋转。沿 a 轴的第一次旋转,无论是负向还是正向,都可以正常工作。但是,如果我沿 a 轴正向旋转,然后在该轴上负向旋转,则旋转将前后抖动,就好像它在进行正向和负向旋转之间交替一样。

如果我自动旋转(例如向左旋转 500 次,然后向右旋转 500 次),那么它似乎工作正常,这让我认为这可能与按键有关。但是,如果我绕 x 轴旋转然后绕 y 轴旋转,旋转也是不正确的(因为没有更好的词)。

无论如何,我有一个渲染器类,带有以下用于绘制“场景节点”的显示循环:

private void render(GLAutoDrawable drawable) {
    GL2 gl = drawable.getGL().getGL2();
    gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);

    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glLoadIdentity();
    glu.gluPerspective(70, Constants.viewWidth / Constants.viewHeight, 0.1, 30000);
    gl.glScalef(1.0f, -1.0f, 1.0f); //flip the y axis

    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();

    camera.rotateCamera();
    glu.gluLookAt(camera.getCamX(), camera.getCamY(), camera.getCamZ(), camera.getViewX(), camera.getViewY(), camera.getViewZ(), 0, 1, 0);
    drawSceneNodes(gl);
}

private void drawSceneNodes(GL2 gl) {
    if (currentEvent != null) {
        ArrayList<SceneNode> sceneNodes = currentEvent.getSceneNodes();
        for (SceneNode sceneNode : sceneNodes) {
            sceneNode.update(gl);
        }
    }

    if (renderQueue.size() > 0) {
        currentEvent = renderQueue.remove(0);
    }
}

旋转在相机类中执行如下:

public class Camera {
    private double width;
    private double height;
    private double rotation = 0;

    private Vector3D cam = new Vector3D(0, 0, 0);
    private Vector3D view = new Vector3D(0, 0, 0);
    private Vector3D axis = new Vector3D(0, 0, 0);

    private Rotation total = new Rotation(0, 0, 0, 1, true);

    public Camera(GL2 gl, Vector3D cam, Vector3D view, int width, int height) {
        this.cam = cam;
        this.view = view;
        this.width = width;
        this.height = height;
    }

    public void rotateCamera() {
        if (rotation != 0) {
            //generate local quaternion from new axis and new rotation
            Rotation local = new Rotation(Math.cos(rotation/2), Math.sin(rotation/2 * axis.getX()), Math.sin(rotation/2 * axis.getY()), Math.sin(rotation/2 * axis.getZ()), true);

            //multiply local quaternion and total quaternion
            total = total.applyTo(local);

            //rotate the position of the camera with the new total quaternion
            cam = rotatePoint(cam);

            //set next rotation to 0
            rotation = 0;
        }
    }

    public Vector3D rotatePoint(Vector3D point) {
        //set world centre to origin, i.e. (width/2, height/2, 0) to (0, 0, 0)
        point = new Vector3D(point.getX() - width/2, point.getY() - height/2, point.getZ());

        //rotate point
        point = total.applyTo(point);

        //set point in world coordinates, i.e. (0, 0, 0) to (width/2, height/2, 0) 
        return new Vector3D(point.getX() + width/2, point.getY() + height/2, point.getZ());
    }

    public void setAxis(Vector3D axis) {
        this.axis = axis;
    }

    public void setRotation(double rotation) {
        this.rotation = rotation;
    }
}

rotateCamera 方法从新的旋转和先前的旋转生成新的永久四元数,而 rotatePoint 方法只是将一个点乘以从永久四元数生成的旋转矩阵。

旋转轴和旋转角度通过简单的按键设置如下:

    @Override
public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_W) {
        camera.setAxis(new float[] {1, 0, 0});
        camera.setRotation(0.1f);
    }
    if (e.getKeyCode() == KeyEvent.VK_A) {
        camera.setAxis(new float[] {0, 1, 0});
        camera.setRotation(0.1f);
    }
    if (e.getKeyCode() == KeyEvent.VK_S) {
        camera.setAxis(new float[] {1, 0, 0});
        camera.setRotation(-0.1f);
    }
    if (e.getKeyCode() == KeyEvent.VK_D) {
        camera.setAxis(new float[] {0, 1, 0});
        camera.setRotation(-0.1f);
    }
}

我希望我已经提供了足够的细节。任何帮助将不胜感激。

4

1 回答 1

1

关于抖动:我在您的代码中看不到任何渲染循环。render方法是怎么触发的?通过计时器还是通过事件?

围绕两个轴旋转时的旋转混乱可能与您需要旋转第二个旋转的轴以及第一个轴的总旋转这一事实有关。您不能只应用围绕全局坐标系的 X 或 Y 轴的旋转。您必须围绕相机的上轴和右轴应用旋转。

我建议您创建一个相机类来存储相机的向上、向右和视图方向矢量,并将您的旋转直接应用于这些轴。如果这是一个类似 FPS 的相机,那么您需要围绕绝对 Y 轴而不是向上矢量水平旋转相机(向左/向右看)。这也将导致相机的新右轴。然后,您围绕新的右轴垂直(向上/向下)旋转相机。但是,当相机直接向上或向下看时,您必须小心,因为在这种情况下,您不能使用视图方向和向上向量的叉积来获得正确的向量。

于 2012-04-26T08:54:51.687 回答