Version: 2018.4
言語: 日本語
オクルージョンカリング
マテリアル、シェーダー、テクスチャ

動的解像度

動的解像度は、個々のレンダーターゲットを動的にスケーリングし、GPU の負荷を軽減できるカメラ設定です。アプリケーションのフレームレートが減少する場合は、徐々に解像度を下げて、一貫したフレームレートを維持することができます。アプリケーションが GPU にバインドされている結果、フレームレートが低下しそうなことをパフォーマンスデータが示すと、Unity はこのスケーリングを発動します。また、アプリケーションの特に GPU 負担が大きい部分を見越して、スクリプトを使ってスケーリングを制御することで、スケーリングを手動で発生させることもできます。徐々にスケーリングすると、動的解像度はほとんど目立つことはありません。

サポートされているプラットフォーム

Unity は Xbox One、PS4、Nintendo Switch、iOS/tvOS (Metal のみ)、Android (Vulkan のみ) の動的解像度をサポートします。

レンダーターゲットへの影響

動的な解像度では、Unity はレンダーターゲットを再割り当てしません。概念的には、Unity はレンダーターゲットをスケールします。ただし、実際には、Unity はエイリアシングを使用し、縮小されたレンダリングターゲットは、元のレンダーターゲットのほんの一部のみを使用します。Unity は、レンダーターゲットを完全な解像度で割り当てます。次に、動的解像度システムは、新しいターゲットを再割り当てする代わりに、元のターゲットの一部を使用してレンダリングターゲットをスケールダウンし、再度バックアップします。

レンダーターゲットのスケーリング

動的な解像度では、レンダーターゲットには DynamicallyScalable フラグがあります。これを使って、このレンダーテクスチャを動的解像度処理の一部としてスケールするかどうかを設定します。カメラには allowDynamicResolution フラグもあります。これを使って動的解像度を設定すると、動的解像度をあまり複雑でないものに適用したい場合に、レンダーターゲットをオーバーライドする必要がありません。

MRT バッファ

カメラの Allow Dynamic Resolution を有効にすると、Unity はそのカメラのすべてのターゲットをスケールします。

スケーリングの制御

スケールは ScalableBufferManager を通して制御できます。 ScalableBufferManager は、動的解像度システムがスケールするように設定したすべてのレンダーターゲットの動的な幅と高さのスケールを制御します。

例として、アプリケーションが望ましいフレームレートで実行されていると仮定してください。しかし、状況によってはパーティクルの増加、ポストエフェクト、画面の複雑さが組み合わされて GPU のパフォーマンスが低下する場合があります。Unity FrameTimingManager を使用すると、CPU や GPU のパフォーマンスが低下し始めたときに、それを検出できます。そのため、 FrameTimingManager を使って希望の幅と高さのスケールを計算し、フレームレートを希望の範囲内に維持し、スケールをその値に落としてパフォーマンスを (即座に、または、徐々にフレームの設定された量に) 安定させます。画面の複雑さが減り、GPU が一貫して実行されている場合は、GPU が処理できる値に幅と高さのスケールを上げることができます。

このスクリプトサンプルは、API の基本的な使い方を示しています。シーン内のカメラに追加し、カメラ設定で Allow Dynamic Resolution をチェックしてください。また、Player 設定 (Edit> Project Settings から Player カテゴリを選択) を開き、Enable Frame Timing Stats チェックボックスをチェックする必要があります。

