Unity - use Bezier curves to bend 3D tubular objects

Reference link: [Unity] Simple implementation of elastic fishing rod - modify Mesh through Bezier curve - short book

Reference paper: Wu Xiaoliang, Huang Xiangnian. Using Bezier curves to bend 3D objects in Unity[J]. Modern Computer, 2016 (5): 57-59.

Unity project download: https://download.csdn.net/download/weixin_43042683/87690343

renderings

0 Preface

With the development of virtual reality, it is more and more important to simulate the bending effect of three-dimensional objects in the game engine. In a 3D game engine, some 3D objects need to be bent to achieve real-time simulation of object bending during game operations. When it comes to bending, it is natural to think of curves. From the perspective of curves, the key is how to generate curves and how to modify the shape of objects according to the curves to achieve the bending effect. To generate curves, you can directly think of using Bezier curves. The traditional Bezier curve algorithm is used in various graphics production software, such as Photoshop and other software, but it is mostly limited to the application of two-dimensional lines, and the application on three-dimensional objects less. By combining the Bezier curve algorithm with the grid vertices of the three-dimensional object, the bending change of the bar-shaped three-dimensional object can be realized.

1 Bezier curve

A Bézier curve is a mathematical curve applied to two-dimensional graphics applications. Curve definition: start point, end point (also called anchor point), control point. By adjusting the control points, the shape of the Bezier curve changes. In 1962, the French mathematician Pierre Bézier was the first to study this method of vector drawing curves, and gave a detailed calculation formula, so the curve drawn according to this formula is named after his surname, called Bezier. Searle curve.

Reference link: The principle and application of Unity Bezier curve (Beizer curve)

1.1 First-order Bezier curve

Standard formula:

 Schematic diagram:

Code:

    // 一阶贝塞尔曲线,参数P0、P1、t对应上方原理内的一阶曲线参数.
    Vector3 Bezier(Vector3 p0, Vector3 p1, float t)
    {
        return (1 - t) * p0 + t * p1;
    }

1.2 Second-order Bezier curve

Standard formula:

Schematic diagram:

Code:

    // 二阶贝塞尔曲线,参数对应上方原理内的二阶曲线参数.
    Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, float t)
    {
        Vector3 p0p1 = (1 - t) * p0 + t * p1;
        Vector3 p1p2 = (1 - t) * p1 + t * p2;
        Vector3 temp = (1 - t) * p0p1 + t * p1p2;
        return temp;
    }

1.3 Third-order Bezier curve

Standard formula:

Schematic diagram:

   // 三阶贝塞尔曲线,参数对应上方原理内的三阶曲线参数.
    Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
    {
        Vector3 temp;
        Vector3 p0p1 = (1 - t) * p0 + t * p1;
        Vector3 p1p2 = (1 - t) * p1 + t * p2;
        Vector3 p2p3 = (1 - t) * p2 + t * p3;
        Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
        Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;
        temp = (1 - t) * p0p1p2 + t * p1p2p3;
        return temp;
    }

1.4 Bezier curve of order n

Standard formula:

Code:

//贝塞尔曲线公式
private Vector3 CalculateBezier(float t)
{
    Vector3 ret = new Vector3(0, 0, 0);
    int n = 阶数;
    for(int i = 0; i <= n; i++)
    {
        Vector3 pi = 第i个控制点的坐标;
        ret = ret + Mathf.Pow(1 - t, n - i) * Mathf.Pow(t, i) * Cn_m(n, i) * pi;
    }
    return ret;
}
//组合数方程
private int Cn_m(int n, int m)
{
    int ret = 1;
    for(int i = 0; i < m; i++){
        ret = ret * (n - i) / (i + 1);  
    }
    return ret;    
}

Design ideas

Add multiple control points on the three-dimensional object, where the control points can be replaced by n empty nodes, and the coordinates of the control points are the coordinates of the empty nodes. As for the t value, it can be regarded as the ratio of the distance from the vertex to the bottom to the length of the entire three-dimensional object, 0<=t<=1. In this design, our first control point P0 should be at the bottom of the three-dimensional object, and the last control point Pn should be at the top of the three-dimensional object.

2. Realization of bending 

What is calculated according to the above formula is just a curve, and our goal is that the 3D object model can be bent according to this curve.

For a tubular object, the curve we calculate is actually its center line, and the mesh vertices should be located on both sides of the center line, so the coordinates of the curved vertices should be calculated by the Bezier curve after a certain transformation. Come.
After observation, it can be found that the coordinate P' of the vertex after bending should be obtained by two offsets from the calculated coordinate P on the curve: offset a in the normal direction of the point, and in the direction perpendicular to the curved surface Make an offset b .

 The corresponding code is as follows:

