背景を一枚絵にしたい!
やってみました↓
2D背景x3Dキャラの組み合わせは初代プレステのRPGでよく見た気がします。
背景に描画コストを高く割けないけど、広い空間を表現したい・・・という目的だったんじゃないかなーと思います。

▲クロノ・クロスの美しい背景
マシンスペックが上がり、3Dモデルの製作ツールも充実してきた現代では見る機会が減りましたが、小規模開発では製作コストを削減するなどのメリットもありそうだなと思い、作ってみました。
実装
まず、新規作成したUnlitシェーダーから不要な処理を削って色を指定するだけのUnlitシェーダーを用意します。
下記シェーダーをベースに処理を書いていきます。
Shader "Custom/TransparentShadowReceive" {
Properties {
_Color ("Color", Color) = (1, 1, 1, 1)
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
fixed4 _Color;
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 col = _Color;
return col;
}
ENDCG
}
}
}
これをPlaneオブジェクトのマテリアルに反映します。

こんな感じのただの白い板として表示されます。まだ影も表示されません。
普通の影を受けるシェーダーにする
次に、影を受けるための最低限の処理を追加していきます。
Tags {
"RenderType"="Opaque"
+ "LightMode"="ForwardBase"
}
LightModeにForwardBaseを指定して、マテリアルがライトの情報を受け取れるようにします。
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
+ #pragma multi_compile_fwdbase
#include "UnityCG.cginc"
+ #include "AutoLight.cginc"
multi_compile_fwdbaseのシェーダーバリアントを指定して、フォワードレンダリングのベース処理をコンパイルします。
また、AutoLight.cgincをincludeして、ライティングを実装するために必要なマクロを使えるようにします。
struct v2f {
- float4 vertex : SV_POSITION;
+ float4 pos : SV_POSITION;
+ SHADOW_COORDS(0)
};
SHADOW_COORDSマクロで、引数に応じたunityShadowCoord4型のTEXCOORDを定義します。
また、SV_POSITIONの名前をposに変えておきます。(理由は後述)
v2f vert (appdata v) {
v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
+ o.pos = UnityObjectToClipPos(v.vertex);
+ TRANSFER_SHADOW(o);
return o;
}
TRANSFER_SHADOWマクロでSHADOW_COORDS内で定義した変数にスクリーンスペースの座標を入れています。
この中の処理でSV_POSITIONの変数名がposが決め打ちとなっているので、v2f構造体のSV_POSITIONの名前はposである必要があります。
fixed4 frag (v2f i) : SV_Target {
fixed4 col = _Color;
+ col.rgb *= LIGHT_ATTENUATION(i);
return col;
}
LIGHT_ATTENUATIONマクロでライトの減衰率を計算します。
影が落ちているところには1が、そうでないところには0が返ってくるため、fragの返り値にそのまま乗算しています。
<備考>
LIGHT_ATTENUATIONの値は、シーン内のLightのStrengthの値によって0〜1で変化します。
また、Soft Shadowが有効なときは、影の境目も0〜1で変化します。
}
ENDCG
}
}
+ Fallback "Diffuse"
}
ライティングに関する実装で、今回は映り込む影のこと以外は何もしないので、FallbackでDiffuseシェーダーの内容に任せています。
これでPlaneオブジェクトに影が投影されるようになりました。
影だけが表示されるシェーダーにする
影が表示されていないところを透明にして、影も透明度が指定できるようにします。
Tags {
- "RenderType"="Opaque"
+ "RenderType"="Transparent"
"LightMode"="ForwardBase"
}
+ Blend SrcAlpha OneMinusSrcAlpha
RenderTypeにTransparentを指定して、半透明でレンダリングできるようにします。
また、Blend SrcAlpha OneMinusSrcAlphaで後ろの表示との合成タイプをしています。
以下、一般的な合成タイプです。
| タイプ | 記述 |
|---|---|
| 昔ながらの透明 | SrcAlpha OneMinusSrcAlpha |
| プリマルチプライドの透明 | One OneMinusSrcAlpha |
| 追加 | One One |
| ソフトな追加 | OneMinusDstColor One |
| 乗算 | DstColor Zero |
| 2x 乗算 | DstColor SrcColor |
詳細:ShaderLab: Blending | Unity マニュアル
fixed4 frag (v2f i) : SV_Target {
fixed4 col = _Color;
- col.rgb *= LIGHT_ATTENUATION(i);
+ col.a *= 1 - LIGHT_ATTENUATION(i);
return col;
}
ライトの減衰率を透明度に反映しています。
ライトの減衰率は影が濃い部分ほど0に近づくため、0〜1の範囲を反転させています。
これで影の部分のみが表示されるようになりました!

_Colorの値で影色と透明度を設定できるため、とりあえず黒にしましょう。

バッチリですね。
背景スプライトの設定
画角やライティングの影響を受けずに背景スプライトを表示するのは、Render ModeをScreen Space - CameraにしたキャンバスにImageコンポーネントを置いて表示されるのが簡単かなと思います。
しかし、上記シェーダーは影を受けるためにZWriteはOffにしていないので、不透明オブジェクトより描画順が後のImageなどのオブジェクトはPlaneオブジェクトより後ろにある時に描画がスキップされてしまいます。

これについては、背景Imageにマテリアルを用意してRender QueueにGeometryを指定することで対処しました。

シェーダー全文
----------------------------------------
TransparentShadowReceive.shader(折りたたみ)
------------------------------------------
Shader "Custom/TransparentShadowReceive" {
Properties {
_Color ("Color", Color) = (0, 0, 0, 1)
}
SubShader {
Tags {
"RenderType"="Transparent"
"LightMode"="ForwardBase"
}
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
SHADOW_COORDS(0)
};
fixed4 _Color;
v2f vert (appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 col = _Color;
col.a *= 1 - LIGHT_ATTENUATION(i);
return col;
}
ENDCG
}
}
Fallback "Diffuse"
}
所感
ノベルゲームのフリー素材背景は、このテクニックを使って3Dのキャラクターやオブジェクトを合成するのに適したアングルのものが多い印象があります。
夕方や夜景など、ライティング環境もさまざまなので、こだわりがいがありそう。

▲from: みんちりえ( https://min-chi.material.jp/ )様
影だけでなく、床に対してキャラの映り込みなどもやりたくなってきますね・・・!
発想としては古典的なテクニックですが、現代の表現と組み合わせればユニークな表現もできそうだし、負荷や制作コストを下げるに収まらず活かしがいがありそうなネタでした。
