热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

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

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

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

引用维基百科里的

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

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

x=r \sin\theta \cos\phiy=r \sin\theta \sin\phiz=r \cos\theta

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

人眼球面坐标

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

 1     type Camera() = 
2 let mutable eye = Vector3.Zero
3 let mutable eyeLength = 1.
4 let mutable yangle = 0.
5 let mutable xangle= Math.PI/2.
6 member this.Eye
7 with get() = eye
8 and set value = eye <- value
9 member this.EyeLength
10 with get() = eyeLength
11 and set value =
12 if value <0. then eyeLength <- 0.1
13 eyeLength <- value
14 member this.YAngle
15 with get() = yangle
16 and set value =
17 if value > Math.PI then yangle <- 0.
18 //elif value <0. then yangle <- Math.PI
19 else yangle <- value
20 member this.XAngle
21 with get() = xangle
22 and set value =
23 printfn "xangle:%f" value
24 if value > 2.* Math.PI then xangle <- 0.
25 elif value <0. then xangle <- 2. * Math.PI
26 else xangle <- value
27 member this.Target
28 with get() =
29 //printfn "%f" this.XAngle
30 let xyLength = Math.Cos(this.YAngle)
31 let x:float =float eye.X + eyeLength * xyLength * Math.Cos(this.XAngle)
32 let y:float =float eye.Y + eyeLength * Math.Sin(this.YAngle)
33 let z:float =float eye.Z + eyeLength * xyLength * Math.Sin(this.XAngle)
34 Vector3(float32 x,float32 y,float32 z)
35 member this.Transelt (x,y,z) =
36 let sinX = Math.Sin(this.XAngle)
37 let cosX = Math.Cos(this.XAngle)
38 let x1 = float this.Eye.X + x * sinX + z * cosX
39 let y1 = float this.Eye.Y + y
40 let z1 = float this.Eye.Z + z * sinX - x * cosX
41 printfn "angle:%f, sinx:%f, cosx:%f" this.XAngle sinX cosX
42 printfn "x:%f, y:%f, z:%f" x1 y1 z1
43 this.Eye <- new Vector3(float32 x1,float32 y1,float32 z1)
44 member this.UpAndDown y =
45 let ya = this.YAngle + y
46 this.YAngle <- ya
47 member this.RightAndLeft x =
48 let xa = this.XAngle + x
49 this.XAngle <- xa
50 member this.Rotate (x,y) =
51 let xa = this.XAngle + x
52 let ya = this.YAngle + y
53 this.YAngle <- ya
54 this.XAngle <- xa
View Code

可以看到我们的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绘制我们的小屋(四)第三人称漫游第一张图有更详细的说明.

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

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

  1 #r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.dll"
