おもちゃラボ

Unityで遊びを作ってます

【Unity】WebCamTextureで写真撮影

画面をタップしたときにパシャっと写真撮影し、撮影画像をPlaneなどに貼って表示するというのが意外と大変だったので、その手順をまとめました。

f:id:nn_hokuson:20211009190337p:plain:w300

写真撮影する流れ

画面がタップされてからカメラを起動→撮影とすると時間がかかってしまうため、基本的にカメラはバックグラウンドで起動しておき、タップされたときにカメラに映っている映像を取得する、という流れになります。

これをふまえて、写真撮影をする流れは次のようになります。

  1. カメラの映像をWebCamTextureで取得
  2. 取得した映像をRawImageで表示
  3. タップされたらRawImageの映像をTexture2Dに変換
  4. マテリアルのテクスチャに設定

図にするとこんな感じになります。

f:id:nn_hokuson:20211009184005p:plain:w500

プログラムを作る

WebCamControllerという名前でスクリプトを作成して、次のプログラムを入力してください。

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(RawImage))]
public class WebCamController : MonoBehaviour
{
    WebCamTexture webcamTexture;
    RawImage rawImage;

    void Start()
    {
        WebCamDevice[] devices = WebCamTexture.devices;
        webcamTexture = new WebCamTexture(devices[0].name,1920, 1080, 30);
        this.rawImage = GetComponent<RawImage>();
        this.rawImage.texture = webcamTexture;
        this.rawImage.enabled = false;
        webcamTexture.Play();
    }

    void TakeShot()
    {
        Texture tex = this.rawImage.texture;
        int w = tex.width;
        int h = tex.height;

        RenderTexture currentRT = RenderTexture.active;
        RenderTexture rt = new RenderTexture(w, h, 32);

        Graphics.Blit(tex, rt);
        RenderTexture.active = rt;

        Texture2D result = new Texture2D(w, h, TextureFormat.RGBA32, false);
        result.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        result.Apply();
        RenderTexture.active = currentRT;

        GetComponent<MeshRenderer>().material.mainTexture = result;
    }
 
    private void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            TakeShot();
        }
    }
}

Startメソッドの中では、WebCamTextureでカメラ映像を取得し、それをRawImageに設定しています。

具体的には、まずWebCamTexture.devicesでPCに接続されているカメラを取得し、カメラ名からWebCamTextureを作成します。作成したWebCamTexture をRawImageコンポーネントのテクスチャに設定することで、RawImageにカメラからの映像が表示されます。今回は写真を撮影したときにのみ画面表示を行いたいので、ここではRawImageのenableをfalseにしています。

f:id:nn_hokuson:20211009184151p:plain:w500

画面がタップされるとTakeShotメソッドが呼ばれます。TakeShotメソッドではRawImageの映像をTexture2Dに変換しています。

RawImageからTexture2Dへは、単純にキャストするだけでは変換できません。いったんRawImageをRenderTextureに書き出し、それをReadPixelsメソッドでTexture2Dに書き込んでいます。

f:id:nn_hokuson:20211009184231p:plain:w500

最後に、PlaneオブジェクトのMeshRendererコンポーネントを通してマテリアルを取得し、作成したTexture2D型の画像をマテリアルのmainTextureに設定しています。

f:id:nn_hokuson:20211009185629p:plain:w500

Planeに設定する

ヒエラルキーウインドウの「+」をクリックして「3D Object」→「Plane」を選択してください。シーンビューにPlaneが表示されます。

作成したPlaneにWebCamControllerスクリプトドラッグ&ドロップしてアタッチします。スクリプト中でRequireComponent(typeof(RawImage))としているため、RawImageコンポーネントも自動的にアタッチされます。

f:id:nn_hokuson:20211009185346j:plain

実行してみる

ここまでできたら実行してみてください。画面をクリックするとクリックした時点での写真が表示されます。