OpenGL中的3D相机旋转:如何防止相机抖动?

时间:2012-04-26 06:26:44

标签: opengl camera rotation jogl quaternions

我是OpenGL和3D编程的新手,但我已经开始使用基于http://www.cprogramming.com/tutorial/3d/quaternions.html教程的四元数来实现相机旋转。这都是使用JOGL用Java编写的。

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

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

如果我自动旋转,(例如向左旋转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);
    }
}

我希望我提供了足够的细节。非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

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

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

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