渋谷ほととぎす通信

完全趣味でやってる技術メモ。※所属団体とは一切関係がありません。

Unity2018.3.0からDynamicResolutionがiOSやAndroid端末で使えるようになっているので検証


先日リリースされたUnity2018.3の新機能をなめている最中です。本記事ではDynamicResolutionについて検証します。

DynamicResolutionとは?

ランタイム中にレンダーターゲットの解像度を変更することができる機能です。
例 : 1920×1080で描画していたものを、動的に1280×720の解像度に落としたり戻したりすることが可能
リッチなイメージエフェクトや無駄に高い解像度端末のせいで、フラグメントシェーダの負荷(GPU負荷)が高くなってしまった状態を打破する手段の1つとして考えられます。

重要なのは解像度を変更するしないを、カメラないしはレンダーテクスチャ個別に選択できるという点です。

Unity的には2017.3のタイミングでXbox Oneでサポートしていました。
Unity 2017.3がリリースされました! – Unity Blogより

Unity2018.1でPS4に対応した模様。
Unity 2018.1 リリース – Unity Blogより

Unity2018.3で、ついにiOS(Metalのみ)、Android(Vulkanのみ)*1に対応しました。

Mobile improvements include Dynamic Resolution Scaling support for Vulkan and Metal, Android AppBundle generation support and faster APK package build times on Android with APKzlib.

Introducing Unity 2018.3 – Unity Blog

ということで、手持ちのiPhone6sで検証してみます。
※iOSなのでMetalは必須


繰り返しになりますがカメラ、レンダーテクスチャともにDyamicResolution設定が個別に出来ます。
※反映する解像度の拡縮値は一律

カメラのDyamicResolution設定

スクリプトだったら以下のようにallowDynamicResolutionプロパティをtrueにします。

_camera.allowDynamicResolution = true;

カメラのインスペクタからであれば以下にチェック。
f:id:esakun:20181217160857p:plain

レンダーテクスチャのDyamicResolution設定

スクリプトからであれば以下のようにuseDynamicScaleプロパティをtrueにします。

_renderTexture.useDynamicScale = true;

レンダーテクスチャのインスペクタからであれば以下にチェック。 f:id:esakun:20181217161013p:plain

レンダーテクスチャに書き込んだ後からuseDynamicScaleフラグの切り替えは出来ません。以下のエラーが出力されてしまいます。

Setting dynamic scaling of already created render texture is not supported!
UnityEngine.RenderTexture:set_useDynamicScale()

解像度を動的に変更する

ScalableBufferManager.ResizeBuffers(0.5f, 0.5f);

このような感じでScalableBufferManager.ResizeBuffersに変更したいスケールを代入するだけです。とても簡単。元の等倍状態は1.0です。
Unity - Scripting API: ScalableBufferManager

f:id:esakun:20181217173031g:plain
手持ちのiPhone6sでキャプチャした動画です。このように動的に解像度が変更できるようになりました。
※非常にカクついているのはgifに変換した時に低画質にしたからで、本当はヌルヌルに動いています

解像度を変更した後にRenderTextureを更新する

ScalableBufferManager.ResizeBuffersで解像度を変更したら、useDynamicScaleフラグを立てているレンダーテクスチャがクリアされますので、必ずレンダーテクスチャの更新を入れる必要があります。

// 解像度変更
ScalableBufferManager.ResizeBuffers(0.5f, 0.5f);
// RenderTextureの更新
Graphics.Blit(_tex, _renderTexture);

雑な例ですが、上のようなイメージです。
解像度変更後にレンダーテクスチャを更新をしないと見た目的にRenderTextureのカラーが(0,0,0,0)になります。

バグ?

// カメラのレンダーターゲットにRenderTextureをセットして描画
_camera.targetTexture = _renderTexture;
_camera.Render();

// カメラのレンダーターゲットを元のフレームバッファへ戻す
_camera.targetTexture = null;

こんな感じでCamera.targetTextureにRenderTextureをセットして、フレームバッファをレンダーテクスチャにコピーしてカメラのRenderTargetを既存のフレームバッファに戻します。
その後ScalableBufferManager.ResizeBuffersの値がリセットされ(1.0fへ戻る)、新しい値を代入しても反映されなくなります。

_camera.targetTexture = null;

このようにtargetTextureにnullを突っ込んだだけでも同様の状態になります。

仕様なのでしょうかね。

まとめ

  • カメラが複数存在しても、カメラごとに個別に設定できる
  • レンダーテクスチャごとに設定ができる
  • 解像度を変えたタイミングで、元の解像度時以上のGPU負荷が上がることはない(もちろんCPU側も)
  • UnityEditor上ではテストが出来ない => 確認がとてもめんどい

例えば3D表示の解像度だけ下げたくて、UIは解像度を下げたくないという場面はよくあると思いますが、お手軽対応できる点がグッドです。 Screen.SetResolutionより自由度はある

Unityブログでも書かれていますが、あくまでGPU負荷を下げるテクニックなので、制作しているプロダクトがそもそもGPUバウンドになりそうかどうかを確認すべきかと思います。CPUバウンドの場合は効果は見込めません。

また重要なことですがUIカメラにはDynamicResolution適用できません
UIに反映する場合はUIをレンダーテクスチャにする必要ありです。


サンプル一旦コチラにアップ

*1:その他にNintendo Switch、tvOS(Metalのみ)に対応しています