最近在卷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.
给shader添加一个宏,让shader走新的分支
为了之后的Shader操作,我们需要给我们的shader开放两个CustomData端口,分别在Material.cpp跟MaterialAttributeDefinitionMap.cpp文件
接着我们让新的ShadingModel写入GBuffer,这里我们需要修改两个文件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++中的保持一致。
接着我们定义一下debug view下的ShadingModel的颜色
然后我们在Definitions.usf中定义宏的默认值
现在我们在BasePassCommon.ush输出GBuffer
这步之后我们可以在Debug状态下看见我们的ShadingModel,现在我们输出CustomData分别修改DeferredShadingCommon.ush文件与ShadingModelsMaterial.ush
下面就是正菜,我们先新建一个ush专门放新加的函数。
我们下面来到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
最后我们重新编译一下看一下效果