zoukankan      html  css  js  c++  java
  • Opengl绘制我们的小屋(二)第一人称漫游

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

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

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

    引用维基百科里的

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

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

    x=r sin	heta cosphiy=r sin	heta sinphiz=r cos	heta

    上面的这些大家都好理解,我们的漫游模型可以根据这个来,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:3D1.0BinariesOpenTKDebugOpenTK.dll"
      2 #r "F:3D1.0BinariesOpenTKDebugOpenTK.GLControl.dll"
      3 #r "F:3D1.0BinariesOpenTKDebugExamples.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

  • 相关阅读:
    30岁的程序猿坐的太久,也要用一下脑子
    HIPO图
    CMake入门(二)
    hdu1711 Number Sequence
    EF架构~在ef中支持IQueryable级别的Contains被翻译成了Exists,性能可以接受!
    JS框架~Angularjs
    将不确定变为确定~transactionscope何时提升为分布式事务?(sql2005数据库解决提升到MSDTC的办法)
    SignalR实现服务器与客户端的实时通信
    基础才是重中之重~LazyInitializer.EnsureInitialized对属性实现化的性能优化
    [置顶] 电视机顶盒搜台原理和方法简析
  • 原文地址:https://www.cnblogs.com/zhouxin/p/3418239.html
Copyright © 2011-2022 走看看