RenderTextureをRawImageに貼り付けてUIに組み込む処理は定番ですが、煙のようなふんわりとした半透明エフェクトや、髪の毛やガラスのような透明な質感のオブジェクトを描画した際、その境界線がまるで焦げ付いたように不自然に黒ずむバグ(通称: フリンジング、または黒フチ問題)に遭遇することが多々あります。このバグはデジタルカラー合成の基礎であるアルファブレンドの仕組みによって発生します。本記事では、このバグが起きる仕組みを『色ガラスの二重重ね』の例え話を交えて分かりやすく解説し、美しくなめらかなUI表現を実装するための実践的な解決法を提示します。
1. 例え話で理解する:『黒いアクリル板とセロハンテープ』のすれ違い
この現象を直感的に理解するために、『黒いアクリル板の上で透明テープを重ねて切り抜き、白い画用紙へ貼り直す作業』を例えに考えてみましょう。
あなたが透明なセロハンテープ(半透明なエフェクト)に、赤い油性ペンで色を塗ったとします。そして、テープを切る作業を行うための台座として『黒いアクリル板(サブカメラのクリア背景色:透明な黒 #00000000)』の上にテープを貼り付けました。この時点では、アクリル板が黒いため、テープの粘着層や端っこには黒いアクリル板の成分(黒いカラー値)がピッタリと吸着して混ざり合ってしまいます。
次に、このアクリル板から『テープだけを剥がして、白いきれいな画用紙(明るいUI画面)』の上に貼り直そうとします。しかし、テープを剥がした際、テープの薄いエッジ(半透明なフチ)の部分には、台座にしていたアクリル板の『黒い粘着剤の汚れ(黒いピクセル情報)』が一緒にくっついて剥がれてしまっています。この汚れが付いたまま白い画用紙に貼るため、赤いテープのフチの透明度が上がって薄くなるほど、くっついてきた黒い汚れが浮き彫りになり、境界線がドロリと黒ずんだフチとして見えてしまうのです。
これが、『乗算済みアルファ(Premultiplied Alpha)』の不整合による黒ずみバグです。サブカメラがRenderTextureにオブジェクトを描画する際、背景を透明(Alpha=0)に指定してクリアしますが、そのカラー自体は『黒(RGB=0)』であるため、オブジェクトの半透明な境界が描画される際、背景の『黒』とブレンドされてRenderTextureに書き込まれます。その後、UIシステムがそのテクスチャを画面に合成するときに、すでに背景の黒がカラーに混ざり合っていることを想定しない通常のアルファブレンドで合成してしまうため、混ざり込んだ黒がフチに浮かび上がってしまいます。
2. 解決策A:サブカメラのクリアカラーを『透明な白』に変更する
最もお手軽かつノーコードで実行できる解決法は、サブカメラがシーンをクリアする際に使う『透明な背景色』のカラー値を、黒から**『白(または背景UIと同系色)』**に変えることです。
設定手順:
- RenderTextureへ描画を行っているサブカメラのインスペクターを開きます。
- Cameraコンポーネントの『Background Type』を『Solid Color』に設定します。
- 背景色のカラーパレット(Background)を開きます。
- カラーコードを『#FFFFFF』に設定し、アルファチャンネル(A値)を『0(完全透明)』に設定します。
これにより、先ほどの例え話における『台座アクリル板』の色が黒から『透明な白』になります。半透明オブジェクトの境界線に混ざり込む色が『白』になるため、白い画用紙(明るいUI)に貼り付けた際、フチに混ざり合うのは白のカラー値になり、黒ずみが完全に消失し、不自然なダークエッジが出なくなります。
3. 解決策B:乗算済みアルファに対応したUIシェーダーの適用(高度な解決法)
ゲームの背景UIが非常にカラフルであったり、ダークテーマとライトテーマが動的に切り替わる場合、クリアカラーを白にするだけでは完璧ななじみを実現できません。その場合は、UI側でRenderTextureを描画するRawImageに対し、**『乗算済みアルファ(Premultiplied Alpha)を正確にブレンドするカスタムマテリアル』**を割り当てます。
UIの標準シェーダーは `Blend SrcAlpha OneMinusSrcAlpha`(通常のアルファブレンド)で動作しますが、RenderTextureに描画された時点でカラーにすでにアルファが乗算されているため、UI側では `Blend One OneMinusSrcAlpha`(乗算済みアルファブレンド)で描画を行う必要があります。マテリアルを作成し、RawImageの「Material」スロットに適用してください。
// UI用の乗算済みアルファブレンド対応シェーダーの例
Shader "UI/PremultipliedAlphaRawImage"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
// ★超重要:ブレンドモードを One / OneMinusSrcAlpha(乗算済み対応)に設定
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
return OUT;
}
fixed4 frag(v2f IN) : SV_Target
{
// RenderTextureからカラーをサンプリング
fixed4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
return color;
}
ENDCG
}
}
}
このシェーダーを使用したマテリアルをRawImageに適用することで、背景色に関係なく、エッジのアルファブレンドが数学的に正しくなり、輪郭線に黒ずみが一切出ない極めてシャープで美しい3DプレビューUIを実現できます。