こんにちは、Unityエンジニアのオオバです。
本記事では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のみ)((その他にNintendo Switch、tvOS(Metalのみ)に対応しています))に対応しました。
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.
ということで、手持ちのiPhone6sで検証してみます。
繰り返しになりますがカメラ、レンダーテクスチャともにDyamicResolution設定が個別に出来ます。
カメラのDyamicResolution設定
スクリプトだったら以下のようにallowDynamicResolution
プロパティをtrueにします。
_camera.allowDynamicResolution = true;
カメラのインスペクタからであれば以下にチェック。
レンダーテクスチャのDyamicResolution設定
スクリプトからであれば以下のようにuseDynamicScale
プロパティをtrueにします。
_renderTexture.useDynamicScale = true;
レンダーテクスチャのインスペクタからであれば以下にチェック。
レンダーテクスチャに書き込んだ後からuseDynamicScaleフラグの切り替えは出来ません。以下のエラーが出力されてしまいます。
Setting dynamic scaling of already created render texture is not supported!
UnityEngine.RenderTexture:set_useDynamicScale()
レンダーテクスチャを動的に生成した場合は、メモリに乗せる前にuseDynamicScaleフラグをセットしておく必要があります。そうしないと同じエラーが出力されてしまいます。
解像度を動的に変更する
ScalableBufferManager.ResizeBuffers(0.5f, 0.5f);
このような感じでScalableBufferManager.ResizeBuffers
に変更したいスケールを代入するだけです。とても簡単。元の等倍状態は1.0です。
Unity - Scripting API: ScalableBufferManager
手持ちの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を突っ込んだだけでも同様の状態になります。
仕様なのでしょうかね。
👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!
まとめ
- カメラが複数存在しても、カメラごとに個別に設定できる
- レンダーテクスチャごとに設定ができる
- 解像度を変えたタイミングで、元の解像度時以上のGPU負荷が上がることはない(もちろんCPU側も)
- Unityエディタ上ではテストが出来ないので注意
例えば3D表示の解像度だけ下げたくて、UIは解像度を下げたくないという場面はよくあると思いますが、お手軽対応できる点がグッドです。
※Screen.SetResolution
より自由度はある
Unityブログでも書かれていますが、あくまでGPU負荷を下げるテクニックなので、制作しているプロダクトがそもそもGPUバウンドになりそうかどうかを確認すべきかと思います。CPUバウンドの場合は効果は見込めません。
また重要なことですがUIカメラにはDynamicResolution適用できません
UIに反映する場合はUIをレンダーテクスチャにする必要ありです。
サンプル一旦コチラにアップ
GitHub - baobao/DynamicResolutionTest
■追記:2020.06.07
githubにアップしたプロジェクトをUnity2019.3.15f1にアップデートしました。

- Unity2019.3.15f1