28 KiB
Raw Blame History

Common

Common.ush

添加结构体主要用在材质的CustomNode里。

// 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。

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新增变量

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新增逻辑

  
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逻辑。代码如下
...
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:

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()的逻辑。

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

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;  
}

创建了ToonCustomBxDFSHADINGMODELID_TOON_BASE与ToonLitBxDFSHADINGMODELID_TOON_PBRSHADINGMODELID_TOON_SKIN2个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;


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时才会计算卡通光影效果。
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<FSceneTextureUniformParameters>的功能

之后再PostProcessTonemap.usf中CustomStencil进行判断如果为true则直接返回之前渲染结果。实际上BufferVisualization里根本看不出来。

#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行代码用处不明。

#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

=>

#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