// 对原来的顶点做贝塞尔曲线变换,得到弯曲变换后对应的点位置
private void UpdateBezierBend()
{   
    oriVertices = 模型未弯曲时的顶点数组;
    topPos = 最后一个控制点的坐标,用来计算模型长度;
    bendVector = 弯曲方向;
    for(int i = 0; i < oriVertices.Length; i++)
    {
        //获取顶点坐标,计算t值
        Vector3 oriPos = oriVertices[i];
        float t = oriPos.y / topPos.y;
        //获取顶点在贝塞尔曲线上对应的坐标
        Vector3 p = CalculateBezier(t); 
        //获取顶点在曲线上应有的法线偏移向量
        Vector3 vectorA = GetBendNormalVector(t, oriPos, bendVector); 
        //获取顶点在曲线上应有的垂直偏移向量
        Vector3 vectorB = new Vector3(oriPos.x, 0, oriPos.z) - Vector3.Project(new Vector3(oriPos.x, 0, oriPos.z), bendVector); 
        //获取顶点最终弯曲位置
        vector3 p' = p + vectorA + vectorB;
    }
    todo-修改顶点坐标;
}
// 获取指定点上的法向量偏移
private Vector3 GetBendNormalVector(float t, Vector3 oriPos, Vector3 bendVector)
{
    Vector3 tangentVector = CalculateBezierTangent(t);//切线斜率
    Vector3 normalVector = 由法线和切线互相垂直计算出法线方向;
    //法线向量的模应为到投影到弯曲面后,到中心点的距离
    float magnitude = Vector3.Project(new Vector3(oriPos.x, 0, oriPos.z), bendVector).magnitude;
    normalVector = normalVector.normalized * magnitude;
    return normalVector;
}
//对曲线公式求导得出切线向量
private Vector3 CalculateBezierTangent(float t)
{
    Vector3 ret = new Vector3(0, 0, 0);
    int n = 阶数;
    for(int i = 0; i <= n; i++)
    {
        Vector3 pi = 第i个控制点的坐标;
        ret = ret + (-1 * (n - i) * Mathf.Pow(1 - t, n - i - 1) * Mathf.Pow(t, i) * Cn_m(n, i) * pi + i * Mathf.Pow(1 - t, n - i) * Mathf.Pow(t, i - 1) * Cn_m(n, i) * pi);
    }
    return ret;
}

In this way, we have realized the method of generating curves through control points and bending objects through curves.

3. Construct the force model

Simply construct a force model, apply a pulling force through the object, and the pulling force will change the control point, thereby making the object bend. Set a Cube as the object that applies the pulling force F, and then set a force Fc required for complete bending for each control point, and then set the bending angle of the control point in the direction of the pulling force as: a = Mathf.Clamp(F/
Fc , 0, 1.0) * The angle between the pulling force and the control point;
in order to simulate a more realistic bending effect, Fc can be regarded as the elastic force of each section of the three-dimensional object. The closer to the bottom of the control point, the larger the Fc is, and the more difficult it is to bend. Conversely, the closer the control point Fc is to the top, the smaller it is, and the easier it is to bend.
code show as below:

private void UpdateControlPoint()
{
    float F = Cube.force;
    //根据受力计算各个控制点旋转角度
    n = 控制点数量;
    for(int i = 1; i < n - 1; i++)//第一个和最后一个点不计算弯曲
    {
        //计算最大弯曲方向
        Vector3 toVector = 施力物体相对控制点pi的方向;
        Quaternion maxRotation =  Quaternion.FromToRotation(Vector3.up, toVector);
        //计算弯曲比例
        float rotateRate = Mathf.Clamp(F / Fc, 0f, 1.0f);
        //设置旋转角度
        pi.localRotation = Quaternion.Lerp(Quaternion.Euler(0, 0, 0), maxRotation, rotateRate);
    }
}

4. Conclusion

The bending effect produced by this method is still very natural (such as the rendering), and it is relatively simple to use, and does not require joint control. However, it is more performance-intensive. In addition, considering the lighting, the normal information of the mesh needs to be recalculated after the vertex coordinates are updated. 

At present, this method works well for tubular 3D objects, but is poor for 3D objects with complex shapes. This method requires the number of vertices of the three-dimensional object to be as large as possible to be effective. If the number of vertices is small, it is easy to cause distortion of the three-dimensional object.

Guess you like

Origin blog.csdn.net/weixin_43042683/article/details/130155790