Unityで3D化技術を駆使した2Dイラストのライティング検証
「Happy Elements Advent Calendar 2023」 12月6日の記事です。
はじめに
Happy Elements株式会社のカカリアスタジオでゲームエンジニアをしているtommyです。
昨今ゲームのリッチ化が進む中で、2Dイラストでゲームを作る際にもライティングして影を付けてリッチに見せる表現も用いられるようになっています。
今回はカカリアスタジオから今年リリースした「六ツ獄恋いろは」の開発中に、3D化技術を駆使してお手軽にリッチなライティング表現ができないか試みた検証について紹介します。
「六ツ獄恋いろは」は、スマホでカジュアルにあやかしたちとの学園での恋愛を楽しめる恋愛ノベルゲームです。
ゲームシステム自体はよくあるノベルゲームの形式を採用しており、少しリッチな見せ方ができないかを初期の頃に検証していました。
実際には調整の手間や工数の関係でゲームに取り入れるまではいけなかったのですが、アプローチ自体は面白いのではないかと思っておりアドベントカレンダーのネタとして供養させてください。
イラストをライティングするためのアプローチ
今回は「イラストを一度3D化することによってお手軽にいい感じにライティングできないか」というアプローチをしてみました。
具体的には、
- 機械学習でイラストを一度3D化する
- その3Dから法線情報を取得する
- 法線情報を元にイラストにトゥーン調のライティングをする
というのが可能なのではないかと考えて実際に試してみました。
尚、この検証を行ったのはまだChatGPTなどがメジャーになる前の今ほどAIが騒がれていない頃で、現在は3D化技術も色々出てきているかもですが、この検証を行ったときには PIFuHD
が良さそうだったのでそちらを利用しました。
イラストを3D化する
PIFuHD
はややこしい環境構築をしなくてもGoogle Colabでデモを触ることができるようになっています。
早速「六ツ獄恋いろは」に登場する「月読 尊人」のイラストを3D化してみます。
以下のような出力が返ってきました。
基本的にPIFuHD
は写真などで3D化が行われているデモばかりで、イラストでどこまでの精度が出るのか気になっていましたが、このような出来になりました。
足まわりは綺麗ですが、衣装が独特なものなので上半身は細部まではうまく3D化できていないようです。もっとめちゃくちゃになるかと思ったのですが、思いの外大まかな形状はしっかりできてそうだったので、ひとまず検証についてはこれで進めてみることにしました。
3Dから法線情報を取得する
出力された3Dモデルからポリゴンの向きである法線情報を取得します。
数値として扱いやすいように自力で法線ベクトルのxyzをrgbでUnity上で描画して撮影しました。
Shader "Custom/CheckNormal" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = fixed4(v.normal * 0.5 + 0.5, 1);
return o;
}
fixed4 frag(v2f i) : COLOR {
return i.color;
}
ENDCG
}
}
}
作成した画像が以下になります。
法線情報を元にトゥーン調のライティングをする
簡単にトゥーンレンダリングをする方法(と自分が思っている)は、ライトの向きと法線が相対しているときは明、逆のときは暗の2種類の色でパキッと描画するとそれっぽくなるのかなと思っています。
以下のサイトで分かりやすく解説されています。
ということで、ライトの向きと法線ベクトルの内積が正or負で暗or明で描画するシェーダーを書きました。ついでにライトのカラーも乗せられるようにしています(比率は雰囲気)。
Shader "Custom/Character"
{
Properties
{
_MainTex("MainTex", 2D) = "white" {}
_NormalTex("NormalTex", 2D) = "white" {}
}
SubShader
{
Pass
{
Tags
{
"Queue" = "Transparent"
"RenderType" = "Transparent"
"LightMode" = "ForwardBase"
}
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _NormalTex;
uniform float4 _LightColor0;
struct appdata
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 lightDir : TEXCOORD1;
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
return o;
}
float4 frag(v2f i) : COLOR
{
float4 texcolor = tex2D(_MainTex, i.uv);
float4 normalColor = tex2D(_NormalTex, i.uv);
texcolor.rgb = texcolor.rgb * 0.8f + _LightColor0.rgb * 0.2f;
float3 normal = normalColor.xyz * 2 - 1;
half NdotL = dot(i.lightDir, normal);
if(NdotL > 0)
{
texcolor.rgb *= 0.75f + _LightColor0.rgb * 0.25f;
// texcolor.rgb *= 0.75f; ライトを乗せない場合
}
return texcolor;
}
ENDCG
}
}
}
検証結果
-
無加工(昼背景)
-
無加工(夕方背景)
-
ライトカラー&影適用(影色なし)
-
ライトカラー&影適用(影色としてライトカラーを使用)
影色は別途別の色を指定できるようにしても良さそうです。
- ライトを動かしてみる
ある程度は顔の形状を感じられる影の入り方になっているのではないでしょうか。
ただ、変な影の入り方をしている部分もあるので微調整がしたかったり、イラストによっては綺麗に3Dモデルが出力されなかったりもしたので、これで実際にゲームに入れましょうというところまでは至れませんでした。
実際のゲームでは夕方背景などの場合はキャラが背景に馴染むように、均一に赤系の色を乗せる程度に留めています。
おわりに
工数の関係で検証はこれぐらいにしましたが、3Dモデルをスムージングしたり、顔まわりを球面近似するアイデアも見かけたりしたので、ブラッシュアップする方法は多少あるかなとは思っています。
あとは、AIの発展で3D化技術の精度が高くなってくるとこのライティング方法の精度も上がってくるかなと思います。
小規模なゲーム開発でもこのような技術を活用してリッチな表現ができるようになっていくといいですね。
Discussion