Add new Custom Lit shading model for custom lighting in materials (eg. Cel Shading)

This commit is contained in:
Wojtek Figat
2025-06-29 13:51:59 +02:00
parent 3dc7546dd4
commit bdd7bae459
7 changed files with 53 additions and 22 deletions

View File

@@ -28,6 +28,13 @@ TextureCube SkyLightTexture : register(t__SRV__);
Buffer<float4> ShadowsBuffer : register(t__SRV__);
Texture2D<float> ShadowMap : register(t__SRV__);
@4// Forward Shading: Utilities
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
LightData GetDirectionalLight() { return DirectionalLight; }
LightData GetSkyLight() { return SkyLight; }
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
uint GetLocalLightsCount() { return LocalLightsCount; }
LightData GetLocalLight(uint i) { return LocalLights[i]; }
@5// Forward Shading: Shaders
// Pixel Shader function for Forward Pass
@@ -76,9 +83,8 @@ void PS_Forward(
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
// Calculate lighting from a single directional light
float4 shadowMask = 1.0f;
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
shadowMask = GetShadowMask(shadow);
float4 shadowMask = GetShadowMask(shadow);
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
// Calculate lighting from sky light

View File

@@ -124,7 +124,8 @@ namespace FlaxEditor.Surface.Archetypes
case MaterialDomain.Particle:
case MaterialDomain.Deformable:
{
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit;
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit && info.ShadingModel != MaterialShadingModel.CustomLit;
bool isOpaque = info.BlendMode == MaterialBlendMode.Opaque;
bool withTess = info.TessellationMode != TessellationMethod.None;
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
@@ -135,8 +136,8 @@ namespace FlaxEditor.Surface.Archetypes
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || info.BlendMode != MaterialBlendMode.Opaque;
GetBox(MaterialNodeBoxes.Refraction).IsActive = info.BlendMode != MaterialBlendMode.Opaque;
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || !isOpaque;
GetBox(MaterialNodeBoxes.Refraction).IsActive = !isOpaque;
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess;
GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess;

View File

@@ -414,16 +414,18 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Prepare
auto& info = _shaderHeader.Material.Info;
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
const bool isOpaque = info.BlendMode == MaterialBlendMode::Opaque;
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle;
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && !isOpaque) || info.Domain == MaterialDomain::Particle;
const bool useTess =
info.TessellationMode != TessellationMethod::None &&
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
const bool useDistortion =
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
info.BlendMode != MaterialBlendMode::Opaque &&
!isOpaque &&
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
const MaterialShadingModel shadingModel = info.ShadingModel == MaterialShadingModel::CustomLit ? MaterialShadingModel::Unlit : info.ShadingModel;
// @formatter:off
static const char* Numbers[] =
@@ -435,7 +437,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Setup shader macros
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)info.ShadingModel] });
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)shadingModel] });
options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] });
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] });
@@ -492,7 +494,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] });
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && isOpaque ? 1 : 0] });
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
#endif
}

View File

@@ -42,6 +42,8 @@ void DeferredMaterialShader::Bind(BindParameters& params)
// Setup features
const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv);
if (_info.ShadingModel == MaterialShadingModel::CustomLit)
ForwardShadingFeature::Bind(params, cb, srv);
// Setup parameters
MaterialParameter::BindMeta bindMeta;

View File

@@ -103,6 +103,11 @@ API_ENUM() enum class MaterialShadingModel : byte
/// The foliage material. Intended for foliage materials like leaves and grass that need light scattering to transport simulation through the thin object.
/// </summary>
Foliage = 3,
/// <summary>
/// The custom lit shader that calculates own lighting such as Cel Shading. It has access to the scene lights data during both GBuffer and Forward pass rendering.
/// </summary>
CustomLit = 5,
};
/// <summary>

View File

@@ -184,28 +184,29 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
return true; \
} \
}
const bool isOpaque = materialInfo.BlendMode == MaterialBlendMode::Opaque;
switch (baseLayer->Domain)
{
case MaterialDomain::Surface:
if (materialInfo.TessellationMode != TessellationMethod::None)
ADD_FEATURE(TessellationFeature);
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
if (isOpaque)
ADD_FEATURE(MotionVectorsFeature);
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
if (isOpaque)
ADD_FEATURE(LightmapFeature);
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
if (isOpaque)
ADD_FEATURE(DeferredShadingFeature);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None)
if (!isOpaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None)
ADD_FEATURE(DistortionFeature);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination))
if (!isOpaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination))
{
ADD_FEATURE(GlobalIlluminationFeature);
// SDF Reflections is only valid when both GI and SSR is enabled
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::ScreenSpaceReflections))
// SDF Reflections is only valid when both GI and SSR are enabled
if (EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::ScreenSpaceReflections))
ADD_FEATURE(SDFReflectionsFeature);
}
if (materialInfo.BlendMode != MaterialBlendMode::Opaque)
if (materialInfo.BlendMode != MaterialBlendMode::Opaque || materialInfo.ShadingModel == MaterialShadingModel::CustomLit)
ADD_FEATURE(ForwardShadingFeature);
break;
case MaterialDomain::Terrain:
@@ -215,16 +216,16 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
ADD_FEATURE(DeferredShadingFeature);
break;
case MaterialDomain::Particle:
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None)
if (!isOpaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None)
ADD_FEATURE(DistortionFeature);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination))
if (!isOpaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination))
ADD_FEATURE(GlobalIlluminationFeature);
ADD_FEATURE(ForwardShadingFeature);
break;
case MaterialDomain::Deformable:
if (materialInfo.TessellationMode != TessellationMethod::None)
ADD_FEATURE(TessellationFeature);
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
if (isOpaque)
ADD_FEATURE(DeferredShadingFeature);
if (materialInfo.BlendMode != MaterialBlendMode::Opaque)
ADD_FEATURE(ForwardShadingFeature);

View File

@@ -62,8 +62,8 @@ void GetRadialLightAttenuation(
float distanceBiasSqr,
float3 toLight,
float3 L,
inout float NoL,
inout float attenuation)
out float NoL,
out float attenuation)
{
// Distance attenuation
if (lightData.InverseSquared)
@@ -104,6 +104,20 @@ void GetRadialLightAttenuation(
}
}
// Calculates radial light (point or spot) attenuation factors (distance, spot and radius mask)
void GetRadialLightAttenuation(
LightData lightData,
bool isSpotLight,
float3 toLight,
float3 N,
out float NoL,
out float attenuation)
{
float distanceSqr = dot(toLight, toLight);
float3 L = toLight * rsqrt(distanceSqr);
GetRadialLightAttenuation(lightData, isSpotLight, N, distanceSqr, 1, toLight, L, NoL, attenuation);
}
// Find representative incoming light direction and energy modification
float AreaLightSpecular(LightData lightData, float roughness, inout float3 toLight, inout float3 L, float3 V, half3 N)
{