Viewport3D中的摄像机(二、摄像机动作)
前文介绍了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();
}
}
Viewport3D中的摄像机(二、摄像机动作)的更多相关文章
- C#中的线程二(Cotrol.BeginInvoke和Control.Invoke)
C#中的线程二(Cotrol.BeginInvoke和Control.Invoke) 原文地址:http://www.cnblogs.com/whssunboy/archive/2007/06/07/ ...
- C语言中如何将二维数组作为函数的参数传递
今天写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单里,但是最后我也解决了遇到的问题,所以这篇文章主要介绍如何处理二维数组当作参数传递的情况,希望大家不 ...
- IT公司100题-35- 求一个矩阵中最大的二维矩阵(元素和最大)
问题描述: 求一个矩阵中最大的二维矩阵(元素和最大).如: 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 中最大的是: 4 5 9 10 分析: 2*2子数组的最大和.遍历求和,时 ...
- C++中的异常处理(二)
C++中的异常处理(二) 标签: c++C++异常处理 2012-11-24 20:56 1713人阅读 评论(2) 收藏 举报 分类: C++编程语言(24) 版权声明:本文为博主原创文章,未经 ...
- c#中的linq二
c#中的linq二 using System; using System.Collections.Generic; using System.Linq; using System.Text; us ...
- [转]Visual Studio 2008中如何比较二个数据库的架构【Schema】和数据【Data】并同步
使用场景: 在团队开发中,每一个人都有可能随时更新数据库,这时候数据库中数据和架构等信息都会发生变化.如果更新不及时,就会发生数据错误或数据丢失的风险,影响团队的开发效率和 项目进度,这时候我们该怎么 ...
- php中向前台js中传送一个二维数组
在php中向前台js中传送一个二维数组,并在前台js接收获取其中值的全过程方法: (1),方法说明:现在后台将数组发送到前台 echo json_encode($result); 然后再在js页面中的 ...
- Golang中的坑二
Golang中的坑二 for ...range 最近两周用Golang做项目,编写web服务,两周时间写了大概五千行代码(业务代码加单元测试用例代码).用Go的感觉很爽,编码效率高,运行效率也不错,用 ...
- JSP中的编译指令和动作指令的区别
JSP中的编译指令和动作指令的区别 1.编译指令是通知Servlet引擎的处理消息,而动作指令只是运行时的脚本动作 2.编译指令是在将JSP编译成Servlet时起作用,而动作指令可替换成JSP脚本, ...
随机推荐
- PatentTips - Modified buddy system memory allocation
BACKGROUND Memory allocation systems assign blocks of memory on request. A memory allocation system ...
- layer iframe 之间传值和关闭iframe弹窗
1.访问父页面元素值 var parentId=parent.$("#id").val();//访问父页面元素值 2.访问父页面方法 var parentMethodValue=p ...
- 【Lucene4.8教程之二】索引 2014-06-16 11:30 3845人阅读 评论(0) 收藏
一.基础内容 0.官方文档说明 (1)org.apache.lucene.index provides two primary classes: IndexWriter, which creates ...
- js如何使用正则表达式验证电话号码(可选区号)和邮箱?(分步骤)
js如何使用正则表达式验证电话号码(可选区号)和邮箱?(分步骤) 一.总结 js进阶正则表达式16电话号码和邮箱正则(分类解决邮箱验证)(分组加?解决电话号码区号选择问题)([\w\.-]中括号解决邮 ...
- 从 BM 到 RBM
1. 拓扑结构上 如下图示,在拓扑结构上,RBM(受限的玻尔兹曼机)与 BM(玻尔兹曼机)的最大区别在于: RBM 取消了可见层的层内连接以及隐含层的层内连接,主要在于 BM 的层内连接使得其学习过程 ...
- 【t047】网络
Time Limit: 1 second Memory Limit: 128 MB [问题描述] Bessie受雇来到John的农场帮他们建立internet网络.农场有 N (2<= N &l ...
- [Javascript] Write a function pipeline
const _pipe = (f, g) => (...args) => g(f(...args)) export const pipe = (...fns) => fns.redu ...
- 简洁常用权限系统的设计与实现(一):构造权限菜单树的N(N>=4)种方法
权限系统,Web开发常见标准子系统之一.结合自己的一些思考和实践,从本篇开始权限系统的设计与实现之路. 最近,重构了项目的权限菜单构造过程,向前端返回json格式的权限树. 这一篇,只是大致介绍下这个 ...
- js进阶 9-16 如何实现多选框全选和取消
js进阶 9-16 如何实现多选框全选和取消 一.总结 一句话总结:选择取到每一个checkbox的值,然后赋值为true或者false就好. 1.如何实现多选框全选和取消? 选择取到每一个check ...
- Opencv Sift和Surf特征实现图像无缝拼接生成全景图像
Sift和Surf算法实现两幅图像拼接的过程是一样的,主要分为4大部分: 1. 特征点提取和描述 2. 特征点配对,找到两幅图像中匹配点的位置 3. 通过配对点,生成变换矩阵,并对图像1应用变换矩阵生 ...