背景を一枚絵にしたい!
やってみました↓
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/ )様
影だけでなく、床に対してキャラの映り込みなどもやりたくなってきますね・・・!
発想としては古典的なテクニックですが、現代の表現と組み合わせればユニークな表現もできそうだし、負荷や制作コストを下げるに収まらず活かしがいがありそうなネタでした。