2 #r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.GLControl.dll"
3 #r "F:\3D\1.0\Binaries\OpenTK\Debug\Examples.exe"
4 #load "Shape.fsx"
5
6 open System
7 open System.Collections.Generic
8 open System.Windows.Forms
9 open System.Threading
10 open System.Drawing
11 open System.Drawing.Imaging
12 open OpenTK
13 open OpenTK.Graphics
14 open OpenTK.Graphics.OpenGL
15 open Shape
16
17 type loopForm() as form=
18 inherit Form()
19 let caram = Shape.Camera()
20 let offest = 0.1f
21 let offestd = float offest
22 let glCOntrol= new OpenTK.GLControl()
23
24 let cubeEnter = Shape.Cube(1.5f,0.2f,1.8f,3)
25 let cubeParlor = Shape.Cube(4.9f,0.2f,6.3f,3)
26 let cubeBalcony1 = Shape.Cube(4.9f,0.2f,1.9f,3)
27 let cubeBalcony2 = Shape.Cube(1.6f,0.2f,2.1f,3)
28 let cubeBalcony3 = Shape.Cube(1.8f,0.2f,3.0f,3)
29 let cubeKitchen = Shape.Cube(4.5f,0.2f,1.8f,3)
30 let cubeBathroom = Shape.Cube(2.1f,0.2f,2.1f,3)
31 let cubeGallery = Shape.Cube(2.1f,0.2f,1.2f,3)
32 let cubeMasterBedroom = Shape.Cube(3.6f,0.2f,3.9f,3)
33 let cubeSecOndbedroom= Shape.Cube(3.0f,0.2f,3.3f,3)
34
35 let cubeWall1 = Shape.Cube(0.2f,3.f,8.1f,0)
36 let cubeWall2 = Shape.Cube(4.5f,3.f,0.2f,0)
37 let cubeWall3 = Shape.Cube(0.2f,3.f,1.8f,0)
38 let cubeWall4 = Shape.Cube(0.2f,1.8f,1.8f,0)
39 let cubeWall5 = Shape.Cube(3.0f,3.f,0.2f,0)
40 let cubeWall6 = Shape.Cube(0.2f,3.f,3.7f,0)
41 let cubeBathroom1 = Shape.Cube(2.1f,3.f,0.2f,0)
42 let cubeBathroom2 = Shape.Cube(0.2f,3.f,2.1f,0)
43 let cubeFence1 = Shape.Cube(0.05f,0.02f,1.9f,0)
44 let cubeFence2 = Shape.Cube(2.3f,0.02f,0.05f,0)
45 let cubeFence3 = Shape.Cube(0.05f,0.02f,2.1f,0)
46 let cubeFence4 = Shape.Cube(3.4f,0.02f,0.05f,0)
47 let cubeWall7 = Shape.Cube(2.1f,3.f,0.2f,4)
48 let cubeWall8 = Shape.Cube(0.2f,3.f,3.0f,0)
49 let cubePillar = Shape.Cube(0.2f,3.f,0.2f,4)
50 let cubePillar2 = Shape.Cube(0.1f,1.1f,0.2f,4)
51 let mutable oldMouseLocation = Vector2.Zero
52 do
53 caram.Transelt(1.,1.7,0.0)
54 form.SuspendLayout()
55 glControl.Location <- new Point(10,40)
56 glControl.Size <- new Size(400,300)
57 glControl.BackColor <- Color.Red
58 glControl.Resize.Add(form.resize)
59 glControl.Paint.Add(form.paint)
60 glControl.KeyDown.Add(form.KeyDown)
61 glControl.MouseMove.Add(form.MouseDownv)
62 form.ClientSize <- new Size(450,350)
63 form.Text <- "opengl"
64 form.StartPosition <- FormStartPosition.Manual
65 form.Location <- new Point(1200,600)
66 form.Controls.Add(glControl)
67 form.ResumeLayout(false)
68 //#endregion
69 override v.OnLoad e =
70 base.OnLoad e
71 GL.ClearColor Color.MidnightBlue
72 Application.Idle.Add(v.AIdle)
73 GL.FrontFace FrontFaceDirection.Ccw
74 GL.Enable( EnableCap.PointSmooth )
75 //踢除正反面
76 GL.Enable EnableCap.CullFace
77 GL.CullFace CullFaceMode.Back
78 //设置材料面填充模式
79 GL.PolygonMode(MaterialFace.Front,PolygonMode.Fill)
80 GL.PolygonMode(MaterialFace.Back,PolygonMode.Line)
81 //#region
82 member v.resize (e:EventArgs) =
83 GL.Viewport(0,0,glControl.ClientSize.Width,glControl.ClientSize.Height)
84 let aspect = float32 glControl.ClientSize.Width /float32 glControl.ClientSize.Height
85 let mutable projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver2,aspect,0.1f,30.f)
86 GL.MatrixMode MatrixMode.Projection
87 GL.LoadMatrix(&projection)
88 member v.paint (e:PaintEventArgs) =
89 v.Render()
90 member v.AIdle (e:EventArgs) =
91 while (glControl.IsIdle) do
92 v.Render()
93 member v.MouseDownv(e:MouseEventArgs) =
94 if e.Button = MouseButtons.Right then
95 if oldMouseLocation = Vector2.Zero then
96 oldMouseLocation <- Vector2(float32 e.X,float32 e.Y)
97 else
98 let nx = (float32 e.X - oldMouseLocation.X) * offest * 0.1f
99 let ny = (float32 e.Y - oldMouseLocation.Y) * offest * -0.1f
100 caram.Rotate(float nx,float ny)
101 oldMouseLocation <- Vector2(float32 e.X,float32 e.Y)
102 member v.KeyDown(e:KeyEventArgs) =
103 //let keys = e.KeyData
104 match e.KeyCode with
105 | Keys.E ->caram.Transelt(0.,0.,1.* offestd)
106 | Keys.D ->caram.Transelt(0.,0.,-1.* offestd)
107 | Keys.S ->caram.Transelt(1.* offestd,0.,0.0)
108 | Keys.F ->caram.Transelt(-1.* offestd,0.,0.)
109 | _ -> ()
110 member x.UIValue
111 with get (text:TextBox) =
112 let mutable value = 0.f
113 if System.Single.TryParse(text.Text,&value) then
114 value <- value
115 value
116 and set (text:TextBox) (value:float32) = text.Text<- value.ToString()
117 //#endregion
118 member v.Render =
119 //Keyboard[OpenTK.Input.Key.F11]
120 GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit)
121 //地面
122 v.DrawCube(cubeEnter,Vector3.Zero)
123 v.DrawCube(cubeParlor,0.f,0.f,1.8f)
124 v.DrawCube(cubeBalcony1,0.f,0.f,8.1f)
125 v.DrawCube(cubeBalcony2,2.3f,0.f,10.f)
126 v.DrawCube(cubeBalcony3,3.9f,0.f,9.f)
127 v.DrawCube(cubeKitchen,1.5f,0.f,0.f)
128 v.DrawCube(cubeBathroom,3.9f,0.f,1.8f)
129 v.DrawCube(cubeGallery,3.9f,0.f,3.9f)
130 v.DrawCube(cubeMasterBedroom,3.9f,0.f,5.1f)
131 v.DrawCube(cubeSecondbedroom,6.0f,0.f,1.8f)
132 //
133 v.DrawCube(cubeWall1,Vector3.Zero)
134 v.DrawCube(cubeWall2,Vector3(1.5f,0.f,0.f))
135 v.DrawCube(cubeWall3,Vector3(1.5f,0.f,0.f))
136 v.DrawCube(cubeWall4,Vector3(6.0f,0.f,0.f))
137 v.DrawCube(cubeWall5,Vector3(6.0f,0.f,1.8f))
138 v.DrawCube(cubeWall6,Vector3(9.0f,0.f,1.8f))
139 v.DrawCube(cubeWall6,Vector3(7.5f,0.f,5.1f))
140 v.DrawCube(cubeWall6,Vector3(3.9f,0.f,5.1f))
141 v.DrawCube(cubeWall7,Vector3(3.9f,0.f,9.0f))
142 v.DrawCube(cubeWall8,Vector3(6.0f,0.f,9.0f))
143 v.DrawCube(cubeBathroom1,Vector3(3.9f,0.f,1.8f))
144 //v.DrawCube(cubeBathroom1,Vector3(3.9f,0.f,3.9f))
145 v.DrawCube(cubeBathroom2,Vector3(3.9f,0.f,1.8f))
146 v.DrawCube(cubeBathroom2,Vector3(6.0f,0.f,1.8f))
147 v.DrawCube(cubeFence1,0.0f,1.f,8.1f)
148 v.DrawCube(cubeFence2,0.0f,1.f,10.f)
149 v.DrawCube(cubeFence3,2.3f,1.f,10.f)
150 v.DrawCube(cubeFence4,2.3f,1.f,12.1f)
151 v.DrawCube(cubePillar,Vector3(2.3f,0.f,12.f))
152 v.DrawCube(cubePillar,Vector3(2.3f,0.f,9.9f))
153 v.DrawCube(cubePillar2,Vector3(0.f,0.f,9.9f))
154 //房顶
155 GL.PushMatrix()
156 GL.Translate(0.f,3.f,0.f)
157 v.DrawCube(cubeEnter,Vector3.Zero)
158 v.DrawCube(cubeParlor,0.f,0.f,1.8f)
159 v.DrawCube(cubeBalcony1,0.f,0.f,8.1f)
160 v.DrawCube(cubeBalcony2,2.3f,0.f,10.f)
161 v.DrawCube(cubeBalcony3,3.9f,0.f,9.f)
162 v.DrawCube(cubeKitchen,1.5f,0.f,0.f)
163 v.DrawCube(cubeBathroom,3.9f,0.f,1.8f)
164 v.DrawCube(cubeGallery,3.9f,0.f,3.9f)
165 v.DrawCube(cubeMasterBedroom,3.9f,0.f,5.1f)
166 v.DrawCube(cubeSecondbedroom,6.0f,0.f,1.8f)
167 GL.PopMatrix()
168 let mutable lookat = Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)
169 GL.MatrixMode(MatrixMode.Modelview)
170 GL.LoadMatrix(&lookat)
171 glControl.SwapBuffers()
172 ignore
173 member v.DrawCube(cube:Shape.Cube,pos:Vector3) =
174 v.DrawCube(cube,pos,cube.Index)
175 member v.DrawCube(cube:Shape.Cube,x,y,z) =
176 v.DrawCube(cube,new Vector3(x,y,z),cube.Index)
177 member v.DrawCube(cube:Shape.Cube,x,y,z,ind) =
178 v.DrawCube(cube,new Vector3(x,y,z),ind)
179 member v.DrawCube(cube:Shape.Cube, pos:Vector3, ind:int) =
180 GL.PushMatrix()
181 cube.Index <- ind
182 GL.Translate(pos)
183 cube.Draw()
184 GL.PopMatrix()
185 let t = new loopForm()
186 t.Show()
View Code

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

 


推荐阅读
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
author-avatar
mobiledu2502913173
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有