前文介绍了Viewport3D中的两种摄像机:OrthographicCamera和PerspectiveCamera。在3D场景里漫游,最主要的工作就是针对用户输入(例如鼠标左右移动、键盘按下A、W、S、D等键)来改变摄像机的位置、方向。本文接下来介绍如何通过改变PerspectiveCamera的属性,来达到场景的漫游效果。
摄像机动作
我摄像机的动作可以分成三类、移动、旋转、拉升镜头。用一个枚举来描述这些动作:
public enum SceneCameraAction { MoveForward, //向前移动 MoveBack, //向后移动 MoveLeft, //向左移动 MoveRight, //向后移动 MoveUp, //向上移动 MoveDown, //向下移动 TurnLeft, //左转 TurnRight, //右转 TurnUp, //向上看 TurnDown, //向下看 ZoomIn, //拉近镜头 ZoomOut, //拉远镜头 }
移动摄像机
在WPF3D里,可以通过改变计算机的Position属性,来移动PerspectiveCamera,假设摄像机的移动速度为Speed,有以下移动公式:
新坐标=原坐标+速度×移动方向
下图为摄像机向前、向左、向上移动的方向,为了方便计算,移动方向都为单位向量。
向前、向后移动
向前移动的移动方向为LookDirection,向后为-1*LookDirection
向前移动:
Camera.Position += (Speed * Camera.LookDirection);
向后移动
Camera.Position -= (Speed * Camera.LookDirection);
向左、向右移动
向左、向右移动,相当于在XZ平面上,沿着摄像机的LookDirection投影垂直的直线方向移动。
向左移动:
Camera.Position += Speed * (Camera.LookDirection.Rotate(0, Math.PI / 2, 0).GetUnit());
向右移动:
Camera.Position += Speed * (Camera.LookDirection.Rotate(0, -1 * Math.PI / 2, 0).GetUnit());
上面的变换,我用了两个扩展函数:
把向量旋转拆分成分别绕x轴、y轴、z轴旋转:
用以下函数计算一个向量分别绕x、y、z轴旋转后得到的新向量:
/// <summary> /// 向量旋转 /// </summary> /// <param name="x">绕x轴旋转值</param> /// <param name="y">绕y轴旋转值</param> /// <param name="z">绕z轴旋转值</param> /// <returns>旋转结果</returns> public static Vector3D Rotate(this Vector3D vector3D,double x, double y, double z) { Matrix3D rotateX = new Matrix3D( 1, 0, 0, 0, 0, Math.Cos(x), Math.Sin(x), 0, 0, -Math.Sin(x), Math.Cos(x), 0, 0, 0, 0, 1); Matrix3D rotateY = new Matrix3D( Math.Cos(y), 0, -Math.Sin(y), 0, 0, 1, 0, 0, Math.Sin(y), 0, Math.Cos(y), 0, 0, 0, 0, 1); Matrix3D rotateZ = new Matrix3D( Math.Cos(z), Math.Sin(z), 0, 0, -Math.Sin(z), Math.Cos(z), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return vector3D * rotateX * rotateY * rotateZ; }
GetUnit函数是计算一个响亮的单位向量:
public static Vector3D GetUnit(this Vector3D vector3D) { double length = 1.0d; return new Vector3D(vector3D.X * length / vector3D.Length, vector3D.Y * length / vector3D.Length, vector3D.Z * length / vector3D.Length); }
向上、向下移动
向上、向下移动相当于延y轴改变Camera的Position属性:
向上移动:
Camera.Position += Speed * new Vector3D(0, 1, 0);
向下移动:
Camera.Position += Speed * new Vector3D(0, -1, 0);
旋转摄像机
和移动摄像机不同,旋转摄像机时,保持摄像机的Position属性不变,根据旋转值修改摄像机的LookDirection属性。这里仅仅假设绕Y轴旋转:
/// <summary> /// 旋转摄像头 /// </summary> /// <param name="ModelCameraAction">旋转角度</param> public void Turn(SceneCameraAction ModelCameraAction) { double speed = Math.PI / 60; if (ModelCameraAction == SceneCameraAction.TurnLeft) { Camera.LookDirection = Camera.LookDirection.Rotate(0, speed, 0).GetUnit(); } if (ModelCameraAction == SceneCameraAction.TurnRight) { Camera.LookDirection = Camera.LookDirection.Rotate(0, -1 * speed, 0).GetUnit(); } }