夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

MRGTのスクリーンショット機能にショートカットキーを追加する その① 処理の把握

本日はMRGT実装枠です。

 現在MixedRealityGraphicsTools(MRGT)へ新たなイシューを作成し、スクリーンショットの機能改良を行っています。

github.com

 今回はショートカットキーを追加するための調査を行いました。

〇MRGTのスクリーンショット機能

MRGTにはシェーダーなどのグラフィックシステムのほかにグラフィックを支援する多くのツールが提供されています。

 スクリーンショット機能はUnity上のカメラに基づいて最大4倍の解像度で撮影を行うことができます。

 また、Windows標準のスクリーンショット撮影機能との違いとして、背景をくりぬきPing画像として出力することもできます。

redhologerbera.hatenablog.com

〇スクリーンショットの機能の場所

 スクリーンショットのコンポーネントはEditor内のScreenshotUtilitiesです。

ScreenshotUtilitiesの中では大きく4つの関数が定義されています。

〇メニューの追加

 前半部ではUnityのワールドメニューにスクリーンショット撮影のメニューを追加しています。

  [MenuItem("Window/Graphics Tools/Take Screenshot/Native Resolution")]
        private static void CaptureScreenshot1x()
        {
            CaptureScreenshot(GetScreenshotPath(), 1);
            EditorUtility.RevealInFinder(GetScreenshotDirectory());
        }

        [MenuItem("Window/Graphics Tools/Take Screenshot/Native Resolution (Transparent Background)")]
        private static void CaptureScreenshot1xAlphaComposite()
        {
            CaptureScreenshot(GetScreenshotPath(), 1, true);
            EditorUtility.RevealInFinder(GetScreenshotDirectory());
        }

        [MenuItem("Window/Graphics Tools/Take Screenshot/2x Resolution")]
        private static void CaptureScreenshot2x()
        {
            CaptureScreenshot(GetScreenshotPath(), 2);
            EditorUtility.RevealInFinder(GetScreenshotDirectory());
        }

        [MenuItem("Window/Graphics Tools/Take Screenshot/2x Resolution (Transparent Background)")]
        private static void CaptureScreenshot2xAlphaComposite()
        {
            CaptureScreenshot(GetScreenshotPath(), 2, true);
            EditorUtility.RevealInFinder(GetScreenshotDirectory());
        }

        [MenuItem("Window/Graphics Tools/Take Screenshot/4x Resolution")]
        private static void CaptureScreenshot4x()
        {
            CaptureScreenshot(GetScreenshotPath(), 4);
            EditorUtility.RevealInFinder(GetScreenshotDirectory());
        }

        [MenuItem("Window/Graphics Tools/Take Screenshot/4x Resolution (Transparent Background)")]
        private static void CaptureScreenshot4xAlphaComposite()
        {
            CaptureScreenshot(GetScreenshotPath(), 4, true);
            EditorUtility.RevealInFinder(GetScreenshotDirectory());
        }

〇スクリーンキャプチャの撮影

実際に撮影を行う処理はCaptureScreenshot()になります。

第一引数で、保存するパスを、第二引数で解像度を、第三引数で透明度を指定します。ここで背景をくりぬいたPng画像などの作成を設定できます。 第四引数で撮影を行うカメラを指定しています。

Unityでワールドメニューからスクリーンキャプチャのボタンが選択された場合この関数が走ります。

 public static bool CaptureScreenshot(string path, int superSize = 1, bool transparentClearColor = false, Camera camera = null)
        {
            if (string.IsNullOrEmpty(path) || superSize <= 0)
            {
                return false;
            }

            // If a transparent clear color isn't needed and we are capturing from the default camera, use Unity's screenshot API.
            if (!transparentClearColor && (camera == null || camera == Camera.main))
            {
                ScreenCapture.CaptureScreenshot(path, superSize);

                Debug.LogFormat("Screenshot captured to: {0}", path);

                return true;
            }

            // Make sure we have a valid camera to render from.
            if (camera == null)
            {
                camera = Camera.main;

                if (camera == null)
                {
                    Debug.Log("Failed to acquire a valid camera to capture a screenshot from.");

                    return false;
                }
            }

            // Create a camera clone with a transparent clear color.
            var renderCamera = new GameObject().AddComponent<Camera>();
            renderCamera.orthographic = camera.orthographic;
            renderCamera.transform.position = camera.transform.position;
            renderCamera.transform.rotation = camera.transform.rotation;
            renderCamera.clearFlags = transparentClearColor ? CameraClearFlags.Color : camera.clearFlags;
            renderCamera.backgroundColor = transparentClearColor ? new Color(0.0f, 0.0f, 0.0f, 0.0f) : camera.backgroundColor;
            renderCamera.nearClipPlane = camera.nearClipPlane;
            renderCamera.farClipPlane = camera.farClipPlane;

            if (renderCamera.orthographic)
            {
                renderCamera.orthographicSize = camera.orthographicSize;
            }
            else
            {
                renderCamera.fieldOfView = camera.fieldOfView;
            }

            // Create a render texture for the camera clone to render into.
            var width = Screen.width * superSize;
            var height = Screen.height * superSize;
            var renderTexture = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32);
            renderTexture.antiAliasing = 8;
            renderCamera.targetTexture = renderTexture;

            // Render from the camera clone.
            renderCamera.Render();

            // Copy the render from the camera and save it to disk.
            var outputTexture = new Texture2D(width, height, TextureFormat.ARGB32, false);
            RenderTexture previousRenderTexture = RenderTexture.active;
            RenderTexture.active = renderTexture;
            outputTexture.ReadPixels(new Rect(0.0f, 0.0f, width, height), 0, 0);
            outputTexture.Apply();
            RenderTexture.active = previousRenderTexture;

            try
            {
                File.WriteAllBytes(path, outputTexture.EncodeToPNG());
            }
            catch (Exception e)
            {
                Debug.LogException(e);

                return false;
            }
            finally
            {
                UnityEngine.Object.DestroyImmediate(outputTexture);
                UnityEngine.Object.DestroyImmediate(renderCamera.gameObject);
                UnityEngine.Object.DestroyImmediate(renderTexture);
            }

            Debug.LogFormat("Screenshot captured to: {0}", path);

            return true;
        }

〇パスの定義

GetScreenshotDirectory()では保存先のパスを定義しています。

        public static string GetScreenshotDirectory()
        {
            return Application.temporaryCachePath;
        }

docs.unity3d.com

Application.temporaryCachePathはUnityで提供される一時データの保存に使用されるパスが指定されています。

Windowsの場合C:\Users(ユーザー名)\AppData\Local\Temp\DefaultCompany(Unityプロジェクト名)になります。

〇ファイルの指定

この関数はその名前の通り、保存するファイル名取得しています。

 public static string GetScreenshotPath()
        {
            return Path.Combine(GetScreenshotDirectory(), string.Format("Screenshot_{0:yyyy-MM-dd_hh-mm-ss-tt}_{1}.png", DateTime.Now, GUID.Generate()));
        }

Path.Combine()はSystem.Ioで定義される関数で文字列を一つのパスに結合するクラスです。

 引数内はString型が入り、ここでは、撮影したスクリーンショットのファイル名が指定されます。

docs.microsoft.com

 今回はまずショートカットキーを指定するためにどこの関数を呼び出すべきかでざっと処理を把握しました。

 どうやらScreenshotUtilitiesで処理自体は完結しているようです。