本节内容是在第一人称漫游上完成的,请先了解上文中第一人称漫游的实现.

这一节讲下第三人称漫游是如何实现,第三人称,简单来说,就是在你后面会跟着一台摄像机顺着你拍摄。

先看一下失败的尝试。这个方法是把人定在摄像机方向的前面,结合前面第一人称漫游的实现,如果只是前后左右移动,人和摄像机是一起的,这样是不用改动,关键是原来以摄像机为原点旋转,而这个我们要以人为原点来旋转,先来看下水平左右的的旋转实现,如下图:

根据上面关系,我们主要代码如下,大致过程如下,人左右旋转,然后得到摄像机的新位置,摄像机再调整方向,根据摄像机的方向与人的距离设定人的位置。

 type ThreeCamera() =
let camera = new Camera()
let mutable toEye = .f
member this.Eye with get() = camera.Eye
member this.Target with get() = camera.Target
member this.Location
with get() =
let mutable tv = camera.Target-camera.Eye
tv.Normalize()
camera.Eye + tv * toEye
member this.ToEye with get() = toEye and set value = toEye <- value
member this.Transelt (x,y,z) =
camera.Transelt(x,y,z)
//左右对中心转
member this.RightAndLeft x =
let origin = this.Location
let oe = camera.Eye - origin
let length = oe.Length
let oex = Vector2(oe.X,oe.Z)
let oez = Vector2(-oe.Z,oe.X)
let sinLR =length * float32 (Math.Sin(x))
let cosLR =length * float32 (Math.Cos(x))
//得到摄像机新位置
let newEye = oex * cosLR + oez * sinLR + Vector2(origin.X,origin.Z)
camera.Eye <- Vector3(newEye.X,camera.Eye.Y,newEye.Y)
//重新调整摄像机的方向
camera.XAngle <- camera.XAngle + x

左右旋转在正常的速度下,能得到正常的效果,后面我试着改变人与摄像机的长度,或者快速左右旋转,都会造成人的位置移动,想了一久都没想到是什么原因,如果有知道可能原因,麻烦告知一下.谢谢了.

此路不通后,我想了一种思路,说起来很简单,第一人称是以摄像机为球心,摄像机的方向为球面坐标系.如果以第三人称来看,人应该是球心,摄像机所在位置可以看做是球面,只需要换算一下球心与球面的算法就可以,如下所示.

     type Camera() =
let mutable origin = Vector3.Zero
let mutable length = .
let mutable yangle = .
let mutable xangle= Math.PI/.
let mutable bThree = true
do
if not bThree then xangle <- Math.PI/. else xangle <- 1.5*Math.PI
member this.IsThree
with get() = bThree
and set value =
//if value then this.Origin <- this.Target
xangle <- xangle + Math.PI
bThree <- value
member this.Eye
with get() =
let mutable eye = this.Origin
if bThree then eye <- this.Direction
eye
member this.People
with get() =
let mutable people = this.Origin
if not bThree then people <- this.Direction
people
member this.Target
with get() =
let mutable target = this.Direction
if bThree then target <- this.Origin
target
member this.Origin
with get() = origin
and set value = origin <- value
member this.Length
with get() = length
and set value =
if value < . then length <- 0.1
length <- value
member this.YAngle
with get() = yangle
and set value =
if value > Math.PI/. then yangle <- Math.PI/.
elif value < -Math.PI/. then yangle <- -Math.PI/.
else yangle <- value
member this.XAngle
with get() = xangle
and set value =
if value > .* Math.PI then xangle <- value - .* Math.PI
elif value < . then xangle <- value + . * Math.PI
else xangle <- value
member this.PeopleAngle
with get()=
let mutable angle = this.XAngle + Math.PI/.
angle
member this.Direction
with get() =
let mutable len = .
if bThree then len <- length
let xyLength = Math.Cos(this.YAngle) * len
let x:float =float origin.X + xyLength * Math.Cos(this.XAngle)
let y:float =float origin.Y + len * Math.Sin(this.YAngle)
let z:float =float origin.Z + xyLength * Math.Sin(this.XAngle)
Vector3(float32 x,float32 y,float32 z)
member this.Transelt (x,y,z) =
let sinX = Math.Sin(this.XAngle)
let cosX = Math.Cos(this.XAngle)
let mutable xstep = x * sinX + z * cosX
let mutable zstep = z * sinX - x * cosX
if bThree then
xstep <- -xstep
zstep <- -zstep
let x1 = float origin.X + xstep
let y1 = float origin.Y + y
let z1 = float origin.Z + zstep
printfn "angle:%f, sinx:%f, cosx:%f" this.XAngle sinX cosX
printfn "x:%f, y:%f, z:%f" x1 y1 z1
origin <- new Vector3(float32 x1,float32 y1,float32 z1)
member this.Rotate (x,y) =
let xa = this.XAngle + x
let mutable ystep = y
if bThree then ystep <- -y
let ya =this.YAngle + ystep
this.YAngle <- ya
this.XAngle <- xa

