[Unity] 動的にテクスチャにお絵かきする

お絵かき機能のあるゲームを作成する必要があったので調べました。

こちらが非常にわかりやすいですが少々バグとUnityのアップデートがあったので方法をメモします。

Unityでテクスチャにお絵描きしよう – おもちゃラボ http://nn-hokuson.hatenablog.com/entry/2016/12/08/200133

 

まず、Cubeオブジェクトを配置し、カメラに写るようにします。

Cubeオブジェクトの名前を「cube_draw」とし、インスペクタからBoxColliderをRemoveComponentし、新たにMeshColliderを追加します。これは後ほど登場するRayHitからUV値を取得するtextureCoordがBoxColliderだと常に(0,0)になってしまうのを防ぐためです。

続いてテキトーな画像を1つ用意し、テクスチャとしてCubeに貼り付けます。この時、「Advanced」->「Read/Write Enabled」にチェックをいれます。

最後に適当なMonoBehaviourに以下のようなコードを書きます。

public class SceneManager : MonoBehaviour {

	private Renderer renderer;
	private Texture2D drawTexture;
	private Color[] textureBuffer;

	private int brushSize = 10;
	// Use this for initialization
	void Start () {
		        
		this.renderer = GameObject.Find("cube_draw").GetComponent<Renderer>();
		var bodyTexture = (Texture2D)this.renderer.material.mainTexture;
		var bodyPixels = bodyTexture.GetPixels();
		this.textureBuffer = new Color[bodyPixels.Length];
		bodyPixels.CopyTo(this.textureBuffer,0);
		        
		this.drawTexture = new Texture2D(bodyTexture.width,bodyTexture.height,TextureFormat.RGBA32,false);
		this.drawTexture.filterMode = FilterMode.Point;
		this.drawTexture.SetPixels(this.textureBuffer);
		this.drawTexture.Apply();
		this.renderer.material.mainTexture = this.drawTexture;
	}
	
	// Update is called once per frame
	void Update () {
		if (Input.GetMouseButton(0))
		{
			// Screenのマウスの位置から空間に向けてレイ(光線)を放つ
			var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
			// 光線が何かのオブジェクトにヒットしたなら
			RaycastHit hit;
			if (Physics.Raycast(ray, out hit))
			{
				// UV値からテクスチャのどの部分にヒットしたのかを計算
				var hitPoint = new Vector2(hit.textureCoord.x * drawTexture.width, hit.textureCoord.y * drawTexture.height);

				// brushSize分のピクセルを塗りつぶす
				for (int x = (int) (hitPoint.x - brushSize / 2); x < hitPoint.x + brushSize; x++)
				{
					for (int y = (int) (hitPoint.y - brushSize / 2); y < hitPoint.y + brushSize; y++)
					{
						if (x >= 0 && y >= 0)
						{
							this.textureBuffer.SetValue(Color.black, (int) x + drawTexture.width * (int) y);
						}
					}
				}

			}

			// テクスチャを適用
			this.drawTexture.SetPixels(this.textureBuffer);
			this.drawTexture.Apply();
			this.renderer.material.mainTexture = this.drawTexture;
		}
	}
}

コードの解説としては、MainCameraのScreenPointToRayメソッドによってカメラから擬似的な光線を照射し、ヒットしたオブジェクトを返します。その後、textureCoordによってヒットした場所のテクスチャUV値を取得します。最後にその部分周辺のピクセル値を書き換えることでお絵かきを実現できます。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください