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

本来为了查找漫游的功能,在网上找了些,发现相关讲解都很少,更多只是写出了代码,花了一些时间查找相关概念与调试,其中把我的理解会说明上,有不对的地方欢迎大家指出.

漫游最基本的功能,我们包括相关步进,前进,后退,左进,右进.还有就是视角旋转,包含水平方向旋转,以及垂直方向上的旋转.在讲下面之前,我们先来看下什么是球面坐标系.

引用维基百科里的

数学里,球坐标系英语Spherical coordinate system)是一种利用球坐标 表示一个点 p 在三维空间的位置的三维正交坐标系

右图显示了球坐标的几何意义:原点与点 P 之间的径向距离 ,原点到点 P 的连线与正 z-轴之间的天顶角 ,以及原点到点 P 的连线,在 xy-平面的投影线,与正 x-轴之间的方位角

上面的这些大家都好理解,我们的漫游模型可以根据这个来,eye是摄像机位置,p摄像机朝向向量.但是我们的坐标应该是这样的.

根据上面的我们来完成我们摄像机类,如下.

     type Camera() =
let mutable eye = Vector3.Zero
let mutable eyeLength = .
let mutable yangle = .
let mutable xangle= Math.PI/.
member this.Eye
with get() = eye
and set value = eye <- value
member this.EyeLength
with get() = eyeLength
and set value =
if value < . then eyeLength <- 0.1
eyeLength <- value
member this.YAngle
with get() = yangle
and set value =
if value > Math.PI then yangle <- .
//elif value < 0. then yangle <- Math.PI
else yangle <- value
member this.XAngle
with get() = xangle
and set value =
printfn "xangle:%f" value
if value > .* Math.PI then xangle <- .
elif value < . then xangle <- . * Math.PI
else xangle <- value
member this.Target
with get() =
//printfn "%f" this.XAngle
let xyLength = Math.Cos(this.YAngle)
let x:float =float eye.X + eyeLength * xyLength * Math.Cos(this.XAngle)
let y:float =float eye.Y + eyeLength * Math.Sin(this.YAngle)
let z:float =float eye.Z + eyeLength * 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 x1 = float this.Eye.X + x * sinX + z * cosX
let y1 = float this.Eye.Y + y
let z1 = float this.Eye.Z + z * sinX - x * cosX
printfn "angle:%f, sinx:%f, cosx:%f" this.XAngle sinX cosX
printfn "x:%f, y:%f, z:%f" x1 y1 z1
this.Eye <- new Vector3(float32 x1,float32 y1,float32 z1)
member this.UpAndDown y =
let ya = this.YAngle + y
this.YAngle <- ya
member this.RightAndLeft x =
let xa = this.XAngle + x
this.XAngle <- xa
member this.Rotate (x,y) =
let xa = this.XAngle + x
let ya = this.YAngle + y
this.YAngle <- ya
this.XAngle <- xa

可以看到我们的Target,就是看的方向,根据球面坐标,我们可以通过xangle与yangle来求得。而xangle与yangle可以根据上面给出的UpAndDown,RightAndLeft,Rotate这三个方法来增加对应我们在水平与垂直方向上的旋转角度.其中Xangle默认是PI/2,就是90度,是因为默认我们是向前看,也就是向Z轴看.

然后是左右,上下前进的代码,这部分逻辑代码在this.Transelt (x,y,z) ,x,y,z分别表示左右,上下,前后上的走的值,最开始我用的(x1,z1) = this.Eye.(X,Z) + (x,z)这样发现如果没有进行旋转,那么是对的,但是如果我旋转90度后来看,前后变成左右了,大家可以试着分析下相关原因,比如前进,我们增加的是z的值.但是我们旋转90度后,我们再来看,这是前进对应的是x轴的值,所以实际上,我们前后左右走,还与我们的左右旋转的角度xangle有关,实际我们应该是以方向向量为xangle方向在对应的x与z值的投影值.

