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

原文: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();
    }
}

原文地址:https://www.cnblogs.com/lonelyxmas/p/9833824.html

时间: 2024-10-09 07:14:55

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

[JSP]JSP中include指令和include动作的区别

include指令是编译阶段的指令,即include所包含的文件的内容是编译的时候插入到JSP文件中,JSP引擎在判断JSP页面未被修改,否则视为已被修改.由于被包含的文件是在编译时才插入的,因此如果只修改了include文件内容,而没有对JSP修改,得到的结构将不会改变,所以直接执行已经存在的字节码文件,而没有重新编译.因此对不经常变化的内容,用include指令是合适的,如果需要的内容是经常变化的,则需要动作元素<jsp:include>.下面将详细区分他们之间的不同 1.include指

二叉树进阶之寻找一棵二叉树中的最大二叉搜索子树

转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6618915.html  (规律:在二叉树中寻找某性质的,都应该以递归思维:从根结点开始递归左右,一直到底,由底向上返回的信息来判断当前结点.求当前结点.即:二叉树的题目,从下往上想,递归的返回过程就是从下往上由叶到根建立二叉树的过程,在此过程中对每一步的"根"结点作性质判断,返回到根时即是整棵树的性质判断了) 从一棵树中寻找结点数最多的二叉搜索子树,并返回这棵子树的头结点. 从题目我们知道以下要求

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

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

Jquery:Jquery中的事件&lt;二&gt;

这几天快忙死了,办了离职还得办入职,完全打乱了我的计划,但是能有一个理想的工作,还是很开心的,以后加把劲,争取把计划再赶上来!不说了,学习!!! 五.事件对象的属性 1.event.type:获取事件的类型,其中event是事件的对象. 2.event.preventDefaule(),在上一个学习笔记中已经有介绍了,该方法是阻止默认的事件事件行为.event.stopPropagation(),该方法的作业是阻止事件的冒泡. 3.event.target,它的作用是获取到触发事件的元素.通过返

JSP中include指令和include动作区别

首先 <%@ include file=" "%>:为指令元素 <jsp:include page=" " flush="true"/>:为 动作元素 先说指令元素: include指令元素读入指定页面的内容.并把这些内容和原来的页面融合到一起. 然后经过两个阶段: 1.将jsp翻译成 servlet  2.servlet 翻译成 .class文件 这样的话,在被引入文件中请不要加入 contentype 的属性 ,因为j

掌握JS中的“this” (二)

在上一篇文章 掌握JS中的"this" (一) 里面, 我们学会了如何正确使用JavaScript中的 this 关键字及其基本原理.我们也知道决定 this 指向哪个对象的关键因素, 是找出当前的执行上下文(execution context).但如果执行上下文不按正常的方式进行设置,问题可能就会变得很棘手.在本文中,我会着重提示在哪些地方会发生这种情况, 以及用什么方式可以弥补. 解决常见问题 在本节中,我们将探讨一些使用 this 关键字时最常见的问题, 并了解如何处理这种情况.

19.把1~100存到二维数组a[10][10]中,并按二维矩阵形式输出

#include<iostream>using namespace std; int main(){    int a[10][10];    for(int i=0;i<10;i++)    {        for(int j=0;j<10;j++)        {            a[i][j]=i*10+j+1;//二维数组逻辑上还是一维数组的存储方式        }    }    for(int j=0;j<10;j++)    {        for

JSP中的编译指令和动作指令的差别

JSP中的编译指令和动作指令的差别 1.编译指令是通知Servlet引擎的处理消息.而动作指令仅仅是执行时的脚本动作 2.编译指令是在将JSP编译成Servlet时起作用,而动作指令可替换成JSP脚本,是JSP脚本标准化写法

Golang中使用log(二):Golang 标准库log的实现

前一篇文章我们看到了Golang标准库中log模块的使用,那么它是如何实现的呢?下面我从log.Logger开始逐步分析其实现. 其源码可以参考官方地址 1.Logger结构 首先来看下类型Logger的定义: type Logger struct { mu sync.Mutex // ensures atomic writes; protects the following fields prefix string // prefix to write at beginning of each