|
概述:
本篇只是记录自己学习在UE4中自定义光照模型的一个过程.
在自定义光照模型时,需要分为两个层面:
实践:
C++层:
- EnguneTypes.h
- HLSLMaterialTranslator.cpp
- Material.cpp
1.打开EnguneTypes.h,找到EMaterialShadingModel,可以看到UE默认的光照模型枚举,在下面我们可以添加新的光照模型枚举(此时我们只是添加了一个切换枚举,可以看到材质编辑器下出现了我们自定义的shadingModel):
2.现在切换到自定义光照模型之后,UE也不知道我们要做什么。那么如何让UE知道切换光照模型分支呢?(TODO:材质编辑器是如何压入HLSL的)
找到HLSLMaterialTranslator.cpp,在GetMaterialEnvironment中找到光照模型判断,添加我们自己的宏
3.当切换光照模型之后,我们需要新的接口暴露怎么办呢?
打开Material.cpp,寻找名为IsPropertyActive的函数,它调用的IsPropertyActiveInDerived函数,继续查看,发现真正的定义在IsPropertyActive_Internal函数内。可以暴露激活任意参数,此次测试只暴露customData0、customData1(它们是一组范围为0-1的float值)。
编译。使用我们自定义光照模型会发现customData0、customData1已经激活。至此,C++层面已经修改完毕,回到shader层面。
Shader层面:
- ShandingCommon.ush
- BasePassCommon.ush
- ShadingModelsMaterial.ush
- DeferredShadingCommon.ush
- Shadingmodels.ush
- DeferredLightingCommon.ush
1.在ShandingCommon.ush中定义一个新的宏,做出如下图修改,注意排序。这个宏是告诉我们,模型的shader是使用的哪种光照模型。
在下面可以找到GetShadingModelColor函数,这个函数设置不同光照模型的返回颜色。我们给我们的光照模型定义一个土黄色(0.3f, 0.2f, 0.1f).
做完之后编译,回到编辑器。我们就能在ShadingModle预览模式下看到如下效果:
2.在BasePassCommon.ush中修改宏,允许我们将customData0、customData1写入GBuffer
// Only some shader models actually need custom data.
#define WRITES_CUSTOMDATA_TO_GBUFFER (USES_GBUFFER && (MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || MATERIAL_SHADINGMODEL_CLEAR_COAT || MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || MATERIAL_SHADINGMODEL_HAIR || MATERIAL_SHADINGMODEL_CLOTH || MATERIAL_SHADINGMODEL_EYE || MATERIAL_SHADINGMODEL_NEWTESTSHADINGMODEL)) //begin //end3.再然后,在ShadingModelsMaterial.ush中处理这两个数据
4.进入DeferredShadingCommon.ush,找到HasCustomGBufferData函数,加入我们自定义光照模型的判定(如果这一步不加入,则GBuffer无法读到customData0、customData1)
5.然后在Shadingmodels.ush中对我们的光照模型进行自定义的BRDF。
我直接Copy了大佬的代码,只修改了函数名------源码如下:
float3 CustomStep(float Range, float Input)
{
return smoothstep(0.5 - Range, 0.5 + Range, Input);
}
FDirectLighting NewTestShadingModelBxDF(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 = CustomStep(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 = 0;
ShadowLightener = (saturate(smoothstep(0, 1, saturate(1 - NoLOffset))) * ShadowColor * 0.1);
Lighting.Transmission = (ShadowLightener + TransmissionSoft) * Falloff;
return Lighting;
}在下面找到IntegrateBxDF函数,对上一步我们定义的函数调用:
6.其实到这里,一个基本的自定义光照模型的流程就结束了,为了符合我们这个光照模型(让它更好看),我们最后在DeferredLightingCommon.ush中进行一些修改;
/** Calculates lighting for a given position, normal, etc with a fully featured lighting model designed for quality. */
FDeferredLightingSplit GetDynamicLightingSplit(
float3 WorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID,
FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos, FRectTexture SourceTexture,
inout float SurfaceShadow)
{
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
float3 V = -CameraVector;
float3 N = GBuffer.WorldNormal;
BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT && CLEAR_COAT_BOTTOM_NORMAL)
{
const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 2) - (256.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
N = OctahedronToUnitVector(oct1);
}
float3 L = LightData.Direction; // Already normalized
float3 ToLight = L;
float LightMask = 1;
if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation( WorldPosition, LightData, ToLight, L );
}
LightAccumulator.EstimatedCost += 0.3f; // running the PixelShader at all has a cost
BRANCH
if( LightMask > 0 )
{
FShadowTerms Shadow;
Shadow.SurfaceShadow = AmbientOcclusion;
Shadow.TransmissionShadow = 1;
Shadow.TransmissionThickness = 1;
Shadow.HairTransmittance.OpaqueVisibility = 1;
GetShadowTerms(GBuffer, LightData, WorldPosition, L, LightAttenuation, Dither, Shadow);
SurfaceShadow = Shadow.SurfaceShadow;
LightAccumulator.EstimatedCost += 0.3f; // add the cost of getting the shadow terms
BRANCH
if( Shadow.SurfaceShadow + Shadow.TransmissionShadow > 0 )
{
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
float3 LightColor = LightData.Color;
//begin
float3 Attenuation = 1;
BRANCH
if (GBuffer.ShadingModelID == SHADINGMODELID_NEWTESTSHADINGMODEL)
{
float offset = GBuffer.CustomData.b;
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;
}
}
//end
#if NON_DIRECTIONAL_DIRECT_LIGHTING
float Lighting;
if( LightData.bRectLight )
{
FRect Rect = GetRect( ToLight, LightData );
Lighting = IntegrateLight( Rect, SourceTexture);
}
else
{
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
Lighting = IntegrateLight( Capsule, LightData.bInverseSquared );
}
float3 LightingDiffuse = Diffuse_Lambert( GBuffer.DiffuseColor ) * Lighting;
LightAccumulator_AddSplit(LightAccumulator, LightingDiffuse, 0.0f, 0, LightColor * LightMask * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation);
#else
FDirectLighting Lighting;
if (LightData.bRectLight)
{
FRect Rect = GetRect( ToLight, LightData );
#if REFERENCE_QUALITY
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture, SVPos );
#else
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture);
#endif
}
else
{
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
#if REFERENCE_QUALITY
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, SVPos );
#else
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );
#endif
}
Lighting.Specular *= LightData.SpecularScale;
//begin
BRANCH
if (GBuffer.ShadingModelID == SHADINGMODELID_NEWTESTSHADINGMODEL)
{
LightAccumulator_AddSplit(LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * Shadow.SurfaceShadow * Attenuation * 0.25, bNeedsSeparateSubsurfaceLightAccumulation);
}
else
{
LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
}
//end
LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, LightColor * LightMask * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator.EstimatedCost += 0.4f; // add the cost of the lighting computations (should sum up to 1 form one light)
#endif
}
}
return LightAccumulator_GetResultSplit(LightAccumulator);
}完成。
效果:
编译,让我们看看编辑器里的效果(多灯光测试)。
Reference:
[UE4] Custom Shading Model
虚幻4渲染编程(材质编辑器篇)【第二卷:自定义光照模型】
UE4(虚幻)学习笔记--虚幻4.26自定义shadermodel
Unreal Engine 4 Rendering Part 6: Adding a new Shading Model |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|