作业有道题,让算出三个贝塞尔曲线的点,并画出草图。其实题目本身没有什么难度,就是带进去算一下就能出来了。不过还是想直接把他画出来。
如果发现视频不是曲线的绘制的话(貌似在手机上就不是,电脑上是。两个视频时分p的),可以直接到 原视频 看。
先根据老师PPT里面的公式,写出对应的函数。
贝塞尔曲线的函数
public Vector2 Bezier (float t, int n, List<Vector2> cPoints) {
Vector2 res = new Vector2 ();
for (int i = 0; i <= n; i++) {
float temp = BaseBezier (t, i, n);
res.x += cPoints[i].x * temp;
res.y += cPoints[i].y * temp;
}
return res;
}
public float BaseBezier (float t, int i, int n) {
float res = Fa (n) * Mathf.Pow (t, i) * Mathf.Pow (1 - t, n - i) / (Fa (i) * Fa (n - i));
return res;
}
以及根据需要写了一个求阶乘的函数
// Factorial 阶乘
public int Fa (int i) {
int res = 1;
for (int j = i; j > 1; j--) {
res *= j;
}
return res;
}
大体的思想就是把曲线分成很多个小片段,然后用直线把这些线段连起来。
具体一点就是先通过划分多少份来计算t值,然后带进去公式算出来的点存到List里面,全部算完之后再通过Gizmos.DrawLine画出来。
直接放出完整的代码(还有B样条曲线的也加上了,不过B样条画出来总感觉怪怪的)
using System.Collections.Generic;
using UnityEngine;
public class DrawBezierAndB : MonoBehaviour {
public bool autoUpdate = true; // 修改值时更新曲线
[Tooltip ("次数")]
public int n; // 次数
public enum Type {
Bezier,
BSpline
}
public Type drawType = Type.Bezier; // 绘制曲线的方式
public Color curveColor; // 曲线的颜色
public List<Vector2> points = new List<Vector2> (); // 用于绘制的点
[Range (1, 50)]
public int part = 20; // 分段
public List<Vector2> controlPoints = new List<Vector2> (); // 控制点
public void Draw () {
switch (drawType) {
case Type.Bezier:
CaculateBezier ();
break;
case Type.BSpline:
CaculateBSpline ();
break;
default:
break;
}
}
private void OnDrawGizmos () {
// 画控制点
Gizmos.color = Color.red;
if (controlPoints.Count > 0) {
for (int i = 0; i < controlPoints.Count; i++) {
Gizmos.DrawSphere (controlPoints[i], 0.1f);
}
}
if (controlPoints.Count > 0) {
for (int i = 0; i < controlPoints.Count - 1; i++) {
Gizmos.DrawLine (controlPoints[i], controlPoints[i + 1]);
}
}
// 画曲线
Gizmos.color = curveColor;
if (points.Count > 0) {
for (int i = 0; i < points.Count - 1; i++) {
Gizmos.DrawLine (points[i], points[i + 1]);
}
}
}
public void CaculateBSpline () {
points.Clear ();
for (int i = 0; i <= part; i++) {
points.Add (BSpline (i / (float) part, n, controlPoints));
}
}
public void CaculateBezier () {
points.Clear ();
for (int i = 0; i <= part; i++) {
points.Add (Bezier (i / (float) part, n, controlPoints));
}
}
// 贝塞尔曲线
public Vector2 Bezier (float t, int n, List<Vector2> cPoints) {
Vector2 res = new Vector2 ();
for (int i = 0; i <= n; i++) {
float temp = BaseBezier (t, i, n);
res.x += cPoints[i].x * temp;
res.y += cPoints[i].y * temp;
}
return res;
}
// B 样条曲线
public Vector2 BSpline (float t, int n, List<Vector2> cPoints) {
Vector2 res = new Vector2 ();
for (int l = 0; l <= n; l++) {
float temp = BaseBSpline (t, l, n);
res.x += cPoints[l].x * temp;
res.y += cPoints[l].y * temp;
}
return res;
}
// B样条基函数
public float BaseBSpline (float t, int l, int n) {
float res = 0;
for (int j = 0; j <= n - l; j++) {
res += Mathf.Pow (-1, j) *
Mathf.Pow (t + n - l - j, n) *
Fa (n + 1) / (Fa (j) * Fa (n + 1 - j));
}
return res / Fa (n);
}
// 贝塞尔基函数
public float BaseBezier (float t, int i, int n) {
float res = Fa (n) * Mathf.Pow (t, i) * Mathf.Pow (1 - t, n - i) / (Fa (i) * Fa (n - i));
return res;
}
// Factorial 阶乘
public int Fa (int i) {
int res = 1;
for (int j = i; j > 1; j--) {
res *= j;
}
return res;
}
}
为了方便在编辑器里面修改控制点,对编辑器进行了一些扩展,不需要播放就直接能画了。
using UnityEditor;
using UnityEngine;
[CustomEditor (typeof (DrawBezierAndB))]
public class DrawBezierAndBEditor : Editor {
DrawBezierAndB drawBAB;
void OnEnable () {
//获取当前编辑自定义Inspector的对象
drawBAB = (DrawBezierAndB) target;
}
public override void OnInspectorGUI () {
// 如果面板上有值改变返回值true
if (DrawDefaultInspector ()) {
// 确保次数要小于控制点的个数
if (drawBAB.n > drawBAB.controlPoints.Count - 1) {
drawBAB.n = drawBAB.controlPoints.Count - 1;
Debug.LogWarning ("次数要小于控制点的个数");
}
// 如果是改变值时也生成就生成
if (drawBAB.autoUpdate) {
drawBAB.Draw ();
}
}
// 点下Generate按钮就生成
if (GUILayout.Button ("Generate")) {
drawBAB.Draw ();
}
}
}