├── README.md
├── c++
├── CatmullRom.cpp
├── CatmullRom.h
├── Vec3.cpp
├── Vec3.h
└── test.cpp
├── curve1.PNG
├── curve2.PNG
├── js
├── CatmullRom.js
├── Vec3.js
└── test.js
├── lua
├── CatmullRom.lua
├── Vec3.lua
└── test.lua
└── matlab
├── CatmullRom.m
├── Vec3.m
└── test.m
/README.md:
--------------------------------------------------------------------------------
1 | # AverageCurve
2 |
3 | #### 均速曲线路径
4 |
5 | 由于贝塞尔曲线和基本样条曲线插值形成的曲线路径不是均速的,在某些场景可能会影响效果。比如说鱼游动,一般的曲线插值会忽快忽慢,像下面这样子:
6 |
7 | 
8 |
9 | 变速的曲线不利于精确控制速度,所以需要做额外的计算,以符合要求。
10 |
11 |
12 |
13 | 在插值之前,额外计算到指定长度的t变量,实现均速曲线,处理之后的效果图:
14 |
15 | 
16 |
17 | #### 均速缓动曲线
18 |
19 | 前言:
20 |
21 | 物体的运动有时候需要由快变慢或者慢慢加快 这就需要叠加一个缓动曲线,游戏引擎预设的一些曲线都是初等函数叠加形成的,不能直观的感受到缓动曲线的变化趋势。
22 | 因此需要一个能够直接想象的到,并且方便自定义曲线的方式。
23 |
24 | 实现:
25 |
26 | > let rom = new CatmullRom(pts,1)
27 | > rom.lerp(t).y
28 |
29 |
30 |
31 |
32 | 接口 |
33 | 参数 |
34 | 说明 |
35 |
36 |
37 | catmullRom=new CatmullRom(pts,usage) |
38 | pts |
39 | 曲线经过的点数组 |
40 |
41 |
42 | usage |
43 | 0:均速路径曲线 1:均速缓动曲线 |
44 |
45 |
46 | catmullRom.lerp(t) |
47 | t |
48 | 插件比例,范围0-1,0:起点, 1:终点 |
49 |
50 |
51 |
52 |
53 | matlab版本可以直观的观察曲线的轨迹,可以用来编辑曲线。
54 |
--------------------------------------------------------------------------------
/c++/CatmullRom.cpp:
--------------------------------------------------------------------------------
1 | #include"CatmullRom.h"
2 |
3 | CatmullRom::CatmullRom(const vector& pts, int usage)
4 | {
5 | points = pts;
6 |
7 | int len = pts.size();
8 | bClose = pts[0] == pts[len - 1];
9 |
10 | float sumDistance = 0;
11 | Vec3 p0, p1, p2, p3;
12 | for (int i = 0; i < len - 1; i++){
13 | if (i == 0) {
14 | p0 = bClose ? pts[len - 2] : pts[0];
15 | }
16 | else {
17 | p0 = pts[i - 1];
18 | }
19 |
20 | p1 = pts[i];
21 | p2 = pts[i + 1];
22 | if (i + 1 == len - 1) {
23 | p3 = bClose ? pts[1] : pts[len - 1];
24 | }
25 | else {
26 | p3 = pts[i + 2];
27 | }
28 |
29 | Funcs fs = CatmullRom::curve(p0, p1, p2, p3);
30 | funcs.push_back(fs);
31 | funcs[i].f2 = usage == 0 ? fs.f2 : fs.f3;
32 |
33 | sumDistance += CatmullRom::gaussLegendre(funcs[i].f2, 0, 1);
34 | distances.push_back(sumDistance);
35 | }
36 | }
37 |
38 | CatmullRom::~CatmullRom()
39 | {
40 | }
41 |
42 |
43 | Vec3 CatmullRom::lerp(float t){
44 | int len = points.size();
45 | //第一个和最后一个点直接返回
46 | if (t == 0) {
47 | return points[0];
48 | }
49 | else if (t == 1) {
50 | return points[len - 1];
51 | }
52 | //平均距离
53 | float averDis = t * distances[len - 2];
54 | float index = 0, beyond = 0, percent = 0;
55 | for (int i = 0; i < len - 1; i++) {
56 | if (averDis < distances[i]) {
57 | float preDis = i == 0 ? 0 : distances[i - 1];
58 | index = i;
59 | beyond = averDis - preDis;
60 | percent = beyond / (distances[i] - preDis);
61 | break;
62 | }
63 | }
64 | //牛顿切线法求根
65 | float a = percent, b;
66 | //最多迭代6次
67 | for (int i = 0; i < 6; i++) {
68 | float actualLen = CatmullRom::gaussLegendre(funcs[index].f2, 0, a);
69 | b = a - (actualLen - beyond) / funcs[index].f2(a);
70 | if (abs(a - b) < 0.0001) {
71 | break;
72 | }
73 | a = b;
74 | }
75 | percent = b;
76 | return funcs[index].f1(percent);
77 | }
78 | CatmullRom::Funcs CatmullRom::curve(Vec3 p0, Vec3 p1, Vec3 p2, Vec3 p3){
79 | // 弹性
80 | float s = 0.5;
81 | //计算三次样条线函数系数
82 | Vec3 b1 = p0*(-s) + p1*(2 - s) + p2*(s - 2) + p3*s;
83 | Vec3 b2 = p0 * 2 * s + p1*(s - 3) + p2*(3 - 2 * s) + p3*(-s);
84 | Vec3 b3 = p0*(-s) + p2*s;
85 | Vec3 b4 = p1;
86 |
87 | // 函数曲线
88 | auto f1 = [=](float x) -> Vec3 {
89 | return b1*pow(x , 3) + b2*pow(x , 2) + b3*x + b4;
90 | };
91 | // 曲线长度变化率, 用于匀速曲线运动
92 | auto f2 = [=](float x) -> float {
93 | Vec3 der = b1 * 3 * pow(x, 2) + b2*x * 2 + b3;
94 | return sqrt(pow(der.x, 2) + pow(der.y, 2) + pow(der.z, 2));
95 | };
96 | // 曲线x变化率, 用于自定义缓动曲线
97 | auto f3 = [=](float x) -> float {
98 | Vec3 der = b1 * 3 * pow(x, 2) + b2*x * 2 + b3;
99 | return der.x;
100 | };
101 | return Funcs(f1, f2, f3);
102 | }
103 |
104 | float CatmullRom::gaussLegendre(const function& func, float a, float b){
105 | //3次系数
106 | static std::map GauFactor{ { 0.7745966692, 0.555555556 }, { 0, 0.8888888889 } };
107 | //5次系数
108 | //static std::map GauFactor{ { 0.9061798459, 0.2369268851 }, { 0.5384693101, 0.4786286705 }, { 0, 0.5688888889 } };
109 | float GauSum = 0;
110 |
111 | for (auto &kv : GauFactor) {
112 | float key = kv.first;
113 | float v = kv.second;
114 | float t = ((b - a) * key + a + b) / 2;
115 | float der = func(t);
116 | GauSum = GauSum + der * v;
117 | if (key > 0) {
118 | t = ((b - a) * (-key) + a + b) / 2;
119 | der = func(t);
120 | GauSum = GauSum + der * v;
121 | }
122 | }
123 | return GauSum * (b - a) / 2;
124 | }
--------------------------------------------------------------------------------
/c++/CatmullRom.h:
--------------------------------------------------------------------------------
1 | #ifndef __CatmullRom__
2 | #define __CatmullRom__
3 |
4 | #include"Vec3.h"
5 | #include
6 | #include
7 | #include