# Common ## Common.ush 添加结构体,主要用在材质的CustomNode里。 ```c++ // Used by toon shading. // Define a global custom data structure which can be filled by Custom node in material BP. struct FToonShadingPerMaterialCustomData { // Toon specular float3 ToonSpecularColor; float ToonSpecularLocation; float ToonSpecularSmoothness; // Toon shadow float3 ToonShadowColor; float ToonShadowLocation; float ToonShadowSmoothness; float ToonForceShadow; // Toon secondary shadow float3 ToonSecondaryShadowColor; float ToonSecondaryShadowLocation; float ToonSecondaryShadowSmoothness; // custom data, usually not used float4 CustomData0; float4 CustomData1; float4 CustomData2; float4 CustomData3; }; static FToonShadingPerMaterialCustomData ToonShadingPerMaterialCustomData; ``` ## DeferredShadingCommon.ush 1. 实现[[#Encode/Decode函数]] 2. HasCustomGBufferData()函数添加对应的ToonShadingModel宏判断 3. [[#FGBufferData新增变量]] 4. [[#Encode/Decode GBufferData新增逻辑]] 1. Metallic/Specualr/Roughness => ToonShadowLocation/ToonForceShadow/ToonShadowSmoothness 2. AO => ToonSecondaryShadowLocation 3. CustomData => ToonShadowColor/ToonSecondaryShadowSmoothness 4. PrecomputedShadowFactors => ToonSecondaryShadowColor 5. `#define GBUFFER_REFACTOR 0` 以此关闭自动生成Encode/Decode GBufferData代码,并使用硬编码调用Encode/Decode GBufferData。 6. `#if WRITES_VELOCITY_TO_GBUFFER` => `#if GBUFFER_HAS_VELOCITY`,以此**关闭写入VELOCITY到GBuffer中**。 ### Encode/Decode函数 RGB655 to 8-bit RGB。 将R 256 => 64 ,GB 256 => 32。之后使用2个8bit浮点来存储:通道1存储R与G的头两位;通道2存储G的后3位与B。 ```c++ float2 EncodeColorToRGB655(float3 Color) { const uint ChannelR = (1 << 6) - 1; const uint ChannelG = (1 << 5) - 1; const uint ChannelB = (1 << 5) - 1; uint3 RoundedColor = uint3(float3( round(Color.r * ChannelR), round(Color.g * ChannelG), round(Color.b * ChannelB) )); return float2( (RoundedColor.r << 2 | RoundedColor.g >> 3) / 255.0, (RoundedColor.g << 5 | RoundedColor.b ) / 255.0 ); } float3 DecodeRGB655ToColor(float2 RGB655) { const uint ChannelR = (1 << 6) - 1; const uint ChannelG = (1 << 5) - 1; const uint ChannelB = (1 << 5) - 1; uint2 Inputs = uint2(round(RGB655 * 255.0)); uint BitBuffer = (Inputs.x << 8) | Inputs.y; uint R = (BitBuffer & 0xFC00) >> 10; uint G = (BitBuffer & 0x03E0) >> 5; uint B = (BitBuffer & 0x001F); return float3(R, G, B) * float3(1.0 / ChannelR, 1.0 / ChannelG, 1.0 / ChannelB); } ``` ### FGBufferData新增变量 ```c++ struct FGBufferData { ... // Toon specular // 0..1, specular color half3 ToonSpecularColor; // 0..1, specular edge position half ToonSpecularLocation; // 0..1, specular edge smoothness half ToonSpecularSmoothness; // Toon shadow // 0..1, shadow color half3 ToonShadowColor; // 0..1, shadow egde location half ToonShadowLocation; // 0..1, shadow edge smoothness half ToonShadowSmoothness; // 0..1, force shadow half ToonForceShadow; // Toon secondary shadow // 0..1, secondary shadow color float3 ToonSecondaryShadowColor; // 0..1, secondary shadow edge location float ToonSecondaryShadowLocation; // 0..1, secondary shadow edge smoothness float ToonSecondaryShadowSmoothness; // Toon render half3 ToonCalcShadowColor; }; ``` ### Encode/Decode GBufferData新增逻辑 ```c++ void EncodeGBuffer( FGBufferData GBuffer, out float4 OutGBufferA, out float4 OutGBufferB, out float4 OutGBufferC, out float4 OutGBufferD, out float4 OutGBufferE, out float4 OutGBufferVelocity, float QuantizationBias = 0 // -0.5 to 0.5 random float. Used to bias quantization. ) { ... switch(GBuffer.ShadingModelID) { case SHADINGMODELID_TOON_BASE: OutGBufferB.r = ToonShadingPerMaterialCustomData.ToonShadowLocation; OutGBufferB.g = ToonShadingPerMaterialCustomData.ToonForceShadow; OutGBufferB.b = ToonShadingPerMaterialCustomData.ToonShadowSmoothness; OutGBufferC.a = ToonShadingPerMaterialCustomData.ToonSecondaryShadowLocation; OutGBufferD.a = ToonShadingPerMaterialCustomData.ToonSecondaryShadowSmoothness; OutGBufferD.rgb = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; OutGBufferE.gba = ToonShadingPerMaterialCustomData.ToonSecondaryShadowColor.rgb; break; case SHADINGMODELID_TOON_PBR: OutGBufferB.g = ToonShadingPerMaterialCustomData.ToonShadowLocation; OutGBufferD.a = ToonShadingPerMaterialCustomData.ToonShadowSmoothness; OutGBufferD.rgb = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; OutGBufferE.gba = ToonShadingPerMaterialCustomData.ToonSpecularColor.rgb; break; case SHADINGMODELID_TOON_SKIN: OutGBufferB.r = ToonShadingPerMaterialCustomData.ToonShadowLocation; OutGBufferD.a = ToonShadingPerMaterialCustomData.ToonShadowSmoothness; OutGBufferD.rgb = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; break; default: break; } ... } FGBufferData DecodeGBufferData( float4 InGBufferA, float4 InGBufferB, float4 InGBufferC, float4 InGBufferD, float4 InGBufferE, float4 InGBufferF, float4 InGBufferVelocity, float CustomNativeDepth, uint CustomStencil, float SceneDepth, bool bGetNormalizedNormal, bool bChecker) { FGBufferData GBuffer = (FGBufferData)0; ... switch(GBuffer.ShadingModelID) { case SHADINGMODELID_TOON_BASE: GBuffer.ToonShadowColor = InGBufferD.rgb; GBuffer.ToonShadowLocation = InGBufferB.r; GBuffer.ToonShadowSmoothness = InGBufferB.b; GBuffer.ToonForceShadow = InGBufferB.g; GBuffer.ToonSecondaryShadowColor = InGBufferE.gba; GBuffer.ToonSecondaryShadowLocation = InGBufferC.a; GBuffer.ToonSecondaryShadowSmoothness = InGBufferD.a; GBuffer.Metallic = 0.0; GBuffer.Specular = 1.0; GBuffer.Roughness = 1.0; GBuffer.GBufferAO = 0.0; GBuffer.IndirectIrradiance = 1.0; GBuffer.PrecomputedShadowFactors = !(GBuffer.SelectiveOutputMask & SKIP_PRECSHADOW_MASK) ? float4(InGBufferE.r, 1.0, 1.0, 1.0) : ((GBuffer.SelectiveOutputMask & ZERO_PRECSHADOW_MASK) ? 0 : 1); GBuffer.StoredMetallic = 0.0; GBuffer.StoredSpecular = 1.0; break; case SHADINGMODELID_TOON_PBR: GBuffer.ToonSpecularColor = InGBufferE.gba; GBuffer.ToonShadowColor = InGBufferD.rgb; GBuffer.ToonShadowLocation = InGBufferB.g; GBuffer.ToonShadowSmoothness = InGBufferD.a; GBuffer.ToonSecondaryShadowColor = GBuffer.ToonShadowColor; GBuffer.ToonForceShadow = 1.0; GBuffer.ToonSpecularLocation = 1.0; GBuffer.Specular = 1.0; GBuffer.PrecomputedShadowFactors = !(GBuffer.SelectiveOutputMask & SKIP_PRECSHADOW_MASK) ? float4(InGBufferE.r, 1.0, 1.0, 1.0) : ((GBuffer.SelectiveOutputMask & ZERO_PRECSHADOW_MASK) ? 0 : 1); break; case SHADINGMODELID_TOON_SKIN: GBuffer.ToonShadowColor = InGBufferD.rgb; GBuffer.ToonShadowLocation = InGBufferB.r; GBuffer.ToonShadowSmoothness = InGBufferD.a; GBuffer.ToonSecondaryShadowColor = GBuffer.ToonShadowColor; GBuffer.ToonForceShadow = 1.0; GBuffer.Metallic = 0.0; GBuffer.StoredMetallic = 0.0; GBuffer.PrecomputedShadowFactors = !(GBuffer.SelectiveOutputMask & SKIP_PRECSHADOW_MASK) ? float4(InGBufferE.r, 1.0, 1.0, 1.0) : ((GBuffer.SelectiveOutputMask & ZERO_PRECSHADOW_MASK) ? 0 : 1); break; default: break; } ... }; ``` # BasePass BasePassPixelShader.usf 1. `#if 1` => `#if GBUFFER_REFACTOR && 0`,以此关闭自动生成Encode/Decode GBufferData代码,并使用硬编码调用Encode/Decode GBufferData。 2. 在FPixelShaderInOut_MainPS()中添加写入FGBufferData逻辑。代码如下: ```c++ ... switch(GBuffer.ShadingModelID) { case SHADINGMODELID_TOON_BASE: GBuffer.ToonShadowColor = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; GBuffer.ToonShadowLocation = ToonShadingPerMaterialCustomData.ToonShadowLocation; GBuffer.ToonShadowSmoothness = ToonShadingPerMaterialCustomData.ToonShadowSmoothness; GBuffer.ToonForceShadow = ToonShadingPerMaterialCustomData.ToonForceShadow; GBuffer.ToonSecondaryShadowColor = ToonShadingPerMaterialCustomData.ToonSecondaryShadowColor.rgb; GBuffer.ToonSecondaryShadowLocation = ToonShadingPerMaterialCustomData.ToonSecondaryShadowLocation; GBuffer.ToonSecondaryShadowSmoothness = ToonShadingPerMaterialCustomData.ToonSecondaryShadowSmoothness; GBuffer.Specular = 1.0; GBuffer.GBufferAO = 0.0; GBuffer.PrecomputedShadowFactors.gba = 1; break; case SHADINGMODELID_TOON_PBR: GBuffer.ToonSpecularColor = ToonShadingPerMaterialCustomData.ToonSpecularColor.rgb; GBuffer.ToonShadowColor = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; GBuffer.ToonShadowLocation = ToonShadingPerMaterialCustomData.ToonShadowLocation; GBuffer.ToonShadowSmoothness = ToonShadingPerMaterialCustomData.ToonShadowSmoothness; GBuffer.ToonSecondaryShadowColor = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; GBuffer.ToonForceShadow = 1.0; GBuffer.Specular = 1.0; GBuffer.PrecomputedShadowFactors.gba = 1; break; case SHADINGMODELID_TOON_SKIN: GBuffer.ToonShadowColor = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; GBuffer.ToonShadowLocation = ToonShadingPerMaterialCustomData.ToonShadowLocation; GBuffer.ToonShadowSmoothness = ToonShadingPerMaterialCustomData.ToonShadowSmoothness; GBuffer.ToonSecondaryShadowColor = ToonShadingPerMaterialCustomData.ToonShadowColor.rgb; GBuffer.ToonForceShadow = 1.0; GBuffer.PrecomputedShadowFactors.g = 1; break; default: break; } ... ``` # Lighting ## ShadingModels ### ShadingCommon.ush **添加ShadingModelID宏**: - SHADINGMODELID_TOON_BASE 13 - SHADINGMODELID_TOON_PBR 14 - SHADINGMODELID_TOON_SKIN 15 - SHADINGMODELID_NUM 16 判断是否是IsToonShadingModel: ```c++ bool IsToonShadingModel(uint ShadingModel) { uint4 ToonShadingModels = uint4(SHADINGMODELID_TOON_BASE, SHADINGMODELID_TOON_PBR, SHADINGMODELID_TOON_SKIN, 0xFF); return any(ShadingModel.xxxx == ToonShadingModels); } ``` ## DeferredLightingCommon.ush 修改了AccumulateDynamicLighting()的逻辑。 ```c++ FLightAccumulator AccumulateDynamicLighting( float3 TranslatedWorldPosition, half3 CameraVector, FGBufferData GBuffer, half AmbientOcclusion, uint ShadingModelID, FDeferredLightData LightData, half4 LightAttenuation, float Dither, uint2 SVPos, inout float SurfaceShadow) { FLightAccumulator LightAccumulator = (FLightAccumulator)0; half3 V = -CameraVector; half3 N = GBuffer.WorldNormal; BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT && CLEAR_COAT_BOTTOM_NORMAL) { const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 4) - (512.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal); N = OctahedronToUnitVector(oct1); } float3 L = LightData.Direction; // Already normalized float3 ToLight = L; float3 MaskedLightColor = LightData.Color; float LightMask = 1; if (LightData.bRadialLight) { LightMask = GetLocalLightAttenuation( TranslatedWorldPosition, LightData, ToLight, L ); MaskedLightColor *= LightMask; } 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; const float ContactShadowOpacity = GBuffer.CustomData.a; GetShadowTerms(GBuffer.Depth, GBuffer.PrecomputedShadowFactors, GBuffer.ShadingModelID, ContactShadowOpacity, LightData, TranslatedWorldPosition, L, LightAttenuation, Dither, Shadow); SurfaceShadow = Shadow.SurfaceShadow; LightAccumulator.EstimatedCost += 0.3f; // add the cost of getting the shadow terms #if SHADING_PATH_MOBILE const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID); FDirectLighting Lighting = (FDirectLighting)0; half NoL = max(0, dot(GBuffer.WorldNormal, L)); #if TRANSLUCENCY_NON_DIRECTIONAL NoL = 1.0f; #endif Lighting = EvaluateBxDF(GBuffer, N, V, L, NoL, Shadow); Lighting.Specular *= LightData.SpecularScale; LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation ); LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation ); #else // SHADING_PATH_MOBILE //修改了这里 bool UseToonShadow = IsToonShadingModel(GBuffer.ShadingModelID); BRANCH if( Shadow.SurfaceShadow + Shadow.TransmissionShadow > 0 || UseToonShadow)//修改结束 { const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID); //修改了这里 BRANCH if(UseToonShadow) { float NoL = dot(N, L); float ToonNoL = min(NoL, GBuffer.ToonForceShadow); //合并SurfaceShadow以及Transmision Shadow Shadow.SurfaceShadow = min(Shadow.SurfaceShadow, Shadow.TransmissionShadow); //根据ToonShadowSmoothness、ToonShadowLocation、NoL计算阴影亮度,最后计算主阴影颜色。 float RangeHalf = GBuffer.ToonShadowSmoothness * 0.5; float RangeMin = max(0.0, GBuffer.ToonShadowLocation - RangeHalf); float RangeMax = min(1.0, GBuffer.ToonShadowLocation + RangeHalf); float ShadowIntensity = Shadow.SurfaceShadow * smoothstep(RangeMin, RangeMax, ToonNoL); GBuffer.ToonCalcShadowColor = lerp(GBuffer.ToonShadowColor * LightData.SpecularScale, (1.0).xxx, ShadowIntensity); //计算次级阴影颜色,并最终合成。 RangeHalf = GBuffer.ToonSecondaryShadowSmoothness * 0.5; RangeMin = max(0.0, GBuffer.ToonSecondaryShadowLocation - RangeHalf); RangeMax = min(1.0, GBuffer.ToonSecondaryShadowLocation + RangeHalf); ShadowIntensity = Shadow.SurfaceShadow * smoothstep(RangeMin, RangeMax, ToonNoL); GBuffer.ToonCalcShadowColor = lerp(GBuffer.ToonSecondaryShadowColor * LightData.SpecularScale, GBuffer.ToonCalcShadowColor, ShadowIntensity); } //修改结束 #if NON_DIRECTIONAL_DIRECT_LIGHTING float Lighting; if( LightData.bRectLight ) { FRect Rect = GetRect( ToLight, LightData ); Lighting = IntegrateLight( Rect ); } 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, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation); #else FDirectLighting Lighting; if (LightData.bRectLight) { FRect Rect = GetRect( ToLight, LightData ); const FRectTexture SourceTexture = ConvertToRectTexture(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 } //修改了这里 float SurfaceShadow = UseToonShadow ? 1.0 : Shadow.SurfaceShadow; float TransmissionShadow = UseToonShadow ? 1.0 : Shadow.TransmissionShadow; Lighting.Specular *= UseToonShadow ? GBuffer.ToonSpecularColor : LightData.SpecularScale; LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation ); LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation ); //修改结束 LightAccumulator.EstimatedCost += 0.4f; // add the cost of the lighting computations (should sum up to 1 form one light) #endif } #endif // SHADING_PATH_MOBILE } return LightAccumulator; } ``` ## ShadingModels.ush ```c++ float3 ToonSpecular(float ToonSpecularLocation, float ToonSpecularSmoothness, float3 ToonSpecularColor, float NoL) { float ToonSpecularRangeHalf = ToonSpecularSmoothness * 0.5; float ToonSpecularRangeMin = ToonSpecularLocation - ToonSpecularRangeHalf; float ToonSpecularRangeMax = ToonSpecularLocation + ToonSpecularRangeHalf; return smoothstep(ToonSpecularRangeMin, ToonSpecularRangeMax, NoL) * ToonSpecularColor; } ``` 创建了ToonCustomBxDF(**SHADINGMODELID_TOON_BASE**)与ToonLitBxDF(**SHADINGMODELID_TOON_PBR**、**SHADINGMODELID_TOON_SKIN**)2个ShadingModel函数。 ### ToonCustomBxDF的修改 Diffuse里面乘以之前在DeferredShadingCommon.ush中计算好的ShadowColor(已经计算了NoL) `Lighting.Diffuse *= AreaLight.FalloffColor * (Falloff * NoL);` => `Lighting.Diffuse *= AreaLight.FalloffColor * Falloff * GBuffer.ToonCalcShadowColor;` Speuclar直接归零,具体是在BasePass阶段进行计算了。 `Lighting.Specular = 0;` ### ToonLitBxDF的修改 Diffuse里面乘以之前在DeferredShadingCommon.ush中计算好的ShadowColor(已经计算了NoL) `Lighting.Diffuse *= AreaLight.FalloffColor * (Falloff * NoL);` => `Lighting.Diffuse *= AreaLight.FalloffColor * Falloff * GBuffer.ToonCalcShadowColor;` Speuclar最后乘以了**Shadow.SurfaceShadow** `Lighting.Specular *= Shadow.SurfaceShadow;` ```c++ FDirectLighting ToonLitBxDF( FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, half NoL, FAreaLight AreaLight, FShadowTerms Shadow ) { BxDFContext Context; FDirectLighting Lighting; #if SUPPORTS_ANISOTROPIC_MATERIALS bool bHasAnisotropy = HasAnisotropy(GBuffer.SelectiveOutputMask); #else bool bHasAnisotropy = false; #endif float NoV, VoH, NoH; BRANCH if (bHasAnisotropy) { half3 X = GBuffer.WorldTangent; half3 Y = normalize(cross(N, X)); Init(Context, N, X, Y, V, L); NoV = Context.NoV; VoH = Context.VoH; NoH = Context.NoH; } else { #if SHADING_PATH_MOBILE InitMobile(Context, N, V, L, NoL); #else Init(Context, N, V, L); #endif NoV = Context.NoV; VoH = Context.VoH; NoH = Context.NoH; SphereMaxNoH(Context, AreaLight.SphereSinAlpha, true); } Context.NoV = saturate(abs( Context.NoV ) + 1e-5); #if MATERIAL_ROUGHDIFFUSE // Chan diffuse model with roughness == specular roughness. This is not necessarily a good modelisation of reality because when the mean free path is super small, the diffuse can in fact looks rougher. But this is a start. // Also we cannot use the morphed context maximising NoH as this is causing visual artefact when interpolating rough/smooth diffuse response. Lighting.Diffuse = Diffuse_Chan(GBuffer.DiffuseColor, Pow4(GBuffer.Roughness), NoV, NoL, VoH, NoH, GetAreaLightDiffuseMicroReflWeight(AreaLight)); #else Lighting.Diffuse = Diffuse_Lambert(GBuffer.DiffuseColor); #endif // Toon Diffuse Lighting.Diffuse *= AreaLight.FalloffColor * Falloff * GBuffer.ToonCalcShadowColor; BRANCH if (bHasAnisotropy) { //Lighting.Specular = GBuffer.WorldTangent * .5f + .5f; Lighting.Specular = AreaLight.FalloffColor * (Falloff * NoL) * SpecularGGX(GBuffer.Roughness, GBuffer.Anisotropy, GBuffer.SpecularColor, Context, NoL, AreaLight); } else { if( IsRectLight(AreaLight) ) { Lighting.Specular = RectGGXApproxLTC(GBuffer.Roughness, GBuffer.SpecularColor, N, V, AreaLight.Rect, AreaLight.Texture); } else { // Toon specular Lighting.Specular = AreaLight.FalloffColor * (Falloff * NoL) * SpecularGGX(GBuffer.Roughness, GBuffer.SpecularColor, Context, NoL, AreaLight); } } Lighting.Specular *= Shadow.SurfaceShadow; FBxDFEnergyTerms EnergyTerms = ComputeGGXSpecEnergyTerms(GBuffer.Roughness, Context.NoV, GBuffer.SpecularColor); // Add energy presevation (i.e. attenuation of the specular layer onto the diffuse component Lighting.Diffuse *= ComputeEnergyPreservation(EnergyTerms); // Add specular microfacet multiple scattering term (energy-conservation) Lighting.Specular *= ComputeEnergyConservation(EnergyTerms); Lighting.Transmission = 0; return Lighting; } FDirectLighting ToonCustomBxDF( FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, half NoL, FAreaLight AreaLight, FShadowTerms Shadow ) { BxDFContext Context; FDirectLighting Lighting; float NoV, VoH, NoH; #if SHADING_PATH_MOBILE InitMobile(Context, N, V, L, NoL); #else Init(Context, N, V, L); #endif NoV = Context.NoV; VoH = Context.VoH; NoH = Context.NoH; SphereMaxNoH(Context, AreaLight.SphereSinAlpha, true); Context.NoV = saturate(abs( Context.NoV ) + 1e-5); #if MATERIAL_ROUGHDIFFUSE // Chan diffuse model with roughness == specular roughness. This is not necessarily a good modelisation of reality because when the mean free path is super small, the diffuse can in fact looks rougher. But this is a start. // Also we cannot use the morphed context maximising NoH as this is causing visual artefact when interpolating rough/smooth diffuse response. Lighting.Diffuse = Diffuse_Chan(GBuffer.DiffuseColor, Pow4(GBuffer.Roughness), NoV, NoL, VoH, NoH, GetAreaLightDiffuseMicroReflWeight(AreaLight)); #else Lighting.Diffuse = Diffuse_Lambert(GBuffer.DiffuseColor); #endif // Toon Diffuse Lighting.Diffuse *= AreaLight.FalloffColor * Falloff * GBuffer.ToonCalcShadowColor; // Toon specular // Lighting.Specular = AreaLight.FalloffColor * (Falloff * NoL) * ToonSpecular(GBuffer.ToonSpecularLocation, GBuffer.ToonSpecularSmoothness, GBuffer.ToonSpecularColor, NoL); // Lighting.Specular *= Shadow.SurfaceShadow; // FBxDFEnergyTerms EnergyTerms = ComputeGGXSpecEnergyTerms(GBuffer.Roughness, Context.NoV, GBuffer.SpecularColor); // Add energy presevation (i.e. attenuation of the specular layer onto the diffuse component // Lighting.Diffuse *= ComputeEnergyPreservation(EnergyTerms); Lighting.Specular = 0; Lighting.Transmission = 0; return Lighting; } FDirectLighting IntegrateBxDF( FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, half NoL, FAreaLight AreaLight, FShadowTerms Shadow ) { switch( GBuffer.ShadingModelID ) { case SHADINGMODELID_DEFAULT_LIT: case SHADINGMODELID_SINGLELAYERWATER: case SHADINGMODELID_THIN_TRANSLUCENT: return DefaultLitBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_SUBSURFACE: return SubsurfaceBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_PREINTEGRATED_SKIN: return PreintegratedSkinBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_CLEAR_COAT: return ClearCoatBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_SUBSURFACE_PROFILE: return SubsurfaceProfileBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_TWOSIDED_FOLIAGE: return TwoSidedBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_HAIR: return HairBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_CLOTH: return ClothBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_EYE: return EyeBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_TOON_BASE: return ToonCustomBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); case SHADINGMODELID_TOON_PBR: case SHADINGMODELID_TOON_SKIN: return ToonLitBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow ); default: return (FDirectLighting)0; } } ``` ## DeferredLightPixelShaders.usf 在DeferredLightPixelMain()中添加逻辑: 1. 非卡通材质正常渲染。 2. 材质材质只有在LightingChannel = 2时才会计算卡通光影效果。 ```c++ bool UseToonShadow = IsToonShadingModel(ScreenSpaceData.GBuffer.ShadingModelID); // LightingChannel Toon Shading only calculate light of LightingChannel = 2 BRANCH if (!UseToonShadow || (UseToonShadow && DeferredLightUniforms.LightingChannelMask & 0x4)) { const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV); const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth); FDeferredLightData LightData = InitDeferredLightFromUniforms(CURRENT_LIGHT_TYPE); UpdateLightDataColor(LightData, InputParams, DerivedParams); #if USE_HAIR_COMPLEX_TRANSMITTANCE if (ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_HAIR && ShouldUseHairComplexTransmittance(ScreenSpaceData.GBuffer)) { LightData.HairTransmittance = EvaluateDualScattering(ScreenSpaceData.GBuffer, DerivedParams.CameraVector, -DeferredLightUniforms.Direction); } #endif float Dither = InterleavedGradientNoise(InputParams.PixelPos, View.StateFrameIndexMod8); float SurfaceShadow = 1.0f; float4 LightAttenuation = GetLightAttenuationFromShadow(InputParams, SceneDepth); float4 Radiance = GetDynamicLighting(DerivedParams.TranslatedWorldPosition, DerivedParams.CameraVector, ScreenSpaceData.GBuffer, ScreenSpaceData.AmbientOcclusion, ScreenSpaceData.GBuffer.ShadingModelID, LightData, LightAttenuation, Dither, uint2(InputParams.PixelPos), SurfaceShadow); OutColor += Radiance; } ``` # PostProcess ## ToneMapping c++部分主要修改了: 1. PostProcessing.cpp 2. PostProcessTonemap.cpp 3. PostProcessTonemap.h ***实现向ToneMaper Shader传递 `TRDGUniformBufferRef`的功能*** 之后再PostProcessTonemap.usf中,对**CustomStencil**进行判断,如果为true,则直接返回之前渲染结果。实际上BufferVisualization里根本看不出来。 ```c++ #include "DeferredShadingCommon.ush" // pixel shader entry point void MainPS( in noperspective float2 UV : TEXCOORD0, in noperspective float2 InVignette : TEXCOORD1, in noperspective float4 GrainUV : TEXCOORD2, in noperspective float2 ScreenPos : TEXCOORD3, in noperspective float2 FullViewUV : TEXCOORD4, float4 SvPosition : SV_POSITION, // after all interpolators out float4 OutColor : SV_Target0 #if OUTPUT_LUMINANCE , out float OutLuminance: SV_Target1 #endif ) { float Luminance; FGBufferData SamplerBuffer = GetGBufferData(UV * View.ResolutionFractionAndInv.x, false); if (SamplerBuffer.CustomStencil > 1.0f && abs(SamplerBuffer.CustomDepth - SamplerBuffer.Depth) < 1) { OutColor = SampleSceneColor(UV); } else { OutColor = TonemapCommonPS(UV, InVignette, GrainUV, ScreenPos, FullViewUV, SvPosition, Luminance); } #if OUTPUT_LUMINANCE OutLuminance = Luminance; #endif } ``` ## PostProcessCombineLUT.usf 主要移植了UE4版本的LUT,以此保证效果统一。 # 其他 ## GpuSkinCacheComputeShader.usf 注释2行代码,用处不明。 ```c++ #if GPUSKIN_MORPH_BLEND { Intermediates.UnpackedPosition += Unpacked.DeltaPosition; // calc new normal by offseting it with the delta LocalTangentZ = normalize( LocalTangentZ + Unpacked.DeltaTangentZ); // derive the new tangent by orthonormalizing the new normal against // the base tangent vector (assuming these are normalized) LocalTangentX = normalize( LocalTangentX - (dot(LocalTangentX, LocalTangentZ) * LocalTangentZ) ); }#else #if GPUSKIN_APEX_CLOTH ``` => ```c++ #if GPUSKIN_MORPH_BLEND { Intermediates.UnpackedPosition += Unpacked.DeltaPosition; // calc new normal by offseting it with the delta //LocalTangentZ = normalize( LocalTangentZ + Unpacked.DeltaTangentZ); // derive the new tangent by orthonormalizing the new normal against // the base tangent vector (assuming these are normalized) //LocalTangentX = normalize( LocalTangentX - (dot(LocalTangentX, LocalTangentZ) * LocalTangentZ) ); }#else #if GPUSKIN_APEX_CLOTH ```