【Unity(C#)】Lerpで滑らかな線を描く

##はじめに
平面上に線を滑らかに描く方法を調べました。

VR空間でもぬるぬる動いている例を参考にしました(↓すごい)

【参考リンク】：[ホワイトボードで線がヌルヌル描ける仕組み](https://virtualcast.jp/blog/2019/12/whiteboard/)

##描く実装

"描く部分"のコードは下記リンクからまるまる拝借しました。

【参考リンク】：[Unityでテクスチャにお絵描きしよう](https://nn-hokuson.hatenablog.com/entry/2016/12/08/200133)

##デモ

まずは補間することなくそのまま動かしてみたサンプルです。
![Paint2DForQiita1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/d68e7822-2cf9-4a2d-d5e5-b782a8b557d1.gif)

線がかすれてしまって不格好です。

---

次にLerpによる補間を施したものです。
![Paint2DForQiita2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/90f74d43-d5ff-7381-1c4a-af3f97cbd484.gif)

しっかりと線の間を補間してくれました。

【参考リンク】：[[Unity] Vector3.Lerpの使い方](https://qiita.com/aimy-07/items/ad0d99191da21c0adbc3)

##コード

下記全文です。

```C# 
using UnityEngine;

//使い方↓
//https://nn-hokuson.hatenablog.com/entry/2016/12/08/200133
public class SmoothPaint : MonoBehaviour
{
    Texture2D drawTexture;
    Color[] buffer;

    private Vector2 _prevPosition;

    void Start()
    {
        Texture2D mainTexture = (Texture2D) GetComponent<Renderer>().material.mainTexture;
        Color[] pixels = mainTexture.GetPixels();

        buffer = new Color[pixels.Length];
        pixels.CopyTo(buffer, 0);

        drawTexture = new Texture2D(mainTexture.width, mainTexture.height, TextureFormat.RGBA32, false);
        drawTexture.filterMode = FilterMode.Point;
    }

    public void Draw(Vector2 p)
    {
        for (int x = 0; x < 256; x++)
        {
            for (int y = 0; y < 256; y++)
            {
                if ((p - new Vector2(x, y)).magnitude < 5)
                {
                    buffer.SetValue(Color.black, x + 256 * y);
                }
            }
        }
    }

    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            //前回値がまだないなら現在の値を前回値として扱う
            if (_prevPosition == Vector2.zero)
            {
                _prevPosition = Input.mousePosition;
            }

            //線形補間に使う入力の終点座標
            Vector2 endPosition = Input.mousePosition;
            //1フレームの線の距離
            float lineLength = Vector2.Distance(_prevPosition, endPosition);
            //線の長さに応じて変わる補間値　CeilToIntは小数点以下を切り上げ
            int lerpCountAdjustNum = 5;
            int lerpCount = Mathf.CeilToInt(lineLength / lerpCountAdjustNum);

            for (int i = 1; i <= lerpCount; i++)
            {
                //Lerpの割合値を "現在の回数/合計回数" で出す
                float lerpWeight = (float) i / lerpCount;

                //前回の入力座標、現在の入力座標、割合を渡して補間する座標を算出
                Vector3 lerpPosition = Vector2.Lerp(_prevPosition, Input.mousePosition, lerpWeight);
                
                Ray ray = Camera.main.ScreenPointToRay(lerpPosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit, 100.0f))
                {
                    Draw(hit.textureCoord * 256);
                }

                drawTexture.SetPixels(buffer);
                drawTexture.Apply();
                GetComponent<Renderer>().material.mainTexture = drawTexture;
            }
            
            //前回の入力座標を記録
            _prevPosition = Input.mousePosition;
        }
        else
        {
            //前回の入力座標をリセット
            _prevPosition = Vector2.zero;
        }
    }
}
```
ロジックとしては下記です
**①前フレームのマウスの入力座標を保持
②次フレームにて①と現在の入力座標の距離を算出
③距離に応じて補間値(補間回数)を算出
④補間値を利用しLerpで補間**


##線が繋がる
実装していてそこそこ時間を使ってしまったのが、
**線が繋がってしまう**　という現象でした。

下記GIFのように新しく描き始めた箇所と
前回の終了地点が繋がってしまっていました。

![Paint2DForQiita3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/32b74254-7855-2473-98db-ab344229bc4c.gif)


これは描き終えた際の値を前回値として保持したまま
新しい入力箇所での補間を行っていることが原因でした。

ですので、下記箇所で**入力が無い状態になった際に前回値のリセット**を行っています。
さらに、入力値のリセットにより前回値が存在しないフレーム、
すなわち**描き始めのフレームにおいては補間を行う必要が無い**ので
前回の値＝現在の値として扱うようにしています。

```C# 

    if(Input.GetMouseButton(0))
    { 
        //前回値がまだないなら現在の値を前回値として扱う
        if (_prevPosition == Vector2.zero)
        {
            _prevPosition = Input.mousePosition;
        }
    }
    else
    {
        //前回の入力座標をリセット
        _prevPosition = Vector2.zero;
    }
```


これにより描き始めた箇所に線を新しく描画することができました。
![Paint2DForQiita4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/97686119-9ab8-0a12-ebbc-8a4bdd84fa5e.gif)

##おわりに
線の太さ(大きさ)も考慮した補間値を算出すれば
もっと最適な補間ができるみたいです。

そのうち書けたら書きます。