zoukankan      html  css  js  c++  java
  • 贝塞尔曲线.简单推导与用opengl实现动态画出。

    在opengl中,我们可以用少许的参数来描述一个曲线,其中贝塞尔曲线算是一种很常见的曲线控制方法,我们先来看维基百科里对贝塞尔曲线的说明:

    线性贝塞尔曲线

    给定点P0P1,线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:

    mathbf{B}(t)=mathbf{P}_0 + (mathbf{P}_1-mathbf{P}_0)t=(1-t)mathbf{P}_0 + tmathbf{P}_1 mbox{ , } t in [0,1]

    且其等同于线性插值

    二次方贝塞尔曲线

    二次方贝塞尔曲线的路径由给定点P0P1P2的函数Bt)追踪:

    mathbf{B}(t) = (1 - t)^{2}mathbf{P}_0 + 2t(1 - t)mathbf{P}_1 + t^{2}mathbf{P}_2 mbox{ , } t in [0,1]

    TrueType字体就运用了以贝塞尔样条组成的二次贝塞尔曲线。

    一些关于参数曲线的术语,有

    mathbf{B}(t) = sum_{i=0}^n mathbf{P}_imathbf{b}_{i,n}(t),quad tin[0,1]

    即多项式

    mathbf{b}_{i,n}(t) = {nchoose i} t^i (1-t)^{n-i},quad i=0,ldots n

    又称作n阶的伯恩斯坦基底多项式,定义00 = 1。

    Pi称作贝塞尔曲线的控制点多边形以带有线的贝塞尔点连接而成,起始于P0并以Pn终止,称作贝塞尔多边形(或控制多边形)。贝塞尔多边形的凸包(convex hull)包含有贝塞尔曲线。

    在这里贴这些,是因为让我们有个基本的理解,下面把维基百科里一个动态图放上,让大家有更清晰了解贝塞尔曲线是如何生成的。

    然后我们来分析相应数据的产生。最简单的二点,其实就是线段的参数化,大家能简单得到f(t)=p0+(p1-p0)*t=(1-t)p0+t*p1.

    二次方贝塞尔曲线话,我直接用下面的图给出相应过程。

    对应的三次方贝塞尔曲线,我们会如下图来门简略说明。

    在上面,我们给出用我们推导的过程。下面是根据这个过程生成的主要代码。

     1 type BezierCurve() =
     2     static member BasicCurve (p0:Vector3, p1:Vector3,t) = (1.f-t)*p0 + t*p1
     3     static member GetCurveValue(points : Vector3[],t:float32) =
     4         //求得我们是几次方贝兹曲线
     5         let j = points.Length - 1
     6         //复制最先的点p0-pn,以免被新赋值。
     7         let mutable ps = [| for p in points -> p|]
     8         //这是控制层数,如三次方贝兹曲线
     9         //则分别是第一层[p0;p1;p2;p3]二[p4;p5;p6]三[p7;p8]四[p9]
    10         for n = j downto 1 do 
    11             for i = 0 to n - 1 do
    12                 let p0 = ps.[i]
    13                 let p1 = ps.[i+1]
    14                 //如上,根据(p0,p1求p4)(p1,p2求p5),(p4,p5求p7)
    15                 ps.[i] <- BezierCurve.BasicCurve(p0,p1,t)
    16         //就是上面p9
    17         ps.[0]
    18     static member CreateCurve(points : Vector3[],count:int) =
    19         let ps = Array.create count Vector3.Zero
    20         let step = 1.f/float32 count
    21         let len = count - 1
    22         for i = 0 to len do
    23             ps.[i] <- BezierCurve.GetCurveValue(points,step * float32 i)
    24         ps
    25     static member GetCurveValueS(points : Vector3[],t:float32) =
    26         let j = points.Length - 1
    27         let mutable ps = [| for p in points -> p|]
    28         let mutable rs = [||]
    29         for n = j downto 1 do 
    30             for i = 0 to n - 1 do
    31                 let p0 = ps.[i]
    32                 let p1 = ps.[i+1]
    33                 ps.[i] <- BezierCurve.BasicCurve(p0,p1,t)
    34             rs <- Array.append rs ps.[0 .. n-1]
    35         ps.[0],rs
    View Code

    BasicCurve就是我们线段的参数化,pt=p0+(p1-p0)*t=(1-t)p0+t*p1.

    而GetCurveValue就是如上面所示,求多次方贝兹曲线在t(0<t<1)时的值。

    而CreateCurve这个就是我们要生成的贝兹曲线在程序里的精度,值越高则画的点越多。看起来越逼真。

    那下来,我们来生成如维基百科的那种动态图,也好加深的我们的印象。根据动态图,我们要知道的是,在t点,每一层相邻的二个点走到那了(也就是上面的GetCurveValueS的实现),这里给出主要的代码。

     1     override v.OnRenderFrame(e) =
     2         base.OnRenderFrame e 
     3         GL.Clear (ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit)
     4         let mutable lookat = Matrix4.LookAt(caram.Eye,caram.Target,Vector3.UnitY)
     5         GL.MatrixMode(MatrixMode.Modelview)
     6         GL.LoadMatrix(&lookat)
     7 
     8         GL.Color3(Color.White)
     9         GL.VertexPointer(3,VertexPointerType.Float,0,vs)
    10         GL.DrawArrays(BeginMode.LineStrip,0,vs.Length)
    11         currentTime <- currentTime + e.Time
    12         if currentTime > totalTime  && frame < allFrame then
    13             frame <- frame + 1 
    14             let currentStep =float32 frame/float32 allFrame
    15             let v = BezierCurve.GetCurveValueS(vs,currentStep)
    16             ac <- Array.append ac [|fst v|]
    17             ps <- snd v
    18             currentTime <- currentTime - totalTime 
    19             printfn "ac:%A" ps  
    20         let mutable step = 0
    21         for i = vs.Length - 2 downto 1 do
    22             if ps.Length > step + i then
    23                 let is = ps.[step .. step + i]
    24                 GL.VertexPointer(3,VertexPointerType.Float,0,is)
    25                 GL.DrawArrays(BeginMode.LineStrip,0,is.Length)
    26                 step <- step + i + 1
    27                 printfn "is:%A" is     
    28         GL.Color3(Color.Red)                      
    29         GL.VertexPointer(3,VertexPointerType.Float,0,ac)
    30         GL.DrawArrays(BeginMode.LineStrip,0,ac.Length)
    31         v.SwapBuffers()
    View Code

    在上面,我们用totalTime控制显示的快慢,用allFrame控制我们要显示的精度。frame表示当前t点的情况。

    相关效果图:

    下面给出相关源代码的附件

    贝塞尔曲线

    其中EDSF移动镜头,小键盘上的+与-分别控制动画的速度,R键重新开始画曲线过程。

    贝塞尔曲面在opengl有比较容易的实现用求值器,下面是根据opengl红皮书的一个例子改的,定义一个数组,排列顺序按v,u,z来,u表示行,v表示每列,z表示是Vector3,Vector4的几个顶点,如:

     1        let vvs = [|
     2                 //-1.5;-1.5;4.0; -0.5;-1.5;2.0; 0.5;-1.5;-1.0; 1.5;-1.5;2.0
     3                 -1.5;-0.5;1.0; -0.5;-0.5;3.0; 0.5;-0.5;0.0;  1.5;-0.5;-1.0
     4                 -1.5;0.5;4.0;  -0.5;0.5;0.0;  0.5;0.5;3.0;   1.5;0.5;4.0
     5                 -1.5;1.5;-2.0; -0.5;1.5;-2.0; 0.5;1.5;0.0;   1.5;1.5;-1.0
     6                 |]      
     7        //[3,4,3],组织如上面,三列四行,每行三点就是Vector3
     8         //数组格式[v][u][z] 则GL.Map2(MapTarget.Map2Vertexz,0.,1.,z,u,0.,1.,u*z,v,vvs)
     9         GL.Map2(MapTarget.Map2Vertex3,0.,1.,3,4,0.,1.,12,3,vvs)
    10         GL.MapGrid2(10,0.,1.,5,0.,1.)
    11         GL.EvalMesh2(MeshMode2.Line,0,10,0,5)    
    View Code

    这个贝塞尔曲面表示在u方向4个控制点,z方向有3个控制点,u的精细度是10,意思是u方向的线都是由10个点组成,v的精细度是5,精细度越高,线越光滑。效果图如下:

    最后向法国工程师皮埃尔·贝塞尔致敬。

  • 相关阅读:
    HDU ACM 1392 Surround the Trees-&gt;凸包
    JMeter使用记录1 -- JDBC測试
    Html学习笔记4
    c++使用mysql的api连接相关问题
    [Angularjs]ng-select和ng-options
    mysql之字符串操作
    mysql之日期函数
    [sharepoint]Rest api相关知识(转)
    [工具类]泛型集合转换为DataTable
    C#修改文件或文件夹的权限,为指定用户、用户组添加完全控制权限
  • 原文地址:https://www.cnblogs.com/zhouxin/p/3436740.html
Copyright © 2011-2022 走看看