Unityではゲームの実行時にリアルタイムでテクスチャのピクセルを変更することが出来ます。この方法を使ってテクスチャにお絵かきをしてみたいと思います。
今回の記事の内容は次のとおりです。
平面の配置
まずは、シーンビューに絵を描くための平面を配置します。ヒエラルキービューから「Create」→「3D Object」→「Plane」を選択してください。
カメラは平面を見下ろすように配置します。ヒエラルキービューからMain Cameraを選択し、インスペクタからPositionを(0, 7, -8)に、Rotationを(50, 0, 0)に設定してください。
テクスチャの設定
次に、絵を描くためのテクスチャをプロジェクトにインポートします。ここでは次のファイルを使いました。
テクスチャに絵を描くためにはテクスチャ自体の設定が必要です。プロジェクトビューでテクスチャを選択して、インスペクタから「Texture Type」を「Advanced」に設定して、「Read/Write Enabled」にチェックを入れます。
このテクスチャを先ほど配置したPlaneにドラッグ&ドロップして、マテリアルをアタッチします。Planeのシェーダは紙の質感が見えやすいようにUnlit/Textureを選択しておきましょう。
テクスチャの上半分を塗りつぶす
最後に、お絵かきの肝となるスクリプトの作成をしましょう。プロジェクトビューで右クリックし、「Create」→「C# Script」でスクリプトを作成し「PaintController」にリネームしてください。
とりあえずテクスチャの上半分を真っ黒に塗りつぶすスクリプトを書いてみましょう。
using UnityEngine; using System.Collections; public class PaintController : MonoBehaviour { Texture2D drawTexture ; Color[] buffer; void Start () { Texture2D mainTexture = (Texture2D) GetComponent<Renderer> ().material.mainTexture; Color[] pixels = mainTexture.GetPixels(); buffer = new Color[pixels.Length]; pixels.CopyTo (buffer, 0); // 画面上半分を塗りつぶす for(int x = 0; x < mainTexture.width; x++){ for(int y = 0; y < mainTexture.height; y++){ if( y < mainTexture.height / 2 ){ buffer.SetValue (Color.black, x + 256 * y); } } } drawTexture = new Texture2D (mainTexture.width, mainTexture.height, TextureFormat.RGBA32, false); drawTexture.filterMode = FilterMode.Point; } void Update () { drawTexture.SetPixels (buffer); drawTexture.Apply(); GetComponent<Renderer> ().material.mainTexture = drawTexture; } }
Startメソッド内では、現在表示しているテクスチャのピクセルをバッファ用の配列(buffer)にコピーしています。このバッファを更新することで画面上の見た目も更新されるように、Updateメソッドではbufferの配列をdrawTextureに設定したうえで、それをmainTextureにセットしています。
作成したPaintControllerスクリプトをシーンビューの「Plane」にドラッグ&ドロップしてアタッチします。
実行結果は次のようになります。
マウスの軌跡を塗りつぶす
では、このスクリプトを書き換えてマウスの軌跡を黒く塗りつぶすように変更してみましょう。
using UnityEngine; using System.Collections; public class PixAccess : MonoBehaviour { Texture2D drawTexture ; Color[] buffer; 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) { buffer.SetValue (Color.black, (int)p.x + 256 * (int)p.y); } void Update () { if (Input.GetMouseButton (0)) { Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); RaycastHit hit; if (Physics.Raycast (ray, out hit, 100.0f)) { Draw (hit.textureCoord * 256); } drawTexture.SetPixels (buffer); drawTexture.Apply (); GetComponent<Renderer> ().material.mainTexture = drawTexture; } } }
マウスの軌跡を塗りつぶすためには、マウスが乗っている平面の座標を知る必要があります。これにはUnityが提供してくれているRayの機能を使います。Rayを使えば、マウスから出たRayが平面と交差した点のuv座標を取得することができます。
得られる座標は 0 < u < 1、0 < v < 1なので、これをテクスチャのピクセル数に変換して、該当のピクセルを黒く塗りつぶしています。実行結果は次のとおりです。
ブラシの太さを太くする
最後にブラシの太さが1pxでは見にくいので、もう少しブラシを太くするスクリプトを紹介します。今回はDrawメソッドの中身だけを書き換えています。
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); } } } }
先ほどのスクリプトではマウスが乗っている1ピクセルだけを書き換えていました。今回のスクリプトでは毎フレーム、テクスチャ上のすべてのピクセルをチェックして、マウスが乗っている座標からの距離が8以下なら黒く塗りつぶします。
実行結果は次のようになります。
まとめ
今回はテクスチャにお絵かきをする方法を紹介しました。直接テクスチャに書くのではなく、マスクに描画してシェーダで合成するともっときれいな表現が可能になる・・・気がします。
基本的なUnityの使い方は「Unity5の教科書」で解説しているので是非参考にしてみてくださいね!