xangle与yangle分别是指人或者摄像机当前的偏移量,上文中讲第一人称有讲。IsThree表示是在第三人称漫游情况下,这里面会计算球心Origin与球面Direction,在第一人称时,摄像机位置Eye是origin,摄像机方向Target是Direction,因为摄像机是方向向量,所以length此时固定为1来算,此时改变此值没什么意义,而在第三人称时,人是Origin,而摄像机Eye是Direction,此时length表示人与摄像机的距离,会造成视角内人物放大与放小的效果。

这段代码可以很好的工作,并且很容易就实现第一人称与第三人称的切换,需要注意的是,以第三人称漫游时,上下旋转和前后左右走动与第一人称是相反的,大家可以自己想像一下,在第三人称时,人向上,对应摄像机的在球面是向下动的,同理,前后左右走动也是一样,只有左右旋转时,第一人称与第三人称方向一致。

效果图,灯光还没设置完整,里面有点暗。

原来的附件在我的笔记本上HD5650可以运行,我发现在我老婆的电脑上,用的是650TI,会出现相关内存不能为读的问题,经查找,发现问题是在设置顶点数据//GL.VertexPointer(3,VertexPointerType.Float,0,IntPtr.Zero)这句代码有问题,现改为GL.InterleavedArrays(InterleavedArrayFormat.V3f,0,IntPtr.Zero),也可以运行,具体原因不知,有那么清楚,可以帮忙说明下。

下面给出实现第一人称与第三人称切换版的附件。

新增加快捷键V切换一三人称漫游,小键盘上的+与—分别是增加与缩小人与摄像机的距离。

一三人称小室切换版。

