1、
关于贝塞尔曲线曲线我们再前面的文章提到过《Unity 教程之-在Unity3d中使用贝塞尔曲线》,那么本篇文章我们来深入学习下,并自定义实现贝塞尔曲线编辑器,贝塞尔曲线是最基本的曲线,一般用在计算机 图形学和 图像处理。贝塞尔曲线可以用来创建平滑的曲线的道路、 弯曲的路径就像 祖玛游戏、 弯曲型的河流等。看下效果图 !
一条贝塞尔曲线是由一组定义的控制点 P0到 Pn,在 n 调用它的顺序 (n = 1 为线性,2 为二次,等.)。第一个和最后一个控制点总是具有终结点的曲线;然而,中间两个控制点 (如果有的话) 一般不会位于曲线上 。
贝塞尔曲线包含两个控制点即 n = 2 称为线性的贝塞尔曲线
贝塞尔曲线包含三个控制点即 n = 3 称为二次贝塞尔曲线
贝塞尔曲线包含四个控制点即 n = 4,所以称为三次贝塞尔曲线。
贝塞尔曲线返回点的贝塞尔函数,使用线性插值的概念作为基础。所以,让我们了解什么首先是线性插值。
两个点之间的线性插值的点获取那两个点之间,0 <= t <= 1,像 Mathf.Lerp 。
插值点,与 P 公式P0和 P1可以写成,
P = P0+ t (P1 – P0),0 <= t <= 1
在这里,为得到插值的点我们添加 tth指向 P 的分数与这两个之间的距离0.所以,
For T = 0,P = P0.
For T = 1,P = P1.
For T = 0.5,P = P0和 P1间的点.
线性的贝塞尔曲线:
线性的贝塞尔曲线有两个控制点。为给出了两个点 P0和 P1一个线性的贝塞尔曲线是只是这两个点之间的直线。曲线是相当于线性插值给出,
B(t) = P0+ t (P1 – P0) = (1-t) P0 + tP1 ,0 <= t <= 1
线性贝塞尔曲线如何计算出来的是如下所示:
二次贝塞尔曲线:
二次贝塞尔曲线具有三个控制点。二次贝塞尔曲线是点对点的两个线性贝塞尔曲线的线性插值。为给出了三个点 P0、P1和 P2一条二次贝塞尔曲线,其实是两条线性的贝塞尔曲线,线性贝塞尔曲线的 P0和 P1和 线性贝塞尔曲线P1和 P2. 所以,给出二次贝塞尔曲线 :
B(t) = (1-t) BP0P1(t) + t BP1P2(t),0 <= t <= 1
B(t) = (1-t) [(1-t) P0 + tP1] + t [(1-t) P1+ tP2],0 <= t <= 1
通过重新排列上述方程,
B(t) = (1-t)2P0+ 2 (1-t) tP1 + t2P2, 0 <= t <= 1
二次贝塞尔曲线动画计算如下所示:
三次贝塞尔曲线:
三次方贝塞尔曲线具有四个控制点。二次贝塞尔曲线是 点对点的两条二次贝塞尔曲线的线性插值。对于给出的四个点 P0、P1、P2和 P3三次方贝塞尔曲线,是二次贝塞尔曲线P0、P1和 P2和 二次贝塞尔曲线P1、P2和 P3 得到的 线性插值 .所以,给出三次方贝塞尔曲线
B(t) = (1-t) BP0,P1,P2(t) + t BP1,P2,P3(t),0 <= t <= 1
B(t) = (1-t) [(1-t)2P0+ 2 (1-t) tP1 + t2P2] + t [(1-t)2P1+ 2 (1-t) tP2 + t2P3],0 <= t <= 1
通过重新排列上述方程中,
B(t) = (1-t)3P0 + 3(1-t)2tP1+ 3 (1-t) t2P2 + t3P3 0 <= t <= 1
三次贝塞尔曲线计算如下所示:
所以,一般可以作为点对点的线性插值获得从两个相应的贝赛尔曲线的程度 n-1 的两个点定义程度 n 的贝塞尔曲线(就是高级的是两个低一级的线性插值)。
在大多数应用程序使用两种二次或三次方贝塞尔函数。然而,你总可以使用更高程度贝塞尔函数绘制更复杂的曲线,但较高程度贝塞尔函数的计算是比较复杂和增加处理开销。所以,而不是使用更高的学位贝塞尔函数绘制更复杂的曲线,你可以多次使用两种二次或三次方贝塞尔函数。在这里,我创建了一个演示和绘制的∞形曲线,使用三次方贝塞尔函数中循环,如下所示。
若要创建一条曲线,如上所示,请创建场景,如下所示:
现在,将Bezier.cs脚本附加到 Bezier Manager
Bezier.cs:
using UnityEngine;
using System.Collections.Generic;
[RequireComponent(typeof(LineRenderer))]
public class Bezier : MonoBehaviour
{
public Transform[] controlPoints;
public LineRenderer lineRenderer;
private int curveCount = 0;
private int layerOrder = 0;
private int SEGMENT_COUNT = 50;
void Start()
{
if (!lineRenderer)
{
lineRenderer = GetComponent();
}
lineRenderer.sortingLayerID = layerOrder;
curveCount = (int)controlPoints.Length / 3;
}
void Update()
{
DrawCurve();
}
void DrawCurve()
{
for (int j = 0; j
{
for (int i = 1; i <= SEGMENT_COUNT; i++)
{
float t = i / (float)SEGMENT_COUNT;
int nodeIndex = j * 3;
Vector3 pixel = CalculateCubicBezierPoint(t, controlPoints [nodeIndex].position, controlPoints [nodeIndex + 1].position, controlPoints [nodeIndex + 2].position, controlPoints [nodeIndex + 3].position);
lineRenderer.SetVertexCount(((j * SEGMENT_COUNT) + i));
lineRenderer.SetPosition((j * SEGMENT_COUNT) + (i - 1), pixel);
}
}
}
Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;
Vector3 p = uuu * p0;
p += 3 * uu * t * p1;
p += 3 * u * tt * p2;
p += ttt * p3;
return p;
}
}
在这里,CalculateCubicBezierPoint 函数是 Cubiz 贝塞尔函数,我已解释了上面执行。DrawCurve 函数绘制两条 三次方贝塞尔曲线。
Between P0, P0- control Point1, P1- control Point1 and P1.
Between P1, P1- control Point1, P0- control Point2 and P0.
任何控制点 可处理其相应的曲线的曲率。你可以在任何时间改变曲线,通过拖动任意控制点,如下所示:
0
好了本篇unity3d教程到此结束,下篇我们再会!
资源地址: http://cg.silucg.com/dongman/unity3d/7970.html
http://cg.silucg.com/bim/revit/8208.html
http://cg.silucg.com/bim/revit/8207.html
http://cg.silucg.com/bim/revit/8206.html
2、
创建一个U3D的工程,创建一个新游戏对象,绑定LineRenderer组件。
Bezier.cs 这里是贝塞尔曲线的公式C#版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
using UnityEngine;
[System.Serializable]
public class Bezier : System.Object
{
public Vector3 p0;
public Vector3 p1;
public Vector3 p2;
public Vector3 p3;
public float ti = 0f;
private Vector3 b0 = Vector3.zero;
private Vector3 b1 = Vector3.zero;
private Vector3 b2 = Vector3.zero;
private Vector3 b3 = Vector3.zero;
private float Ax;
private float Ay;
private float Az;
private float Bx;
private float By;
private float Bz;
private float Cx;
private float Cy;
private float Cz;
// Init function v0 = 1st point, v1 = handle of the 1st point , v2 = handle of the 2nd point, v3 = 2nd point
// handle1 = v0 + v1
// handle2 = v3 + v2
public Bezier( Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3 )
{
this.p0 = v0;
this.p1 = v1;
this.p2 = v2;
this.p3 = v3;
}
// 0.0 >= t <= 1.0
public Vector3 GetPointAtTime( float t )
{
this.CheckConstant();
float t2 = t * t;
float t3 = t * t * t;
float x = this.Ax * t3 + this.Bx * t2 + this.Cx * t + p0.x;
float y = this.Ay * t3 + this.By * t2 + this.Cy * t + p0.y;
float z = this.Az * t3 + this.Bz * t2 + this.Cz * t + p0.z;
return new Vector3( x, y, z );
}
private void SetConstant()
{
this.Cx = 3f * ( ( this.p0.x + this.p1.x ) - this.p0.x );
this.Bx = 3f * ( ( this.p3.x + this.p2.x ) - ( this.p0.x + this.p1.x ) ) - this.Cx;
this.Ax = this.p3.x - this.p0.x - this.Cx - this.Bx;
this.Cy = 3f * ( ( this.p0.y + this.p1.y ) - this.p0.y );
this.By = 3f * ( ( this.p3.y + this.p2.y ) - ( this.p0.y + this.p1.y ) ) - this.Cy;
this.Ay = this.p3.y - this.p0.y - this.Cy - this.By;
this.Cz = 3f * ( ( this.p0.z + this.p1.z ) - this.p0.z );
this.Bz = 3f * ( ( this.p3.z + this.p2.z ) - ( this.p0.z + this.p1.z ) ) - this.Cz;
this.Az = this.p3.z - this.p0.z - this.Cz - this.Bz;
}
// Check if p0, p1, p2 or p3 have changed
private void CheckConstant()
{
if( this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3 )
{
this.SetConstant();
this.b0 = this.p0;
this.b1 = this.p1;
this.b2 = this.p2;
this.b3 = this.p3;
}
}
}
|
MyBezier.cs 把它直接挂在摄像机上 ,控制拖动条来控制贝塞尔曲线、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
using UnityEngine;
public class MyBezier : MonoBehaviour
{
//贝塞尔曲线算法类
public Bezier myBezier;
//曲线的对象
public GameObject Yellowline;
//曲线对象的曲线组件
private LineRenderer YellowlineRenderer;
//拖动条用来控制贝塞尔曲线的两个点
public float hSliderValue0;
public float hSliderValue1;
void Start()
{
//得到曲线组件
YellowlineRenderer = Yellowline.GetComponent<LineRenderer>();
//为了让曲线更加美观,设置曲线由100个点来组成
YellowlineRenderer.SetVertexCount(100);
}
void OnGUI()
{
//拖动条得出 -5.0 - 5.0之间的一个数值
hSliderValue0 = GUI.HorizontalSlider(new Rect(25, 25, 100, 30), hSliderValue0, -5.0F, 5.0F);
hSliderValue1 = GUI.HorizontalSlider(new Rect(25, 70, 100, 30), hSliderValue1, -5.0F, 5.0F);
}
void Update()
{
//在这里来计算贝塞尔曲线
//四个参数 表示当前贝塞尔曲线上的4个点 第一个点和第四个点
//我们是不需要移动的,中间的两个点是由拖动条来控制的。
myBezier = new Bezier( new Vector3( -5f, 0f, 0f ), new Vector3( hSliderValue1, hSliderValue0 , 0f ), new Vector3(hSliderValue1, hSliderValue0, 0f ), new Vector3( 5f, 0f, 0f ) );
//循环100遍来绘制贝塞尔曲线每个线段
for(int i =1; i <= 100; i++)
{
//参数的取值范围 0 - 1 返回曲线没一点的位置
//为了精确这里使用i * 0.01 得到当前点的坐标
Vector3 vec = myBezier.GetPointAtTime( (float)(i *0.01) );
//把每条线段绘制出来 完成白塞尔曲线的绘制
YellowlineRenderer.SetPosition(i -1,vec);
}
}
}
|
OK 这里贝塞尔曲线的原理就已经完毕。下面我们学习在NGUI中如何使用贝塞尔曲线。刚刚我们说过贝塞尔曲线是由2个固定点 加两个动态点来完成的,其实我们在开发中往往只需要3个点。1 起点 2 中间点 3 结束点 拖动这三个点都可以重新计算曲线的轨迹这样才比较完美。如下图所示,这三个点都是可以任意拖动的,拖动结束后,黑色的线为用户拖拽点连接的直角线段,我们 根据这三个点组成的直角线段计算它们之间的贝塞尔曲线,也就是图中黄色的线段。
简单的进行拖拽一下,是不是感觉贝塞尔曲线很酷炫呢?哇咔咔。
我们来看看代码实现的部分,其实原理和上面完全一样。
BallMove.cs绑定在这三个可以拖拽的点上,让拖动小球后小球可跟随手指移动。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
using UnityEngine;
using System.Collections;
public class BallMove : MonoBehaviour
{
void OnDrag (Vector2 delta)
{
float movex = transform.localPosition.x + (delta.x / 3);
float movey = transform.localPosition.y + (delta.y / 3);
//避免越界操作,这里可以进行一些判断
transform.localPosition = new Vector3(movex,movey ,transform.localPosition.z);
}
}
|
如此一来触摸小球后,小球将跟随用户手指移动。下面我们将监听用户触摸小球后的坐标来计算它们三点之间的贝塞尔曲线。
BallInit.cs挂在摄像机上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
using UnityEngine;
using System.Collections;
public class BallInit : MonoBehaviour {
//黑色直角线段
LineRenderer lineRenderer0;
LineRenderer lineRenderer1;
//贝塞尔曲线
LineRenderer BezierRenderer;
//三个小球触摸对象
public GameObject mark0;
public GameObject mark1;
public GameObject mark2;
//算法公式类
private Bezier myBezier;
void Start ()
{
//分别得到黑色直角线段 与黄色贝塞尔曲线的 线段组件
lineRenderer0 = GameObject.Find("line0").GetComponent<LineRenderer>();
lineRenderer1 = GameObject.Find("line1").GetComponent<LineRenderer>();
BezierRenderer = GameObject.Find("Bezier").GetComponent<LineRenderer>();
//黑色直角是有两个线段组成
lineRenderer0.SetVertexCount(2);
lineRenderer1.SetVertexCount(2);
//为了让贝塞尔曲线细致一些 设置它有100个点组成
BezierRenderer.SetVertexCount(100);
}
void Update ()
{
//mark0 表示中间的小球
//mark1 表示右边的小球
//mark2 表示左边的小球
//中间的标志点分别减去左右两边的标志点,计算出曲线的X Y 的点
float y = (mark0.transform.position.y - mark2.transform.position.y) ;
float x = (mark0.transform.position.x - mark2.transform.position.x) ;
//因为我们是通过3个点来确定贝塞尔曲线, 所以参数3 设置为0 即可。
//这样参数1 表示起点 参数2表示中间点 参数3 忽略 参数4 表示结束点
myBezier = new Bezier( mark2.transform.position, new Vector3(x,y,0f), new Vector3(0f,0f,0f), mark1.transform.position );
//绘制贝塞尔曲线
for(int i =1; i <= 100; i++)
{
Vector3 vec = myBezier.GetPointAtTime( (float)(i * 0.01) );
BezierRenderer.SetPosition(i -1,vec);
}
//绘制直角黑色标志线段
lineRenderer0.SetPosition(0,mark0.transform.position);
lineRenderer0.SetPosition(1,mark2.transform.position);
lineRenderer1.SetPosition(0,mark0.transform.position);
lineRenderer1.SetPosition(1,mark1.transform.position);
}
}
|