【Unity】RenderTextureにParticleがうまく描画されなかった話
概要
RenderTextureにParticleを書き込んで使おうとしたところ、思ったように描画されなかったので、描画されるようになるまでの備忘録です
今回使ったプロジェクトは下記のGithubにあります。手短に結果が見たい人は参照してみてください
。
目指す最終状態
RenderTextureに描画したParticleが正しく描画され、後ろのオブジェクトが透けて見えている状態を目指します。
初期状態
Hierarchyは下記のようになっています
また、シーン上ではこのような配置になっています。Canvas
はScreen Space - Overlay
を使用しています。
大まかな処理の流れは
1.ParticleCamera
でParticleSystem
を撮影
2.RenderTexture
に書き込む
3.Canvas
のRawImage
でRenderTexture
を表示
という流れになっています
ParticleCamera
のClear Flag
がSkyBox
のときは正しくParticleが表示されていました。
ただし、SkyBoxが邪魔をしてその後ろの球体を見ることはできません。
このままではSkyBoxが映り込んでしまうので、Clear Flag
をSolid Color
に設定し、BackGround
を(0,0,0,0)にしたところ、Particleが表示されなくなってしまいました
何が起きているのかRendeTextureを確認してみると、RGBに色は書き込まれているもののAlphaチャンネルに色が書き込まれていないことがわかりました
対策1 CameraのBackgroundを調整する
Alphaチャンネルに情報が書き込まれていないことが原因なので、カメラ側で塗りつぶすようにすればとりあえずはParticleが表示されます。
ただし、この方法だとRenderTextureは透過しないので、後ろのオブジェクトは見えないままです。
CameraのBackgroundを(0,0,0,1)にする
一応Particleは表示される
対策2 ParticleのShaderを変更する
Particleに使われているShaderがAlphaチャンネルに情報を書き込まないのが原因なので、これを修正します。標準で使われているシェーダー(組み込みシェーダー)は
この中からParticleに使われているシェーダーを探します。DefaultResourcesExtra
のフォルダの中におそらく入っていると思われます。
私の場合はParticle Premultiply Blend.shader
でした。
このファイルをUnityプロジェクトの中にコピーします。
そして、ColorMask RGB
となっている部分をColorMask RGBA
に書き換え、シェーダーの名前も変えておきます
- Shader "Legacy Shaders/Particles/Alpha Blended Premultiply" {
+ Shader "Legacy Shaders/Particles/Alpha Blended Premultiply RGBA" {
Properties {
_MainTex ("Particle Texture", 2D) = "white" {}
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
}
Category {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
Blend One OneMinusSrcAlpha
- ColorMask RGB
+ ColorMask RGBA
Cull Off Lighting Off ZWrite Off
新たにMaterialを作成し、上記のShaderをアサイン、TextureにはDefault-Particle
を設定しました。
この状態だと後ろのオブジェクトが見えた状態でParticleも描画されるようになりました。
ただ、よく見てみるとParticleの縁が黒くなっています。
この現象は下記のサイトなどで詳しく説明されています。端的に説明すると、RenderTextureを描画する際にBackgroundColorとアルファブレンドした状態で描画しているのにもかかわらず、UIとして描画する際に再度アルファブレンドしてしまっているのでおかしな色になっています。
対策3 RenderTextureに書き込む際に対策をいれる
アルファブレンドを2回してしまうのが原因ということがわかりました。そこでRenderTextureに書き込む際に、色情報の方にはアルファ適用前の色を書き込むようにします。
具体的にはImageEffectShader
を利用します。
(URPの場合はImageEffectが使えないので、Renderer Featureを使えば同じことができると思います。)
まず、下記のC#スクリプトを作成します
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class ImageEffect : MonoBehaviour
{
[SerializeField] Material _material;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(source, destination, _material);
}
}
次にImageEffect用のShaderを作成します
Shader "ImageEffect/WriteRT"
{
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 = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb /= col.a; //col.rgb * col.aがアルファを適用した色なので、割って元に戻してる
return col;
}
ENDCG
}
}
}
このShaderを適用したMaterialを作成します。(ToRT
という名前)
そしてParticleCamera
にImageEffect
のスクリプトをアタッチし、Material
には今回作成したMaterial(ToRT
)をアサインします。
この状態で実行すると、Particleが正しく描画され、後ろのオブジェクトも見えている状態になりました
Discussion