原文:Viewport3D中的摄像机(二、摄像机动作)

前文介绍了Viewport3D中的两种摄像机:OrthographicCamera和PerspectiveCamera。在3D场景里漫游,最主要的工作就是针对用户输入(例如鼠标左右移动、键盘按下A、W、S、D等键)来改变摄像机的位置、方向。本文接下来介绍如何通过改变PerspectiveCamera的属性,来达到场景的漫游效果。

摄像机动作

我摄像机的动作可以分成三类、移动、旋转、拉升镜头。用一个枚举来描述这些动作:

  1. public enum SceneCameraAction
  2. {
  3. MoveForward, //向前移动
  4. MoveBack, //向后移动
  5. MoveLeft, //向左移动
  6. MoveRight, //向后移动
  7. MoveUp, //向上移动
  8. MoveDown, //向下移动
  9. TurnLeft, //左转
  10. TurnRight, //右转
  11. TurnUp, //向上看
  12. TurnDown, //向下看
  13. ZoomIn, //拉近镜头
  14. ZoomOut, //拉远镜头
  15. }

移动摄像机

在WPF3D里,可以通过改变计算机的Position属性,来移动PerspectiveCamera,假设摄像机的移动速度为Speed,有以下移动公式:

  1. 新坐标=原坐标+速度×移动方向

下图为摄像机向前、向左、向上移动的方向,为了方便计算,移动方向都为单位向量。

向前、向后移动

向前移动的移动方向为LookDirection,向后为-1*LookDirection

向前移动:

  1. Camera.Position += (Speed * Camera.LookDirection);

向后移动

  1. Camera.Position -= (Speed * Camera.LookDirection);

向左、向右移动

向左、向右移动,相当于在XZ平面上,沿着摄像机的LookDirection投影垂直的直线方向移动。

向左移动:

  1. Camera.Position += Speed * (Camera.LookDirection.Rotate(0, Math.PI / 2, 0).GetUnit());

向右移动:

  1. Camera.Position += Speed * (Camera.LookDirection.Rotate(0, -1 * Math.PI / 2, 0).GetUnit());

上面的变换,我用了两个扩展函数:

把向量旋转拆分成分别绕x轴、y轴、z轴旋转:

用以下函数计算一个向量分别绕x、y、z轴旋转后得到的新向量:

  1. /// <summary>
  2. /// 向量旋转
  3. /// </summary>
  4. /// <param name="x">绕x轴旋转值</param>
  5. /// <param name="y">绕y轴旋转值</param>
  6. /// <param name="z">绕z轴旋转值</param>
  7. /// <returns>旋转结果</returns>
  8. public static Vector3D Rotate(this Vector3D vector3D,double x, double y, double z)
  9. {
  10. Matrix3D rotateX = new Matrix3D(
  11. 1, 0, 0, 0,
  12. 0, Math.Cos(x), Math.Sin(x), 0,
  13. 0, -Math.Sin(x), Math.Cos(x), 0,
  14. 0, 0, 0, 1);
  15.  
  16. Matrix3D rotateY = new Matrix3D(
  17. Math.Cos(y), 0, -Math.Sin(y), 0,
  18. 0, 1, 0, 0,
  19. Math.Sin(y), 0, Math.Cos(y), 0,
  20. 0, 0, 0, 1);
  21.  
  22. Matrix3D rotateZ = new Matrix3D(
  23. Math.Cos(z), Math.Sin(z), 0, 0,
  24. -Math.Sin(z), Math.Cos(z), 0, 0,
  25. 0, 0, 1, 0,
  26. 0, 0, 0, 1);
  27.  
  28. return vector3D * rotateX * rotateY * rotateZ;
  29.  
  30. }

GetUnit函数是计算一个响亮的单位向量:

  1. public static Vector3D GetUnit(this Vector3D vector3D)
  2. {
  3. double length = 1.0d;
  4. return new Vector3D(vector3D.X * length / vector3D.Length,
  5. vector3D.Y * length / vector3D.Length,
  6. vector3D.Z * length / vector3D.Length);
  7. }

向上、向下移动

向上、向下移动相当于延y轴改变Camera的Position属性:

向上移动:

  1. Camera.Position += Speed * new Vector3D(0, 1, 0);

向下移动:

  1. Camera.Position += Speed * new Vector3D(0, -1, 0);

旋转摄像机

和移动摄像机不同,旋转摄像机时,保持摄像机的Position属性不变,根据旋转值修改摄像机的LookDirection属性。这里仅仅假设绕Y轴旋转:

  1. /// <summary>
  2. /// 旋转摄像头
  3. /// </summary>
  4. /// <param name="ModelCameraAction">旋转角度</param>
  5. public void Turn(SceneCameraAction ModelCameraAction)
  6. {
  7. double speed = Math.PI / 60;
  8.  
  9. if (ModelCameraAction == SceneCameraAction.TurnLeft)
  10. {
  11. Camera.LookDirection = Camera.LookDirection.Rotate(0, speed, 0).GetUnit();
  12. }
  13.  
  14. if (ModelCameraAction == SceneCameraAction.TurnRight)
  15. {
  16. Camera.LookDirection = Camera.LookDirection.Rotate(0, -1 * speed, 0).GetUnit();
  17. }
  18. }

