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

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

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

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

 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. 解决Installation failed with message Failed to finalize session : INSTALL_FAILED_INVALID_APK的问题

    Android Studio 运行AVD的时候出现: Installation failed with message Failed to finalize session : INSTALL_FAI ...

  2. 多个inputstream的情况下,watermark的值怎么赋值? kakfa中多个partition提取 watermark

    1,org.apache.flink.streaming.api.operators; AbstractStreamOperator public void processWatermark1(Wat ...

  3. vss整合配置连接到Myeclipse中以及中文配置

    配置过很久后 再次配置进行记录以免后续备用 1.下载vss插件和安装vss插件 org.vssplugin_1.6.2 解压到myeclipse 安装路径文件夹C:\MyEclipse 8.5\dro ...

  4. 每日英语:The Most Destructive, Unpredictable Force in Tech

    What's the most destructive force in the tech world, the thing that has nearly killed BlackBerry, pu ...

  5. 基于OCS实现高速缓存

    OCS简介 OCS( Open Cache Service)为分布式高速缓存服务,主要实现热点数据的快速响应: OCS支持Key/Value的数据结构,兼容memcachebinary protoco ...

  6. 在进行vue的学习,项目中需要引入bootstrap、jquery的步骤。

    在进行vue的学习,项目中需要引入bootstrap.jquery的步骤. 一.引入jQuery 在当前项目的目录下(就是package.json),运行命令 cnpm install jquery ...

  7. 判断URL文件是不是在于在。

    判断URL文件是不是在于在. private static bool UrlIsExist(string url) { System.Uri u = null; try { u = new Uri(u ...

  8. C# Null 赋值

    在此之前,我们先看一段程序: class Program    {        static void Main(string[] args)        {            Childre ...

  9. 修改zerolog使log输出的文件名可以在goland里自动定位--技巧

    如何自动定位文件 最近发现goland会自动识别输出的文件或者url,但是有时候又识别不出来,折腾了一下,发现原来要求文件路径或url两边要有空格 改造zerolog 既然如此,那么让我们来改造一下z ...

  10. Java并发编程:并发容器之CopyOnWriteArrayList<转>

    原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容 ...