Custom Shading Model In UE5/自定义光照模型

最近在卷UE5的自定义光照模型,所以就写一个笔记,记录一下/Recently, I’ve been experimenting with custom shading models in Unreal Engine 5 (UE5), so I decided to write some notes to keep track of my progress.

分三个模块,1.拉取源代码,2.C++部分添加custom shading model,3.shader 部分/Divide it into three modules: 1. Fetching the source code, 2. Adding the custom shading model in C++, 3. Working on the shader part.

一. 拉取UE源代码/Fetching the source code

主要步骤还是根据UE官方档案来操作 从源代码构建虚幻引擎 | 虚幻引擎文档 (unrealengine.com)/The main steps to build Unreal Engine from source code should be followed based on the official documentation provided by Unreal Engine.

二.C++部分添加Custom Shading Model/To add a custom shading model in C++

首先我们得先让UE知道我们有一套新的Shading Model,能够在光照模型的下拉菜单中找到我们的custom shading model,所以我们先修改EngineTypes.h跟MaterialShader.cpp这两个文件,本质是添加一个新的枚举/

To make Unreal Engine aware of your custom shading model and have it appear in the dropdown menu of the lighting model, you need to modify the EngineTypes.h and MaterialShader.cpp files. The essence of this modification is to add a new enumeration.

EngineTypes.h的头文件中,在EMaterialShadingModel中添加我们CustonShadingModel
在MaterialShader.cpp文件中,在GetShadingModelString(EMaterialShadingModel ShadingModel)中添加

给shader添加一个宏,让shader走新的分支

HLSLMaterialTranslator.cpp

为了之后的Shader操作,我们需要给我们的shader开放两个CustomData端口,分别在Material.cpp跟MaterialAttributeDefinitionMap.cpp文件

Material.cpp中在两个Customdata中增加我们的ShadingModel的名称
在MaterialAttributeDefinitionMap.cpp增加两个定义

接着我们让新的ShadingModel写入GBuffer,这里我们需要修改两个文件ShaderMaterial.h跟ShaderMaterialDerivedHelpers.cpp

在ShaderMaterial.h中添加我们的光照模型
ShaderMaterialDerivedHelpers.cpp中添加
Dst.WRITES_CUSTOMDATA_TO_GBUFFER = (Dst.USES_GBUFFER && (Mat.MATERIAL_SHADINGMODEL_SUBSURFACE || Mat.MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || Mat.MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || Mat.MATERIAL_SHADINGMODEL_CLEAR_COAT || Mat.MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || Mat.MATERIAL_SHADINGMODEL_HAIR || Mat.MATERIAL_SHADINGMODEL_CLOTH || Mat.MATERIAL_SHADINGMODEL_EYE || Mat.MATERIAL_SHADINGMODEL_TOON));

接着我们在ShaderGenerationUtil.cpp中添加生成

至此我们的C++部分修改已经完成,总的来说没什么难度,基本就是照葫芦画瓢,我们编译一下看看引擎

At this point, we have completed the modifications in the C++ part. Overall, it wasn’t difficult; it was mostly a matter of following a similar pattern. Let’s compile the engine now to see if our changes are successful.

看到这几个就相当于添加成功了

接着我们开始修改shader部分

Let’s continue with the modification of the shader part.

首先在ShadingCommon.ush中添加custom Shading Model的宏定义,顺序要和之前C++中的保持一致。

我们一开始定义的ShadingModel在NUM之前,所以在这的顺序不变,顺便NUM的ID也从13变为14

接着我们定义一下debug view下的ShadingModel的颜色

依然是在同一个文件中

然后我们在Definitions.usf中定义宏的默认值

现在我们在BasePassCommon.ush输出GBuffer

在最后加上MATERIAL_SHADINGMODEL_TOON

这步之后我们可以在Debug状态下看见我们的ShadingModel,现在我们输出CustomData分别修改DeferredShadingCommon.ush文件与ShadingModelsMaterial.ush

在DeferredShadingCommon.ush文件中增加我们的新建的ShadingModel的ID

下面就是正菜,我们先新建一个ush专门放新加的函数。

这个ush文件放在Engine/Shaders/Private这个路径下

我们下面来到Shadingmodels.ush这个文件先include我们刚才创建的头文件

接着添加着色算法

//Toon Shading model
FDirectLighting ToonBxDF(FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow)
{
	#if GBUFFER_HAS_TANGENT
		half3 X = GBuffer.WorldTangent;
		half3 Y = normalize(cross(N,X));
	#else	
		half3 X = 0;
		half3 Y = 0;
	#endif
	
	BxDFContext Context;
	Init(Context, N, X, Y, V, L);
	SphereMaxNoH(Context, AreaLight.SphereSinAlpha, true);
	Context.NoV = saturate(abs(Context.NoV) + 1e-5);
	
	float SpecularOffset = 0.5;
	float SpecularRange = GBuffer.CustomData.x;
	
	float3 ShadowColor = 0;
	ShadowColor = GBuffer.DiffuseColor * ShadowColor;
	float offset = GBuffer.CustomData.y;
	float SoftScatterStrength = 0;
	
	offset = offset * 2 - 1;
	half3 H = normalize(V + L);
	float NoH = saturate(dot(N, H));
	NoL = (dot(N, L) + 1) / 2; // overwrite NoL to get more range out of it
	half NoLOffset = saturate(NoL + offset);
	
	FDirectLighting Lighting;
	Lighting.Diffuse = AreaLight.FalloffColor * (smoothstep(0, 1, NoLOffset) * Falloff) * Diffuse_Lambert(GBuffer.DiffuseColor) * 2.2;
	
	float InScatter = pow(saturate(dot(L, -V)), 12) * lerp(3, .1f, 1);
	float NormalContribution = saturate(dot(N, H));
	float BackScatter = GBuffer.GBufferAO * NormalContribution / (PI * 2);
	
	Lighting.Specular = ToonStep(SpecularRange, (saturate(D_GGX(SpecularOffset, NoH)))) * (AreaLight.FalloffColor * GBuffer.SpecularColor * Falloff * 8);

	float3 TransmissionSoft = AreaLight.FalloffColor * (Falloff * lerp(BackScatter, 1, InScatter)) * ShadowColor * SoftScatterStrength;
	float3 ShadowLightener = (saturate(smoothstep(0, 1, saturate(1 - NoLOffset))) * ShadowColor * 0.1);
	
	Lighting.Transmission = (ShadowLightener + TransmissionSoft) * Falloff;
	
	return Lighting;
	
}

			float3 Attenuation = 1;
			BRANCH

			if (GBuffer.ShadingModelID == SHADINGMODELID_TOON)
			{
				float offset = GBuffer.CustomData.y;
				float TerminatorRange = saturate(GBuffer.Roughness - 0.5);
				
				offset = offset * 2 - 1;
				
				BRANCH

				if (offset >= 1)
				{
					Attenuation = 1;
				}
				else
				{
					float NoL = (dot(N, L) + 1) / 2;
					float NoLOffset = saturate(NoL + offset);
					float LightAttenuationOffset = saturate(Shadow.SurfaceShadow + offset);
					float ToonSurfaceShadow = smoothstep(0.5 - TerminatorRange, 0.5 + TerminatorRange, LightAttenuationOffset);

					Attenuation = smoothstep(0.5 - TerminatorRange, 0.5 + TerminatorRange, NoLOffset) * ToonSurfaceShadow;
				}
			}
			//Toon Shading Model
在DefeeredLightingCommon.ush中添加以上代码以及修改以下代码

最后我们重新编译一下看一下效果