rn.log

備忘録など

【Unity】メッシュの頂点カラーにColorを設定するとParticleSystemで色が壊れる話

はじめに

Unity C# で作成したメッシュをParticleSystemで使用したとき、頂点カラーの表示が壊れました。
この現象の原因の考察・および回避方法をまとめようと思います。

f:id:r-ngtm:20210119040423p:plain:h320
ParticleSystemでメッシュを表示

以下のような頂点カラーを設定すると、ParticleSystemで表示したときに頂点カラーが壊れます。

mesh.colors = colors; // Color[]



環境

Unity 2020.2.0f1
Universal RP 10.2.2


mesh.colorに頂点カラーを設定

mesh.colorsに頂点カラーを設定してメッシュを作成したとします。

mesh.colors = colors; // Color[]

頂点カラー情報は メッシュに16byte カラーとして保存されます。

f:id:r-ngtm:20210119052726p:plain
colorsは16byteカラーとしてメッシュに保存されます。

MeshRenderer で表示した場合

このメッシュをMeshRendererで描画した際は、色が正常に表示されます。

f:id:r-ngtm:20210119040441p:plain:h320
MeshRendererで表示

メッシュのマテリアルにはシェーダーグラフを使用しており、頂点カラーをMasterノードとして出力しています。

f:id:r-ngtm:20210119040750p:plain:h320
頂点カラー(Vertex Color)を出力するShaderGraph

ParticleSystem で表示した場合

このメッシュをParticleSystemで表示すると色が壊れます。

f:id:r-ngtm:20210119040423p:plain:h320
ParticleSystemでメッシュを表示

mesh.colors は Color であるのに対して、Particle Systemでは頂点カラーは Color32 として扱われていることが理由だと考えられます。


以下のように32bitカラーを設定すれば、色が壊れる現象は回避できます。

mesh.colors32 = colors32; // Color32[]

Colors32 は 4byteカラー(32bitカラー)としてメッシュに保存されます。

f:id:r-ngtm:20210119055540p:plain
Colors32 は 4byteカラーとしてメッシュに保存される

色が壊れる理由について

ParticleSystemのカラーはColors32

Unityの ParticleSystemは頂点カラーをColor32として持っているようです。

たとえばParticleSystem.csの中を見ると、ParticleSystem.startColorはColor32型で定義されています。

/// <summary>
///   <para>The initial color of the particle. The current color of the particle is calculated procedurally based on this value and the active color modules.</para>
/// </summary>
public Color32 startColor

ドキュメントが見つからなかったので断言はできませんが、ParticleSystemはメモリ領域からColor32[ ]を読んでいると考えられます。
そして、メッシュの頂点カラー領域にColor[ ] が設定されていた場合、色が壊れます

16byte カラーを設定したときのメモリ領域

RGBA = (0.5, 0.5, 0.5 ,1.0) という灰色を メッシュのColorに設定したとします。

f:id:r-ngtm:20210119054337p:plain
RGB = (0.5, 0.5, 0.5)

メモリ上では、以下のような32bit Float値が4つ並びます。

f:id:r-ngtm:20210119045127p:plain
Color型は16byteカラー (RGBAがそれぞれ32bit float)

ParticleSystemは32bitカラーとして解釈

ParticleSystemは頂点カラーのことを Color32 だと思っているので、カラー情報を以下のように読み取ります。

f:id:r-ngtm:20210119045904p:plain
32bit color として解釈

1つの16byte カラーは 4つの32bit カラーとして解釈されます。

0.5を 32bit カラーとして解釈された場合

0.5という32bit float のビット列は、以下のような32ケタの2進数になります。

f:id:r-ngtm:20210119050221p:plain
0.5のビット列

ParticleSystemはこれをbyte が4つ並んだRGBAカラーとして解釈し、RGBA = (63, 0, 0, 0) になります。

f:id:r-ngtm:20210119051003p:plain
0.5という値は RGBA = (63, 0, 0, 0) として認識される

RGBA = (63, 0, 0, 0) は画面上では以下のような色として表示されます。

f:id:r-ngtm:20210119052512p:plain
RGB = (63, 0, 0)

0.6 を32bitカラーとして認識した場合

Rチャンネルが0.6だった場合も考えてみます。

0.6という32bit floatのビット列は以下のようになります。

f:id:r-ngtm:20210119051924p:plain
0.6のビット列

このビット列を32bit RGBAカラーとして解釈すると、RGBA = (63, 25, 153, 154) になります。

f:id:r-ngtm:20210119052302p:plain
0.6を32bit RGBAカラーとして解釈した場合

RGBA = (63, 25, 153, 154)は画面上では以下のような色として表示されます。

f:id:r-ngtm:20210119052434p:plain
RGB = (63, 25, 153)


まとめ

Color[] を 頂点カラーに設定すると、想定とは異なる色が表示されてしまうことがわかりました。

メッシュを作成するときは以下のように32bitカラーを設定すれば、色が壊れる現象を回避できます。

mesh.colors32 = colors32; // Color32[]
f:id:r-ngtm:20210119055540p:plain
Colors32 は 4byteカラー(32bitカラー)としてメッシュに保存される