用二次贝塞尔曲线绘制随机路径
Drawing random paths with quadratic bezier curves
我想绘制我的对象将遵循的平滑*和随机路径,我决定使用二次贝塞尔曲线(但我对其他想法持开放态度)。
我的代码以随机但不平滑*的方式移动我的对象。
预览:https://youtu.be/Eg9PEKuH4zA
我的问题是:如何让方向变化更顺畅?我应该完全放弃我的 Bezier 解决方案还是有什么方法可以改进我的代码以实现我想要的?
*平滑 == 没有突然的方向变化
我的代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Lights : MonoBehaviour {
public float paintHeight = -90.0f;
public static int NUMBER_OF_LIGHTS = 3;
private static int BEZIER_PATH_POINTS = 100;
private float GOLDEN_COLOR_RATIO = 0.618033988749895f;
private Light[] lights = new Light[NUMBER_OF_LIGHTS];
Vector3 RandomPoint() {
float obj_width = gameObject.GetComponent<RectTransform>().rect.width;
float screenX = Random.Range(-obj_width / 2, obj_width / 2);
float obj_height = gameObject.GetComponent<RectTransform>().rect.height;
float screenY = Random.Range(-obj_height / 2, obj_height / 2);
return new Vector3(screenX, screenY, -paintHeight);
}
Vector3 QuadraticBezierPoint(Vector3 startPoint, Vector3 endPoint, Vector3 vertexPoint, float t) {
/*
* vertex
* /╲
* / ╲
* / p ╲
* / . . ╲
* / . · ╲
* /· · ╲
* start · end
*
* 0 < t < 1
*
* B(t) = (1 - t)^2 * P0 + 2 * (1-t) * t * P1 + t^2 * P2
*
*/
return Mathf.Pow((1 - t), 2) * startPoint + 2 * (1 - t) * t * vertexPoint + Mathf.Pow(t, 2) * endPoint;
}
Color RandomColor() {
float h = Random.Range(0.0f, 1.0f) + GOLDEN_COLOR_RATIO;
h %= 1;
return Color.HSVToRGB(h, 0.99f, 0.99f);
}
void Start() {
for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
GameObject light_obj = new GameObject();
Light light = light_obj.AddComponent<Light>();
light.type = LightType.Point;
light.range = 10.0f;
light.intensity = 3.5f;
light.renderMode = LightRenderMode.ForcePixel;
light.name = "Light" + i;
light.transform.parent = gameObject.transform;
lights[i] = light;
}
StartCoroutine("Move");
}
IEnumerator Move () {
Dictionary<string, Vector3>[] light_points = new Dictionary<string, Vector3>[NUMBER_OF_LIGHTS];
Dictionary<string, Color>[] light_colors = new Dictionary<string, Color>[NUMBER_OF_LIGHTS];
for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
light_points[i] = new Dictionary<string, Vector3>();
light_colors[i] = new Dictionary<string, Color>();
//light_points[i]["startPoint"] = RandomPoint();
//light_points[i]["vertexPoint"] = RandomPoint();
light_points[i]["endPoint"] = RandomPoint();
light_colors[i]["nextColor"] = RandomColor();
}
while(true) {
for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
light_points[i]["startPoint"] = light_points[i]["endPoint"];
light_points[i]["vertexPoint"] = RandomPoint();
light_points[i]["endPoint"] = RandomPoint();
light_colors[i]["currentColor"] = light_colors[i]["nextColor"];
light_colors[i]["nextColor"] = RandomColor();
}
for (int i = 0; i < BEZIER_PATH_POINTS; i++) {
float percent = (float)i / BEZIER_PATH_POINTS;
for (int j = 0; j < NUMBER_OF_LIGHTS; j++) {
lights[j].transform.localPosition = QuadraticBezierPoint(
light_points[j]["startPoint"],
light_points[j]["endPoint"],
light_points[j]["vertexPoint"],
percent
);
lights[j].color = Color.Lerp(light_colors[j]["currentColor"], light_colors[j]["nextColor"], percent);
}
yield return new WaitForSeconds(0.02f);
}
}
}
}
如果你有一条从 A 到 B 的曲线,其中有一些中间点,你必须相应地 select 一个 C,这样 B 处的角度 (A,B,C) 不会太小或者太大了。
此外,您还必须以适当的方式 select 中间点,参见例如https://www.particleincell.com/2012/bezier-splines/ ,这样方向的变化就不会太大。
您可以一直使用 NURBS 或其他一些样条曲线,但根据您发布的内容,可能有更简单、等效的方法来获得平滑曲线。
您正在创建的每条曲线在您的代码开始、顶点和结束中都有 3 个点。让我们称它们为 S、V 和 E。此外,让我们通过时间来观察它们,因此 {S0,V0,E0}
形成第一条曲线,{S1,V1,E1}
形成第二条曲线,依此类推。
现在您确保 S1=E0
、S2=E1
等。这意味着您的灯光不会随机跳跃,但在切换贝塞尔曲线时动作可能会突然。
为保证动作流畅,尽量保证S1 = E0 = 0.5 * (V0+V1)
。这实际上意味着您随机生成 S0 和 V0。然后,对于每条新曲线,您都会生成一个新 V,旧 V 和新 V 之间的中点就是曲线转折点。
这等同于具有均匀度数为 2 的 B 样条曲线。
下面是一些可能具有说明性的示例代码。
// initialize with randomness
// we need a previous v and a current v for the first iteration
// So, the first start point will be halfway between these two points
prev_v = random_point()
v = random_point()
while( true )
{
next_v = random_point()
s = 0.5 * (prev_v + v) // s is halfway between prev_v and v
e = 0.5 * (v + next_v) // e is halfway between v and next_v
yield_movement_bezier( s, v, e )
// prep for next iteration
prev_v = v
v = next_v
}
我想绘制我的对象将遵循的平滑*和随机路径,我决定使用二次贝塞尔曲线(但我对其他想法持开放态度)。
我的代码以随机但不平滑*的方式移动我的对象。
预览:https://youtu.be/Eg9PEKuH4zA
我的问题是:如何让方向变化更顺畅?我应该完全放弃我的 Bezier 解决方案还是有什么方法可以改进我的代码以实现我想要的?
*平滑 == 没有突然的方向变化
我的代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Lights : MonoBehaviour {
public float paintHeight = -90.0f;
public static int NUMBER_OF_LIGHTS = 3;
private static int BEZIER_PATH_POINTS = 100;
private float GOLDEN_COLOR_RATIO = 0.618033988749895f;
private Light[] lights = new Light[NUMBER_OF_LIGHTS];
Vector3 RandomPoint() {
float obj_width = gameObject.GetComponent<RectTransform>().rect.width;
float screenX = Random.Range(-obj_width / 2, obj_width / 2);
float obj_height = gameObject.GetComponent<RectTransform>().rect.height;
float screenY = Random.Range(-obj_height / 2, obj_height / 2);
return new Vector3(screenX, screenY, -paintHeight);
}
Vector3 QuadraticBezierPoint(Vector3 startPoint, Vector3 endPoint, Vector3 vertexPoint, float t) {
/*
* vertex
* /╲
* / ╲
* / p ╲
* / . . ╲
* / . · ╲
* /· · ╲
* start · end
*
* 0 < t < 1
*
* B(t) = (1 - t)^2 * P0 + 2 * (1-t) * t * P1 + t^2 * P2
*
*/
return Mathf.Pow((1 - t), 2) * startPoint + 2 * (1 - t) * t * vertexPoint + Mathf.Pow(t, 2) * endPoint;
}
Color RandomColor() {
float h = Random.Range(0.0f, 1.0f) + GOLDEN_COLOR_RATIO;
h %= 1;
return Color.HSVToRGB(h, 0.99f, 0.99f);
}
void Start() {
for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
GameObject light_obj = new GameObject();
Light light = light_obj.AddComponent<Light>();
light.type = LightType.Point;
light.range = 10.0f;
light.intensity = 3.5f;
light.renderMode = LightRenderMode.ForcePixel;
light.name = "Light" + i;
light.transform.parent = gameObject.transform;
lights[i] = light;
}
StartCoroutine("Move");
}
IEnumerator Move () {
Dictionary<string, Vector3>[] light_points = new Dictionary<string, Vector3>[NUMBER_OF_LIGHTS];
Dictionary<string, Color>[] light_colors = new Dictionary<string, Color>[NUMBER_OF_LIGHTS];
for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
light_points[i] = new Dictionary<string, Vector3>();
light_colors[i] = new Dictionary<string, Color>();
//light_points[i]["startPoint"] = RandomPoint();
//light_points[i]["vertexPoint"] = RandomPoint();
light_points[i]["endPoint"] = RandomPoint();
light_colors[i]["nextColor"] = RandomColor();
}
while(true) {
for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
light_points[i]["startPoint"] = light_points[i]["endPoint"];
light_points[i]["vertexPoint"] = RandomPoint();
light_points[i]["endPoint"] = RandomPoint();
light_colors[i]["currentColor"] = light_colors[i]["nextColor"];
light_colors[i]["nextColor"] = RandomColor();
}
for (int i = 0; i < BEZIER_PATH_POINTS; i++) {
float percent = (float)i / BEZIER_PATH_POINTS;
for (int j = 0; j < NUMBER_OF_LIGHTS; j++) {
lights[j].transform.localPosition = QuadraticBezierPoint(
light_points[j]["startPoint"],
light_points[j]["endPoint"],
light_points[j]["vertexPoint"],
percent
);
lights[j].color = Color.Lerp(light_colors[j]["currentColor"], light_colors[j]["nextColor"], percent);
}
yield return new WaitForSeconds(0.02f);
}
}
}
}
如果你有一条从 A 到 B 的曲线,其中有一些中间点,你必须相应地 select 一个 C,这样 B 处的角度 (A,B,C) 不会太小或者太大了。
此外,您还必须以适当的方式 select 中间点,参见例如https://www.particleincell.com/2012/bezier-splines/ ,这样方向的变化就不会太大。
您可以一直使用 NURBS 或其他一些样条曲线,但根据您发布的内容,可能有更简单、等效的方法来获得平滑曲线。
您正在创建的每条曲线在您的代码开始、顶点和结束中都有 3 个点。让我们称它们为 S、V 和 E。此外,让我们通过时间来观察它们,因此 {S0,V0,E0}
形成第一条曲线,{S1,V1,E1}
形成第二条曲线,依此类推。
现在您确保 S1=E0
、S2=E1
等。这意味着您的灯光不会随机跳跃,但在切换贝塞尔曲线时动作可能会突然。
为保证动作流畅,尽量保证S1 = E0 = 0.5 * (V0+V1)
。这实际上意味着您随机生成 S0 和 V0。然后,对于每条新曲线,您都会生成一个新 V,旧 V 和新 V 之间的中点就是曲线转折点。
这等同于具有均匀度数为 2 的 B 样条曲线。
下面是一些可能具有说明性的示例代码。
// initialize with randomness
// we need a previous v and a current v for the first iteration
// So, the first start point will be halfway between these two points
prev_v = random_point()
v = random_point()
while( true )
{
next_v = random_point()
s = 0.5 * (prev_v + v) // s is halfway between prev_v and v
e = 0.5 * (v + next_v) // e is halfway between v and next_v
yield_movement_bezier( s, v, e )
// prep for next iteration
prev_v = v
v = next_v
}