質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

カメラにOnRenderImageを含むスクリプトがアタッチされていると、Multiple Render Targetsが無視される

受付中

回答 0

投稿

  • 評価
  • クリップ 0
  • VIEW 4,290

ItoZakura

score 8

実現したいことと、起こっている問題

Unity5.3.4f1を使用しています。
オブジェクトの描画時にMultiple Render Targets(MRT)を使用し、通常の描画と並行してそのオブジェクトに関する値を別のRenderTextureに書き込み、それを後段のImageEffectで使用したいと考えています。

しかし、ImageEffectを行うためのOnRenderImageをカメラのスクリプトに追加するとMRTが無視されてしまい、値を書き込んだRenderTextureをImageEffectまで持ってくることができません。

具体的な状況

テストとして以下のようなシーンを作成しました。

  • カメラ - 画面と同じ大きさのRenderTextureを2枚作成し、SetTargetBuffersでそれらを描画先として指定するスクリプトをアタッチ
  • オブジェクト - 通常描画をSV_Target0、赤成分のみをSV_Target1に出力するシェーダで描画される

下の画面写真は、テストシーンの描画履歴をFrameDebuggerで表示したものです。
描画先としてRT0とRT1がリストに表示されており、RT1には赤成分が書き込まれています。ここまでは成功です。
このRenderTexture(RT1)を後段のImageEffectで使用したいと考えています。
OnrenderImageなし

しかし、カメラにアタッチしたスクリプトにOnRenderImageを追加すると、MRTが無視されてしまいます。
下の画面写真はOnRenderImageを追加した状態のものですが、リストにはRT0しかなく、RenderTargetも「ImageEffects Temp」という、こちらで作成したRenderTextureではないものが指定されています。
OnrenderImageあり

OnRenderImage とMRTを併用することはできないのでしょうか?

ソースコード

以下に、テストに使用したコードを掲載します。

  1. - カメラにアタッチするスクリプト(OnRenderImage あり/なし の2種類)
  2. - RenderTextureの内容を表示するためのシェーダ
  3. - MRTを使用するオブジェクトのマテリアル用のシェーダ

カメラにアタッチするスクリプトの、OnRenderImage なしのものです。

using UnityEngine;
using System.Collections;

public class MRTCameraTest : MonoBehaviour
{
  public RenderTexture[] RT;
  public Material postmat;
  private Camera cam;

  // Use this for initialization
  void Start()
  {
    cam = GetComponent<Camera>();

    RT = new RenderTexture[2];
    for (int i = 0; i < 2; i++)
    {
      int Depth = i == 0 ? 24 : 0;
      RT[i] = new RenderTexture(Screen.width, Screen.height, Depth);
      RT[i].Create();
    }

    RenderBuffer[] rb = new RenderBuffer[2];
    for (int i = 0; i < 2; i++)
      rb[i] = RT[i].colorBuffer;

    cam.SetTargetBuffers(rb, RT[0].depthBuffer);
  }

  // Update is called once per frame
  void Update()
  {
  }

  void OnPostRender()
  {
    // render to screen
    Graphics.SetRenderTarget(null);
    //Graphics.Blit(RT[0], postmat); // standard
    Graphics.Blit(RT[1], postmat); // red only
  }
}

OnRenderImageありの場合、OnPostRender以降が下のように差し替わります。

void OnPostRender()
  {
  }

  void OnRenderImage(RenderTexture source, RenderTexture destination)
  {
    RenderTexture tmpRT = RenderTexture.GetTemporary(256, 256, 0); // confirm value of RenderTexture on FrameDebugger
    //Graphics.Blit(RT[0], tmpRT, postmat);
    Graphics.Blit(RT[1], tmpRT, postmat);

    Graphics.Blit(source, destination);
  }

RenderTextureの内容をそのまま画面に表示するためのシェーダです。
このシェーダを割り当てたマテリアルを作成し、カメラスクリプトの postmat に指定して使用します。

// Created by
// Create > Shader > Image Effect Shader

Shader "Custom/Post Only"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                // just invert the colors
                //col = 1 - col;
                return col;
            }
            ENDCG
        }
    }
}

オブジェクトのマテリアルに割り当てるシェーダです。
オブジェクトの通常の描画結果をSV_Target0、赤成分をSV_Target1に出力します。
長いのでここに全体を載せることは出来ないのですが、以下の手順で作成したものです。

  • Create > Shader > Standard Surface Shader でsurfaceシェーダを作成
  • インスペクタから「Show generated code」で同内容のVertex/Fragmentシェーダを表示
  • MRT用の出力構造体(ps_out_multi)を追加
struct ps_out_multi
{
    fixed4 diffuse           : SV_Target0;
    fixed4 sub               : SV_Target1;
};
  • frag_surfの返値の型を変更し、通常描画をSV_Target0、赤成分をSV_Target1に出力
ps_out_multi frag_surf (v2f_surf IN) {
  // prepare and unpack data

  //// (中略)

  UNITY_OPAQUE_ALPHA(c.a);

  ps_out_multi pom;
  pom.diffuse = c; // all color > SV_Target0
  pom.sub = fixed4(c.r, 0, 0, 1); // red only > SV_Target1

  return pom;
}

まとめ

長くなってしまいましたが、当方の疑問としては以下の2点です。

  • 何故 OnRenderImage がカメラスクリプトに含まれていると、SetTargetBuffersによる描画先指定が無視されてしまうのか?
  • MRTによって書き込んだRenderTextureを後段のImageEffectで使用する方法は無いのか?

これらについて、何かご存知の方がいらっしゃいましたら、ご教示いただけますと幸いです。
どうぞよろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Unityに関する質問
  • カメラにOnRenderImageを含むスクリプトがアタッチされていると、Multiple Render Targetsが無視される