如果这个图还是不理解,可以看Opengl绘制我们的小屋(四)第三人称漫游第一张图有更详细的说明.

下面我们进入我们主题,画一个室内景,下面是我在网上找的一个室内模型图,用手机照的,可能不是很清晰.

我们根据上面的模型来建模,相关立方体用上文提供的绘制方法,相关代码如下.

 #r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.dll"
#r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.GLControl.dll"
#r "F:\3D\1.0\Binaries\OpenTK\Debug\Examples.exe"
#load "Shape.fsx" open System
open System.Collections.Generic
open System.Windows.Forms
open System.Threading
open System.Drawing
open System.Drawing.Imaging
open OpenTK
open OpenTK.Graphics
open OpenTK.Graphics.OpenGL
open Shape type loopForm() as form=
inherit Form()
let caram = Shape.Camera()
let offest = 0.1f
let offestd = float offest
let glControl = new OpenTK.GLControl() let cubeEnter = Shape.Cube(1.5f,0.2f,1.8f,)
let cubeParlor = Shape.Cube(4.9f,0.2f,6.3f,)
let cubeBalcony1 = Shape.Cube(4.9f,0.2f,1.9f,)
let cubeBalcony2 = Shape.Cube(1.6f,0.2f,2.1f,)
let cubeBalcony3 = Shape.Cube(1.8f,0.2f,3.0f,)
let cubeKitchen = Shape.Cube(4.5f,0.2f,1.8f,)
let cubeBathroom = Shape.Cube(2.1f,0.2f,2.1f,)
let cubeGallery = Shape.Cube(2.1f,0.2f,1.2f,)
let cubeMasterBedroom = Shape.Cube(3.6f,0.2f,3.9f,)
let cubeSecondbedroom = Shape.Cube(3.0f,0.2f,3.3f,) let cubeWall1 = Shape.Cube(0.2f,.f,8.1f,)
let cubeWall2 = Shape.Cube(4.5f,.f,0.2f,)
let cubeWall3 = Shape.Cube(0.2f,.f,1.8f,)
let cubeWall4 = Shape.Cube(0.2f,1.8f,1.8f,)
let cubeWall5 = Shape.Cube(3.0f,.f,0.2f,)
let cubeWall6 = Shape.Cube(0.2f,.f,3.7f,)
let cubeBathroom1 = Shape.Cube(2.1f,.f,0.2f,)
let cubeBathroom2 = Shape.Cube(0.2f,.f,2.1f,)
let cubeFence1 = Shape.Cube(0.05f,0.02f,1.9f,)
let cubeFence2 = Shape.Cube(2.3f,0.02f,0.05f,)
let cubeFence3 = Shape.Cube(0.05f,0.02f,2.1f,)
let cubeFence4 = Shape.Cube(3.4f,0.02f,0.05f,)
let cubeWall7 = Shape.Cube(2.1f,.f,0.2f,)
let cubeWall8 = Shape.Cube(0.2f,.f,3.0f,)
let cubePillar = Shape.Cube(0.2f,.f,0.2f,)
let cubePillar2 = Shape.Cube(0.1f,1.1f,0.2f,)
let mutable oldMouseLocation = Vector2.Zero
do
caram.Transelt(.,1.7,0.0)
form.SuspendLayout()
glControl.Location <- new Point(,)
glControl.Size <- new Size(,)
glControl.BackColor <- Color.Red
glControl.Resize.Add(form.resize)
glControl.Paint.Add(form.paint)
glControl.KeyDown.Add(form.KeyDown)
glControl.MouseMove.Add(form.MouseDownv)
form.ClientSize <- new Size(,)
form.Text <- "opengl"
form.StartPosition <- FormStartPosition.Manual
form.Location <- new Point(,)
form.Controls.Add(glControl)
form.ResumeLayout(false)
//#endregion
override v.OnLoad e =
base.OnLoad e
GL.ClearColor Color.MidnightBlue
Application.Idle.Add(v.AIdle)
GL.FrontFace FrontFaceDirection.Ccw
GL.Enable( EnableCap.PointSmooth )
//踢除正反面
GL.Enable EnableCap.CullFace
GL.CullFace CullFaceMode.Back
//设置材料面填充模式
GL.PolygonMode(MaterialFace.Front,PolygonMode.Fill)
GL.PolygonMode(MaterialFace.Back,PolygonMode.Line)
//#region
member v.resize (e:EventArgs) =
GL.Viewport(,,glControl.ClientSize.Width,glControl.ClientSize.Height)
let aspect = float32 glControl.ClientSize.Width /float32 glControl.ClientSize.Height
let mutable projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver2,aspect,0.1f,.f)
GL.MatrixMode MatrixMode.Projection
GL.LoadMatrix(&projection)
member v.paint (e:PaintEventArgs) =
v.Render()
member v.AIdle (e:EventArgs) =
while (glControl.IsIdle) do
v.Render()
member v.MouseDownv(e:MouseEventArgs) =
if e.Button = MouseButtons.Right then
if oldMouseLocation = Vector2.Zero then
oldMouseLocation <- Vector2(float32 e.X,float32 e.Y)
else
let nx = (float32 e.X - oldMouseLocation.X) * offest * 0.1f
let ny = (float32 e.Y - oldMouseLocation.Y) * offest * -0.1f
caram.Rotate(float nx,float ny)
oldMouseLocation <- Vector2(float32 e.X,float32 e.Y)
member v.KeyDown(e:KeyEventArgs) =
//let keys = e.KeyData
match e.KeyCode with
| Keys.E ->caram.Transelt(.,.,.* offestd)
| Keys.D ->caram.Transelt(.,.,-.* offestd)
| Keys.S ->caram.Transelt(.* offestd,.,0.0)
| Keys.F ->caram.Transelt(-.* offestd,.,.)
| _ -> ()
member x.UIValue
with get (text:TextBox) =
let mutable value = .f
if System.Single.TryParse(text.Text,&value) then
value <- value
value
and set (text:TextBox) (value:float32) = text.Text<- value.ToString()
//#endregion
member v.Render =
//Keyboard[OpenTK.Input.Key.F11]
GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit)
//地面
v.DrawCube(cubeEnter,Vector3.Zero)
v.DrawCube(cubeParlor,.f,.f,1.8f)
v.DrawCube(cubeBalcony1,.f,.f,8.1f)
v.DrawCube(cubeBalcony2,2.3f,.f,.f)
v.DrawCube(cubeBalcony3,3.9f,.f,.f)
v.DrawCube(cubeKitchen,1.5f,.f,.f)
v.DrawCube(cubeBathroom,3.9f,.f,1.8f)
v.DrawCube(cubeGallery,3.9f,.f,3.9f)
v.DrawCube(cubeMasterBedroom,3.9f,.f,5.1f)
v.DrawCube(cubeSecondbedroom,6.0f,.f,1.8f)
//墙
v.DrawCube(cubeWall1,Vector3.Zero)
v.DrawCube(cubeWall2,Vector3(1.5f,.f,.f))
v.DrawCube(cubeWall3,Vector3(1.5f,.f,.f))
v.DrawCube(cubeWall4,Vector3(6.0f,.f,.f))
v.DrawCube(cubeWall5,Vector3(6.0f,.f,1.8f))
v.DrawCube(cubeWall6,Vector3(9.0f,.f,1.8f))
v.DrawCube(cubeWall6,Vector3(7.5f,.f,5.1f))
v.DrawCube(cubeWall6,Vector3(3.9f,.f,5.1f))
v.DrawCube(cubeWall7,Vector3(3.9f,.f,9.0f))
v.DrawCube(cubeWall8,Vector3(6.0f,.f,9.0f))
v.DrawCube(cubeBathroom1,Vector3(3.9f,.f,1.8f))
//v.DrawCube(cubeBathroom1,Vector3(3.9f,0.f,3.9f))
v.DrawCube(cubeBathroom2,Vector3(3.9f,.f,1.8f))
v.DrawCube(cubeBathroom2,Vector3(6.0f,.f,1.8f))
v.DrawCube(cubeFence1,0.0f,.f,8.1f)
v.DrawCube(cubeFence2,0.0f,.f,.f)
v.DrawCube(cubeFence3,2.3f,.f,.f)
v.DrawCube(cubeFence4,2.3f,.f,12.1f)
v.DrawCube(cubePillar,Vector3(2.3f,.f,.f))
v.DrawCube(cubePillar,Vector3(2.3f,.f,9.9f))
v.DrawCube(cubePillar2,Vector3(.f,.f,9.9f))
//房顶
GL.PushMatrix()
GL.Translate(.f,.f,.f)
v.DrawCube(cubeEnter,Vector3.Zero)
v.DrawCube(cubeParlor,.f,.f,1.8f)
v.DrawCube(cubeBalcony1,.f,.f,8.1f)
v.DrawCube(cubeBalcony2,2.3f,.f,.f)
v.DrawCube(cubeBalcony3,3.9f,.f,.f)
v.DrawCube(cubeKitchen,1.5f,.f,.f)
v.DrawCube(cubeBathroom,3.9f,.f,1.8f)
v.DrawCube(cubeGallery,3.9f,.f,3.9f)
v.DrawCube(cubeMasterBedroom,3.9f,.f,5.1f)
v.DrawCube(cubeSecondbedroom,6.0f,.f,1.8f)
GL.PopMatrix()
let mutable lookat = Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadMatrix(&lookat)
glControl.SwapBuffers()
ignore
member v.DrawCube(cube:Shape.Cube,pos:Vector3) =
v.DrawCube(cube,pos,cube.Index)
member v.DrawCube(cube:Shape.Cube,x,y,z) =
v.DrawCube(cube,new Vector3(x,y,z),cube.Index)
member v.DrawCube(cube:Shape.Cube,x,y,z,ind) =
v.DrawCube(cube,new Vector3(x,y,z),ind)
member v.DrawCube(cube:Shape.Cube, pos:Vector3, ind:int) =
GL.PushMatrix()
cube.Index <- ind
GL.Translate(pos)
cube.Draw()
GL.PopMatrix()
let t = new loopForm()
t.Show()

