Bézier Curve Fun!

Math inspiration!

Bézier Curve Fun!

Context

Sometimes you randomly stumble on some content online that is just so well done and thought out, that you just get super inspired. This recently happened to me after YouTube thankfully suggested this awesome video on Freya Holmér's awesome channel:

I knew about Bézier curves before, but after seeing how well illustrated they were, as shown here, it immediately made me wanted to practice and translate this algorithm into code. Again, this video is simply visually amazing, and very inspiring. I challenge you to just watch it one time!


Also Freya's content, in general, is very cool for people interested in math for game development and 3D graphics.


I'd also add to that list of inspiration the folks at the awesome 3Blue1Brown YouTube channel. I don't care if you even have a PhD in mathematics, I'd bet you'll still learn something new with their awesome videos. For example, their Essence of linear algebra series made some concepts that I already knew click even more:

Note: For the next parts, I'll assume you've seen the Bézier curves video. So I won't go too much into the details of the actual algorithm, unless it's something not shown.

1st Attempt - Quadratic Curve!

Setting Up the Unity Project

Note: For the following, I created a new project using Unity Editor version 2021.3.4f1.

Create a new GameObject and call it BezierCurveDrawer:image.png

On that new GO, add a new script and also call it BezierCurveDrawer:image.png

Just to get something drawing, we're going to take advantage of Unity's OnDrawGizmos script message, so change the code of the script file to this:

using UnityEngine;

public class BezierCurveDrawer : MonoBehaviour
{
    public Vector3 m_point0 = new (-10f, 10f, 0f);
    public Vector3 m_point1 = new (0f, 20f, 0f);
    public Vector3 m_point2 = new (10f, 10f, 0f);

    private void OnDrawGizmos()
    {
        Gizmos.DrawSphere(m_point0, 0.2f);
        Gizmos.DrawSphere(m_point1, 0.2f);
        Gizmos.DrawSphere(m_point2, 0.2f);

        Gizmos.DrawLine(m_point0, m_point1);
        Gizmos.DrawLine(m_point1, m_point2);
    }
}

This will result in the Editor drawing lines between the 3 points. And since they're public, you can even modify the values, and the lines will be updated: Unity_p9GmZRaZMc.gif

Quadratic Curve!

In Freya's video, she talks about importance of linear interpolation: image.png

Lerp Formula: (1 - t) * P0 + t * P1

We can use this technique with multiple points and segments: image.png

The simplicity and beauty of this algorithm is that these nice-looking curves can be generated with this mix of t values that go from 0 to 1.0 (0, 0.05, 0.1, ..., 0.95, 1.0) and linear interpolations: chrome_yGerqGyIkk.gif

So with this knowledge, I can use Unity's built-in Vector3.Lerp function for our algorthim. Let's first organize our code a bit, split the current result in a fixed set of steps, and insert code to draw the set of points resulting from the algorithm:

using System.Collections.Generic;
using UnityEngine;

public class BezierCurveDrawer : MonoBehaviour
{
    public Vector3 m_point0 = new (-10f, 10f, 0f);
    public Vector3 m_point1 = new (0f, 20f, 0f);
    public Vector3 m_point2 = new (10f, 10f, 0f);

    private const float k_tValueStepIncrement = 0.05f;

    private const float k_controlPointRadius = 0.2f;

    private void OnDrawGizmos()
    {
        DrawControlPoints();

        DrawLineSegments();

        DrawBezierCurve();
    }

    private void DrawControlPoints()
    {
        Gizmos.DrawSphere(m_point0, k_controlPointRadius);
        Gizmos.DrawSphere(m_point1, k_controlPointRadius);
        Gizmos.DrawSphere(m_point2, k_controlPointRadius);
    }

    private void DrawLineSegments()
    {
        Gizmos.DrawLine(m_point0, m_point1);
        Gizmos.DrawLine(m_point1, m_point2);
    }

    private void DrawBezierCurve()
    {
        Gizmos.color = Color.cyan;

        var bezierCurvePoints = new List<Vector3>();

        for (float t = 0f; t <= 1.001f; t += k_tValueStepIncrement)
        {
            var segment1 = Vector3.Lerp(m_point0, m_point1, t);
            var segment2 = Vector3.Lerp(m_point1, m_point2, t);

            Vector3 currentCurvePoint = Vector3.Lerp(segment1, segment2, t);

            bezierCurvePoints.Add(currentCurvePoint);
        }

        foreach(var curvePoint in bezierCurvePoints)
        {
            Gizmos.DrawSphere(curvePoint, 0.1f);
        }

        for (int pointListIndex = 1;
               pointListIndex < bezierCurvePoints.Count;
               pointListIndex++)
        {
            Gizmos.DrawLine(
                bezierCurvePoints[pointListIndex - 1],
                bezierCurvePoints[pointListIndex]);
        }
    }
}

And with these set of lerps, we end up with this cool result: Unity_QyE499lZuy.gif

I'll leave it like this for now, but I'll continue this exercise in another post. I'm having a lot of fun with this, and I'll try to implement the next step which would be the cubic Bézier curve, and De Casteljau's algorithm:

image.png

Please let me know what you think!

  • Did you know about this algorithm?
  • Other than the examples mentioned in the video, how else would you use these kinds of curves?
  • Did you know about these channels? And if not, did they pump you up to practice math?

🎉🎉🎉Happy coding!!🎉🎉🎉

References/Inspirations