マウスをクリックするか、1 本の指で画面を軽くタップすると、高さと幅の解像度がそれぞれ scaleWidthIncrementscaleHeightIncrement 変数の値だけ下がります。2 本の指でタップすると同じ値で解像度が上がります。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DynamicResolutionTest : MonoBehaviour
{
    public Text screenText;

    FrameTiming[] frameTimings = new FrameTiming[3];

    public float maxResolutionWidthScale = 1.0f;
    public float maxResolutionHeightScale = 1.0f;
    public float minResolutionWidthScale = 0.5f;
    public float minResolutionHeightScale = 0.5f;
    public float scaleWidthIncrement = 0.1f;
    public float scaleHeightIncrement = 0.1f;

    float m_widthScale = 1.0f;
    float m_heightScale = 1.0f;

    // 動的解像度のアルゴリズムの値。フレームをまたがって持続されます。
    uint m_frameCount = 0;

    const uint kNumFrameTimings = 2;

    double m_gpuFrameTime;
    double m_cpuFrameTime;

    // 初期化に使用します
    void Start()
    {
        int rezWidth = (int)Mathf.Ceil(ScalableBufferManager.widthScaleFactor * Screen.currentResolution.width);
        int rezHeight = (int)Mathf.Ceil(ScalableBufferManager.heightScaleFactor * Screen.currentResolution.height);
        screenText.text = string.Format("Scale: {0:F3}x{1:F3}\nResolution: {2}x{3}\n",
            m_widthScale,
            m_heightScale,
            rezWidth,
            rezHeight);
    }

    // Update はフレームごとに 1 回呼び出されます
    void Update()
    {
        float oldWidthScale = m_widthScale;
        float oldHeightScale = m_heightScale;

        // 1 本指のタップは解像度を下げます
        if (Input.GetButtonDown("Fire1"))
        {
            m_heightScale = Mathf.Max(minResolutionHeightScale, m_heightScale - scaleHeightIncrement);
            m_widthScale = Mathf.Max(minResolutionWidthScale, m_widthScale - scaleWidthIncrement);
        }

        // 2 本指のタップは解像度を上げます
        if (Input.GetButtonDown("Fire2"))
        {
            m_heightScale = Mathf.Min(maxResolutionHeightScale, m_heightScale + scaleHeightIncrement);
            m_widthScale = Mathf.Min(maxResolutionWidthScale, m_widthScale + scaleWidthIncrement);
        }

        if (m_widthScale != oldWidthScale || m_heightScale != oldHeightScale)
        {
            ScalableBufferManager.ResizeBuffers(m_widthScale, m_heightScale);
        }
        DetermineResolution();
        int rezWidth = (int)Mathf.Ceil(ScalableBufferManager.widthScaleFactor * Screen.currentResolution.width);
        int rezHeight = (int)Mathf.Ceil(ScalableBufferManager.heightScaleFactor * Screen.currentResolution.height);
        screenText.text = string.Format("Scale: {0:F3}x{1:F3}\nResolution: {2}x{3}\nScaleFactor: {4:F3}x{5:F3}\nGPU: {6:F3} CPU: {7:F3}",
            m_widthScale,
            m_heightScale,
            rezWidth,
            rezHeight,
            ScalableBufferManager.widthScaleFactor,
            ScalableBufferManager.heightScaleFactor,
            m_gpuFrameTime,
            m_cpuFrameTime);
    }

    // 必要に応じて、次のフレーム時間を見積もり、解像度スケールを更新します
    private void DetermineResolution()
    {
        ++m_frameCount;
        if (m_frameCount <= kNumFrameTimings)
        {
            return;
        }
        FrameTimingManager.CaptureFrameTimings();
        FrameTimingManager.GetLatestTimings(kNumFrameTimings, frameTimings);
        if (frameTimings.Length < kNumFrameTimings)
        {
            Debug.LogFormat("Skipping frame {0}, didn't get enough frame timings.",
                m_frameCount);

            return;
        }

        m_gpuFrameTime = (double)frameTimings[0].gpuFrameTime;
        m_cpuFrameTime = (double)frameTimings[0].cpuFrameTime;
    }
}

関連項目


  • 2018–09–20 編集レビュー を行ってパブリッシュされたページ

  • 動的解像度に関するドキュメントは 2017.4 で追加

オクルージョンカリング
マテリアル、シェーダー、テクスチャ