v.MouseDownv(e:MouseEventArgs)实际对应的鼠标移动,作用是检测鼠标右键按下后,鼠标移动向量,然后进行上下,左右的旋转.

v.KeyDown(e:KeyEventArgs) 就是EDSF行走.对应前后左右.

在Render里,就是我们绘制的模型代码,先画模型,然后设置相机.别的如投影矩阵,大家如果有兴趣,可以自己查找相关资料.

let mutable lookat = Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadMatrix(&lookat)

效果图.我不否认这很丑,可能下一章节还要丑,下一章节如果没意外,会先讲灯光的应用,然后才是纹理.

附件为相关代码以及可运行程序,操作方式和网游一样,鼠标右键按下,上下左右移动是视角.WASD行走,(代码里是EDSF,游戏里的快捷键习惯).

后面我会介绍灯光与纹理贴图的相关应用.

附件地址 http://files.cnblogs.com/zhouxin/%E9%99%84%E4%BB%B6.rar

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

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

    本节内容是在第一人称漫游上完成的,请先了解上文中第一人称漫游的实现. 这一节讲下第三人称漫游是如何实现,第三人称,简单来说,就是在你后面会跟着一台摄像机顺着你拍摄. 先看一下失败的尝试.这个方法是把人 ...

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

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

  3. 基于HTML5及WebGL开发的2D3D第一人称漫游进行碰撞检测

    为了实现一个基于HTML5的场景小游戏,我采用了HT for Web来实现,短短200行代码,我就能实现用“第一人称”来操作前进后退上下左右,并且实现了碰撞检测. 先来看下实现的效果:http://h ...

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

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

  5. 第一课、OpenGL绘制直线等等

    第一课.OpenGL绘制直线等等 分类: [开发技术]OpenGL 2012-01-18 14:59 5217人阅读 评论(0) 收藏 举报 buffer图形c // //  main.c //  o ...

  6. OpenGL绘制自由落体小球

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

  7. 【转】265行JavaScript代码的第一人称3D H5游戏Demo

    译文:http://blog.jobbole.com/70956/ 原文:http://www.playfuljs.com/a-first-person-engine-in-265-lines/ 这是 ...

  8. [UE4]使用Is Locally Controlled解决第一人称和第三人称武器位置问题

    一.在第一人称网络游戏中,自己看到的是第一人称,其他玩家看到的自己是第三人称. 二.由于第一人称和第三人称是不同的模型,所以枪在模型上面的插槽位置也会不一样. 三.在武器挂载在人物模型的使用,使用“I ...

  9. [UE4]第一人称与第三人称

    一.给Character添加一个SkeletalMesh,并设置为第三人称模型.并设置自己看不到该模型. 二.添加给骨骼的右手添加一个Socket插槽用来挂载武器 三.判断当前角色是否被本地Contr ...

