UE4のShading Modelを拡張して独自のライティングや描画機能を追加する方法を公開するよ！

先日のアンリアル・フェス 2014では時間の都合やプレゼン形式だったりで詳しく紹介できなかった、Shading Modelを拡張して独自のライティングや描画機能を追加するコードを紹介します。

※ちなみにUE4自体のソースコードを公の場で公開する場合、当該コードについてフォーラム等で議論等する用途においてのみ30行以内ならOKとなっていますことにご留意ください。詳しくは[End User License Agreement](https://www.unrealengine.com/eula)（日本語版は[エンドユーザーライセンス契約書参考訳](https://www.unrealengine.com/ja/eula)）中で「You are permitted to post snippets of Engine Code, up to 30 lines of code in length, online in public forums for the limited purpose of discussing the content of the snippet and not for any other purpose. 」と書かれています。

# UE4のShading Modelとは？

UE4のマテリアル機能で特殊なライティングを使うための機能です。詳しくは以下のドキュメントを参照してください。
[シェーディングモデル](https://docs.unrealengine.com/latest/JPN/Engine/Rendering/Materials/MaterialProperties/LightingModels/index.html "シェーディングモデル")

実際に適用するのはマテリアルのプロパティになります。<br \>
![UE4ShadingModel.png](https://qiita-image-store.s3.amazonaws.com/0/58485/dac9b8ee-4f76-2051-5677-cc2b65976235.png)<br \>
Materialプロパティ中に「Shading Model」というものがあり、マテリアルへのライティングアルゴリズムを選択するものです。<br \>
![UE4LightingModel.png](https://qiita-image-store.s3.amazonaws.com/0/58485/f1aa5dc3-6c96-c84a-aef6-8a1c4ef787a0.png)<br \>
UE4の4.3までは「Unlit」 「Default Lit」「Subsurface」「Preintegrated Skin」が選択できるようになっていまして、Gバッファ群中の「Lighting Model」というバッファに2ビットの情報として書き出されていましたが、8ビットに拡張され256種類まで認識できるようになりましたので、4.3以降「Clear Coat」「Subsurface Profile」が追加されました。

# Shading Modelの追加・拡張にはソースの改変が必要です

GitHubからソースをダウンロードしてビルドする必要があるのですが、ここでその手順を書きだすと長くなってしまうので、ヒストリアさんやDMさんのサイトを見ながら試してみてください。

[UE4 エンジンのソースコード取得とビルド手順のまとめ](http://historia.co.jp/archives/231 "UE4 エンジンのソースコード取得とビルド手順のまとめ")
[UE4のソースコードを落として読んでみた](http://qiita.com/DandyMania/items/423c9723c3e3ca6ee0da "UE4のソースコードを落として読んでみた")

# それでは実際のコード改変

まずは、新たなShadingModelをenum EMaterialShadingModelに追加します。

```Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h
// Note: Check UMaterialInstance::Serialize if changed!!
UENUM()
enum EMaterialShadingModel
{
	：
	MSM_CellLook			UMETA(DisplayName="Cell Look"), // 追加
	MSM_MAX,
};

```

次にC++側でハンドリングするコードの実装。

```Engine/Source/Runtime/Engine/Private/Materials/MaterialShader.cpp
FString GetShadingModelString(EMaterialShadingModel ShadingModel)
{
	FString ShadingModelName;
	switch(ShadingModel)
	{
		：
		case MSM_CellLook:			ShadingModelName = TEXT("MSM_CellLook"); break; // 追加
		default: ShadingModelName = TEXT("Unknown"); break;
	}
	return ShadingModelName;
}

void UpdateMaterialShaderCompilingStats(const FMaterial* Material)
{
	：
	switch(Material->GetShadingModel())
	{
		：
		case MSM_CellLook: // 追加
			INC_DWORD_STAT_BY(STAT_ShaderCompiling_NumLitMaterialShaders,1);
			break;
		default: break;
	};
	：
}

void FMaterial::SetupMaterialEnvironment(
	EShaderPlatform Platform,
	const FUniformExpressionSet& InUniformExpressionSet,
	FShaderCompilerEnvironment& OutEnvironment
	) const
{
	：
	switch(GetShadingModel())
	{
		：
		case MSM_CellLook:			OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_CELL_LOOK"),			TEXT("1")); break; // 追加
		default: 
			UE_LOG(LogMaterial, Warning, TEXT("Unknown material shading model: %u  Setting to MSM_DefaultLit"),(int32)GetShadingModel());
			OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_DEFAULT_LIT"),TEXT("1"));
	};
	：
}
```

最後にシェーダー側に実装。

```Engine/Shaders/DeferredShadingCommon.usf
：
#define SHADINGMODELID_SUBSURFACE_PROFILE	5
#define SHADINGMODELID_CELL_LOOK			6 // 追加
#define SHADINGMODELID_NUM					7
```


```Engine/Shaders/BasePassPixelShader.usf
void Main(
	：
   )
{
	：
		#elif MATERIAL_SHADINGMODEL_CELL_LOOK // 追加
			GBuffer.ShadingModelID = SHADINGMODELID_CELL_LOOK; // 追加
		#else
			// missing shading model, compiler should report ShadingModelID is not set
		#endif
	：
}
```

```Engine/Shaders/DeferredLightingCommon.usf
float3 CellLookShading( FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N, float2 DiffSpecMask )
{
	// 実際にやりたい描画機能を記述。
}

float3 SurfaceShading( FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N, float2 DiffSpecMask )
{
	switch( GBuffer.ShadingModelID )
	{
		：
		case SHADINGMODELID_CELL_LOOK:
			return CellLookShading( GBuffer, LobeRoughness, LobeEnergy, L, V, N, DiffSpecMask);
		default:
			return 0;
	}
}
```

```Engine/Shaders/PostProcessAmbient.usf
float3 CellLookShading( FGBufferData GBuffer, float Roughness, float3 L, float3 V, half3 N )
{
	実際にやりたい描画機能を記述。
}

float3 ImageBasedLightingMIS( FGBufferData GBuffer, float3 V, float3 N, uint2 Random )
{
				：
				else if( GBuffer.ShadingModelID == SHADINGMODELID_CELL_LOOK )
				{
					Shading = CellLookShading( GBuffer, GBuffer.Roughness, L, V, N );
				}
				else
				{
					Shading = StandardShading( GBuffer, GBuffer.Roughness, L, V, N );
				}
			
				Lighting += SampleColor * Shading * ( NoL * Weight );
			}
		}
	}

	return Lighting;
}

```

以上でこんな感じに選べるようになります。<br \>
![UE4ShadingModelCellLook.png](https://qiita-image-store.s3.amazonaws.com/0/58485/e429a9a3-1b74-411d-e31b-cf586885ea89.png)

えっ？何に使うのか？それは皆さんのアイディア次第です！


# UE4のソースからのビルドまでは手をだせないよ・・・

Clear CoatのShading Modelをハックする方法があります。
Engine/Shaders/BasePassPixelShader.usf というシェーダーファイル中の
\#elif MATERIAL_SHADINGMODEL_CLEAR_COAT後のコードと（#else // missing shading model, compiler should report ShadingModelID is not setの前まで）
Engine/Shaders/DeferredLightingCommon.usf というシェーダーファイルの中に float3 ClearCoatShading( FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N )、Engine/Shaders/PostProcessAmbient.usf というシェーダーファイルの中に float3 ClearCoatShading( FGBufferData GBuffer, float Roughness, float3 L, float3 V, half3 N )
 という関数があるので、この中のコードをイジればハックできます。っていきなり敷居が高いなオイ！っていうツッコミは・・・。ハックとはそういうものです。

でも、このコードを変更して、UE4のエディタで「Shift+Ctrl+.（ピリオド）」を押すだけで.usfのコンパイルできますから・・・こういうハックが好きな方には面白い環境だと思います。(^_^;)

# 応用編

今月の後半（12/20）に、応用編として実際にShading Modelで特殊な描画等やってみたいと思います。画像はそれまでヒミツです。お楽しみに！

明日12/11の担当はalweiさん（[@aizen76](https://twitter.com/aizen76 "alweiさん")）で、
タイトルは「UE4で使えるプラグインいろいろ紹介」です！alweiサーン！よろしくお願いします。