移动方式 & 动作 & 设计

在上一篇文章中我们介绍了如何使用四元数表示旋转,本篇将根据「WebGL Insights - 23.Designing Cameras for WebGL Applications」一文,设计一个可供 3D 引擎使用的,功能完备的 Camera。

移动方式

摄像机按照常用的移动方式可以分成两类:Exploring/tracking 和 Orbiting。 前者固定摄像机位置,常用于第一人称,第三人称视角场景,后者固定 focal point,常用与观察模型的场景。

除此之外,在 Orbiting 类型中,还可以细分出“圆柱体”类型,和通常的球形模式区别就是将 elevation 替换成了距离中心平面的距离:

另外在 RTS 游戏场景下,常用“鸟瞰”模式,摄像机平面平行于地面并保持一定距离:

本文将关注 Tracking 和 Orbiting 这两类。

摄像机动作

learnwebgl - camera_movement 中对于摄像机的动作介绍十分详细,总结下就是分别绕 u v n 轴旋转或者沿这三轴移动:

很自然的,根据移动方式的不同,同一个摄像机动作对应的实现也不同。 我们以 dolly 动作为例,同样都是一个向前向后移动摄像机位置的动作,对于 Orbiting 模式视点不变,而在 Tracking 模式下视点是需要调整的。 以下代码来自 nucleo

nucleo.Camera.prototype.dolly = function (value) {
    var Camera = nucleo.Camera;

    // 视线方向
    var n    = this._forward;
    var pos  = vec3.clone(this._position);
    var step = value * this._dollyingStep;
    pos[0] += step * n[0];
    pos[1] += step * n[1];
    pos[2] += step * n[2];

    // 设置摄像机位置
    this._setPosition(pos);
    if (this.type == Camera.TYPE.ORBITING || this.type == Camera.TYPE.EXPLORING) {
        // 视点不变,更新焦距
        this._getDistance();
    }
    else if (this.type == Camera.TYPE.TRACKING) {
        // 保持焦距,移动视点
        vec3.add(this._focalPoint, pos, this._distanceVector);
    }
    return this;
};

下面我们重点关注一下旋转动作,结合上一篇文章中介绍过的四元数就应该很容易理解了。

旋转

按照 Orbiting 和 Tracking 模式,每次旋转后调整摄像机位置和视点:

if (this.type == Camera.TYPE.ORBITING || this.type == Camera.TYPE.EXPLORING) {
    this._getPosition();
}
else if (this.type == Camera.TYPE.TRACKING) {
    this._getFocalPoint();
}

设计

Responsible Camera

Transparent Camera

Landmarks

参考资料