随机推荐

  1. Java实现单链表翻转

    单链表翻转比方有例如以下链表: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmVuZ3NoaXp0eQ==/font/5a6L5L2T/fontsize ...

  2. 如何从angular2中的url获取查询参数?

    问题描述 我使用angular2.0.0-beta.7.当组件在像”/path?query=value1″这样的路径上加载时,它被重定向到”/path”.为什么删除了GET参数?如何保留参数? 路由器 ...

  3. pandas数组和numpy数组在使用索引数组过滤数组时的区别

    numpy array 过滤后的数组,索引值从 0 开始. pandas Series 过滤后的 Series ,保持原来的索引,原来索引是几,就是几. 什么意思呢,来看个栗子: import num ...

  4. mac book air 与 virtual box 网络互访 安装centos7 mini

    Host-only Adapter     主机模式     虚拟机之间可以访问...主机可以访问虚拟机 NAT                          网络地址转换模式(Network A ...

  5. 在IIS服务器上屏蔽IP的访问

    今天就跟大家分享一下在IIS服务器上如何屏蔽特定IP的访问,希望对大家有所帮助. 第一种方法:通过iis中的ip地址和域名限制. 此方法简单有效,建议使用 点击网站--右键属性--目录安全性--IP地 ...

  6. JIRA python篇之展示多人未完成任务列表

    [本文出自天外归云的博客园] 通过python中的jira类我们可以方便的操作jira,获取一些我们想要再加工的信息. 这里举例,用html页面的形式展示分派给组内每个人的任务(未完成的.正在进行中的 ...

  7. 【C++程序员学 python】python split and join 分割与合并

    感觉这名字有点不对,但不知道用什么好,就将就吧. 坑爹啊,居然要把符号放在前面.

  8. C#学习笔记(22)——C#创建文本文件txt并追加写入数据

    说明(2017-7-31 16:25:06): 1. 有两种办法,第一种是用FileStream创建txt,用StreamWriter写入数据,期间还要加上判断,是否存在这个txt文件,如果不存在就创 ...

  9. EF中的1:0或1:1关系以及1:n关系

    先给出1:0关系 User表包括用户名和密码 public class User { public int ID { get; set; } public string UserName { get; ...

  10. Springmvc 定时器的实现

    有时候会需要项目中,定时去执行一些东西,这个时候就需要用到定时器了.比较简单, 当你springmvc环境搭建成功的时候. 本文转载自:https://www.cnblogs.com/wqj-blog ...