URPで特定レイヤーの描画時に解像度を変更する方法(VoxelStudio)

こんにちは。ApplibotVoxelStudioです。

ApplibotVoxelStudioでは日々グラフィックに関する技術研究を行っています。今回はその研究の一つであるURPで特定レイヤーの描画時に解像度を変更する方法について、実現方法の説明をします。 

この方法によって、下記のように一部のオブジェクトの解像度を変更するような効果を得ることができます。

樹木の解像度と、お城の解像度をそれぞれ変更

1. はじめに

まず、今回使用した環境は以下となります。

Unity2020.3.12f1
UniversalRenderPipeline10.5.1

ここからは実装した順にメインとなる部分のコード内容を解説していき、最後にレンダリング結果を紹介しています。

2. 設定ファイルを作成

※URPの基礎手順になるので、ご存知の方は3まで飛ばしてください

まず、CreateメニューからUniversalRenderPipelineAssetとForwardRendererDataを作成します。

次に、ScriptableRenderFeatureとScriptableRenderPassを継承したスクリプトをそれぞれ作成します。

今回はMultiResolutionRenderFeatureとMultiResolutionRenderPassというクラス名で作成します。

3. ScriptableRenderFeatureでパラメータを定義

今回の複数解像度実装はLayerMask単位で解像度を指定できるようにしたいので、RenderFeatureでは下記のように定義します。

[SerializeField, Header("解像度を適応するMask")] 
LayerMask _mask;
public int MaskValue => _mask.value;

[SerializeField, Header("指定したMaskの解像度比率"), Range(0.01f, 1.0f)] 
private float _resolutionRate = 1;
public float ResolutionRate => _resolutionRate;

[SerializeField, Header("シーンビューに結果を反映するか")] 
private bool _applySceneView = false;

RenderPassの実装で詳しく説明しますが、RenderPassのExecute実行時に描画順は変更できないため今回は不透明描画のみを想定しています。

そのため、設定はLayerMaskの指定(複数)と解像度比率のみとしています。

LayerMaskResolutionRateApplySceneView
RenderFeature_ATree0.15false
RenderFeature_BCastle0.07false

4. レンダリング結果をブレンドするShaderを作成

こちらはシンプルなのでほぼ省略します。

複数の解像度変換後の描画結果をブレンドする処理をShader側で行います。

color.rgb = mainTex.rgb * (1 - lowResolutionTex.a) + lowResolutionTex.rgb;

5. ScriptableRenderPassで描画処理を実装

実際にレンダリングを行う処理部分を実装します。

まずは、コンストラクタで各種の初期化処理を行います。

public MultiResolutionRenderPass(MultiResolutionRenderFeature renderFeature)
{
    _renderFeature = renderFeature;

    // 現状、不透明の描画のみ
    renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;

    _renderId = new ShaderTagId("UniversalForward");
    _lowResolutionTextureId = Shader.PropertyToID(_renderFeature.name + "_Tex");
    _lowResolutionDepthTextureId = Shader.PropertyToID(_renderFeature.name + "_Depth");

    // 複数ブレンド用のマテリアルを生成
    var shader = Shader.Find("Custom/Blend");
    _blendMat = CoreUtils.CreateEngineMaterial(shader);
}

次に、実行処理で解像度を落としたRenderTextureを取得してRenderTargetとして登録する処理を行います。

// CommandBufferの取得
var cmd = CommandBufferPool.Get("MultiResolutionRender" + _renderFeature.name);

// 指定した解像度比率に変換
var originalBuffer = renderingData.cameraData.renderer.cameraColorTarget; // 大本のバッファ
var originalDesc = renderingData.cameraData.cameraTargetDescriptor;

var resolutionRate = 1.0f;
if (!renderingData.cameraData.isSceneViewCamera || _renderFeature.ApplySceneView)
{
    resolutionRate = _renderFeature.ResolutionRate;
}

var width = (int)(originalDesc.width * resolutionRate);
var height = (int)(originalDesc.height * resolutionRate);
cmd.GetTemporaryRT(_lowResolutionTextureId, width, height, originalDesc.depthBufferBits, FilterMode.Point);
cmd.GetTemporaryRT(_lowResolutionDepthTextureId, width, height, originalDesc.depthBufferBits, FilterMode.Point, RenderTextureFormat.Depth);

// RenderTargetを低解像度用のバッファに変更
cmd.SetRenderTarget(color: _lowResolutionTextureId, depth: _lowResolutionDepthTextureId);
cmd.ClearRenderTarget(true, true, Color.clear, 1f);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();

次に、描画対象(LayerMask)を取得してRenderTargetに描画する処理を行います。

var cullingResults = renderingData.cullResults;
var drawSettings = CreateDrawingSettings(_renderId, ref renderingData, SortingCriteria.CommonOpaque);
var renderQueueRange = new RenderQueueRange(0, (int)RenderQueue.GeometryLast);

// 変換したテクスチャを指定したLayerMaskに適応
var filterSettings = new FilteringSettings(renderQueueRange, _renderFeature.MaskValue);
context.DrawRenderers(cullingResults, ref drawSettings, ref filterSettings);

その後、先ほど作成したレンダリング結果をブレンドするShaderを適応したマテリアルを使用し、結果を合成します。

// 低解像度レンダーテクスチャを変数で扱えるようにする
cmd.SetGlobalTexture("_LowResolutionTex", _lowResolutionTextureId);
// レンダーテクスチャを合成
cmd.Blit(originalBuffer, originalBuffer, _blendMat);
// 元のバッファに戻す
cmd.SetRenderTarget(originalBuffer);

context.ExecuteCommandBuffer(cmd);
cmd.ReleaseTemporaryRT(_lowResolutionTextureId);
cmd.ReleaseTemporaryRT(_lowResolutionDepthTextureId);

cmd.Clear();
CommandBufferPool.Release(cmd);

ScriptableRenderPassで行うことはこれで以上です。

6. ForwardRendererDataでRenderFeatureを追加する

実装部分は完了したので、ここまでで実装したものを設定ファイル側で追加し、パラメータを入力します。

TreeのLayerMaskが設定されているオブジェクトの解像度を0.15倍に、CastleのLayerMaskが設定されているオブジェクトの解像度を0.07倍に設定しました。

加えて、追加したLayerMaskをFiltering設定から外しておきます。

描画結果は下記のようになります。

7. 最後に

今回は、VoxelStudioで研究を行ったテーマの紹介をしました。

今後も定期的に発信していきますので、お楽しみに!

使用素材 : Low Poly Ultimate Pack

special thanks : @AblerBiri


関連記事一覧

  1. この記事へのコメントはありません。