Opengl绘制我们的小屋(四)第三人称漫游的更多相关文章

  1. Opengl绘制我们的小屋(二)第一人称漫游

    这章我们先讲第一人称漫游的实现.在openTK里,我们用函数Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)来放置摄像机,其中三个参数分别与摄像 ...

  2. Opengl绘制我们的小屋(三)纹理绘制

    本准备先说光照相关实现,但是发现对那个模型实在看不下去了,于是先绘制纹理. 先看下基本纹理贴上去的显示效果.具体模型图请看上篇文章的实现,这篇只讲纹理实现. 我们常见的纹理绘制差不多如下,先写一个纹理 ...

  3. Opengl绘制我们的小屋(一)球体,立方体绘制

    这个系列我想用来运用opengl红皮书的前八章节的内容,来打造一个室内小屋. 这一章主要是定义几个基本的结构.并给出球体与立方体的画法,先让我们来定义一些基本的结构.一个是包含点,法向量,纹理贴图向量 ...

  4. NeHe OpenGL教程 第三十四课:地形

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. OpenGL学习之路(四)

    1 引子 上次读书笔记主要是学习了应用三维坐标变换矩阵对二维的图形进行变换,并附带介绍了GLSL语言的编译.链接相关的知识,之后介绍了GLSL中变量的修饰符,着重介绍了uniform修饰符,来向着色器 ...

  6. OpenGL绘制自由落体小球

    OpenGL绘制自由落体小球 一.    程序运行的软硬件环境 本次设计在window10系统下进行,运用C++进行编写,在CodeBlocks环境下使用OpenGL进行设计. 所需环境配置分为2部分 ...

  7. 【Unity】第11章 第三人称角色控制器和碰撞体

    分类:Unity.C#.VS2015 创建日期:2016-05-02 一.简介 第三人称视角控制器涉及的相关概念有: 1.刚体(Rigidbody). 2.碰撞体(Collider).包括球体碰撞体( ...

  8. OpenGl 绘制一个立方体

    OpenGl 绘制一个立方体 为了绘制六个正方形,我们为每个正方形指定四个顶点,最终我们需要指定6*4=24个顶点.但是我们知道,一个立方体其实总共只有八个顶点,要指定24次,就意味着每个顶点其实重复 ...

  9. 在Android中使用OpenGL ES开发第(四)节:相机预览

    笔者之前写了三篇Android中使用OpenGL ES入门级的文章,从OpenGL ES的相关概念出发,分析了利用OpenGL ES实现3D绘图的重要的两个步骤:定义形状和绘制形状,简单的绘制了一个三 ...

随机推荐

  1. jQuery页面引导插件 jquery-pagewalkthrough

    http://jwarby.github.io/jquery-pagewalkthrough/ 源码 https://github.com/jwarby/jquery-pagewalkthrough

  2. 深入理解Linux内核-进程

    1.进程的静态特性 进程:程序执行时的一个实例 进程描述符(task_struct): 进程的基本信息(thread_info).指向内存区描述符的指针(mm_struct).进程相关的tty(tty ...

  3. password学4——Java 加密解密之消息摘要算法(MD5 SHA MAC)

    Java 加密解密之消息摘要算法(MD5 SHA MAC) 消息摘要 消息摘要(Message Digest)又称为数字摘要(Digital Digest). 它是一个唯一相应一个消息或文本的固定长度 ...

  4. Gartner 2018新技术成熟度曲线

    https://blog.csdn.net/BtB5e6Nsu1g511Eg5XEg/article/details/82047719 近日,Gartner发布了2018年新技术成熟度曲线,首次将生物 ...

  5. Custom Sublime Text Build Systems For Popular Tools And Languages

    Sublime Text is currently the text editor of choice for a number of developers in the open-source co ...

  6. java8新特性(六):Stream多线程并行数据处理

    转:http://blog.csdn.net/sunjin9418/article/details/53143588 将一个顺序执行的流转变成一个并发的流只要调用 parallel()方法 publi ...

  7. Kali Linux安装Remmina无法加载RDP插件

    原因是确少匹配的 libfreerdp库 可以到这里下载 http://ftp.de.debian.org/debian/pool/main/f/freerdp/ 我的电脑是64位的  我下载的是ht ...

  8. Linux中查看GNOME版本号

    在使用图形终端时,可以在虚拟终端中直接输入gnome-about,会弹出如下窗口. 或者在纯命令行模式下使用下面命令: $ gnome-about --gnome-version 注:Gnome 3. ...

  9. 将目录下面所有的 .cs 文件合并到一个 code.cs 文件中,写著作权复制代码时的必备良药

    将目录下面所有的 .cs 文件合并到一个 code.cs 文件中,写著作权复制代码时的必备良药 @echo off echo 将该目录下所有.cs文件的内容合并到一个 code.cs 文件中! pau ...

  10. HTTP Status 500 PWC6188 jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application

    报错如下: 解决方案: 1.可能是依赖引用错了,注意 JSP 应依赖: <!-- JSP --> <dependency> <groupId>javax.servl ...