Viewport3D中的摄像机(二、摄像机动作)的更多相关文章

  1. C#中的线程二(Cotrol.BeginInvoke和Control.Invoke)

    C#中的线程二(Cotrol.BeginInvoke和Control.Invoke) 原文地址:http://www.cnblogs.com/whssunboy/archive/2007/06/07/ ...

  2. C语言中如何将二维数组作为函数的参数传递

    今天写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单里,但是最后我也解决了遇到的问题,所以这篇文章主要介绍如何处理二维数组当作参数传递的情况,希望大家不 ...

  3. IT公司100题-35- 求一个矩阵中最大的二维矩阵(元素和最大)

    问题描述: 求一个矩阵中最大的二维矩阵(元素和最大).如: 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 中最大的是: 4 5 9 10   分析: 2*2子数组的最大和.遍历求和,时 ...

  4. C++中的异常处理(二)

    C++中的异常处理(二) 标签: c++C++异常处理 2012-11-24 20:56 1713人阅读 评论(2) 收藏 举报  分类: C++编程语言(24)  版权声明:本文为博主原创文章,未经 ...

  5. c#中的linq二

    c#中的linq二   using System; using System.Collections.Generic; using System.Linq; using System.Text; us ...

  6. [转]Visual Studio 2008中如何比较二个数据库的架构【Schema】和数据【Data】并同步

    使用场景: 在团队开发中,每一个人都有可能随时更新数据库,这时候数据库中数据和架构等信息都会发生变化.如果更新不及时,就会发生数据错误或数据丢失的风险,影响团队的开发效率和 项目进度,这时候我们该怎么 ...

  7. php中向前台js中传送一个二维数组

    在php中向前台js中传送一个二维数组,并在前台js接收获取其中值的全过程方法: (1),方法说明:现在后台将数组发送到前台 echo json_encode($result); 然后再在js页面中的 ...

  8. Golang中的坑二

    Golang中的坑二 for ...range 最近两周用Golang做项目,编写web服务,两周时间写了大概五千行代码(业务代码加单元测试用例代码).用Go的感觉很爽,编码效率高,运行效率也不错,用 ...

  9. JSP中的编译指令和动作指令的区别

    JSP中的编译指令和动作指令的区别 1.编译指令是通知Servlet引擎的处理消息,而动作指令只是运行时的脚本动作 2.编译指令是在将JSP编译成Servlet时起作用,而动作指令可替换成JSP脚本, ...

随机推荐

  1. OAM配置代理手冊

     创建webgate与ohs共享实例,copy文件到ohs实例文件夹. 1)进入webgage部署工具文件夹       Cd  /%webgate_home%/webgate/ohs/tools ...

  2. HDU 1248 寒冰王座 完全背包

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1248 中文题,大意就不说了. 第一道完全背包题,跟着背包九讲做的. 和0-1背包的区别在于所不同的是每种 ...

  3. [Angular2 Form] Create custom form component using Control Value Accessor

    //switch-control component import { Component } from '@angular/core'; import { ControlValueAccessor, ...

  4. JDK8 JVM性能优化-1

    原文地址:https://blog.csdn.net/xingkongtianma01/article/details/80689928 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的 ...

  5. 【u117】队列安排

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 一个学校里老师要将班上N个同学排成一列,同学被编号为1-N,他采取如下的方法: 1. 先将1号同学安排 ...

  6. 计算git树上随意两点的近期切割点。

    1.git是一种分布式代码管理工具,git通过树的形式记录文件的更改历史,比方: base'<--base<--A<--A' ^ | --- B<--B' 小米project师 ...

  7. android,安卓get请求的提交以及我遇到的异常

    首先说明 我是安卓4.0以上的版本,这个时候直接用网上的代码会报错的,先赋上网上的普遍代码 String uri = "http://url"; HttpGet httpGet = ...

  8. [转载]Ocelot简易教程(三)之主要特性及路由详解

    上篇<Ocelot简易教程(二)之快速开始2>教大家如何快速跑起来一个ocelot实例项目,也只是简单的对Ocelot进行了配置,这篇文章会给大家详细的介绍一下Ocelot的配置信息.希望 ...

  9. Java 常用工具类---- 各种字符集编码判断与转换

    import java.io.UnsupportedEncodingException; /** * 判断字符编码 * * @author guyinyihun */ public class Cha ...

  10. [Grid Layout] Use the repeat function to efficiently write grid-template values

    We can use the repeat() function if we have repeating specifications for columns and rows. With the  ...