539 lines
23 KiB
Markdown
539 lines
23 KiB
Markdown
---
|
||
title: GBuffer&Material&BasePass
|
||
date: 2023-12-08 17:34:58
|
||
excerpt:
|
||
tags:
|
||
rating: ⭐
|
||
---
|
||
|
||
# # GBuffer
|
||
目前UE5.3会调用
|
||
- WriteGBufferInfoAutogen()
|
||
- **EncodeGBufferToMRT()**
|
||
动态生成BasePassPixelShader.usf中的**EncodeGBufferToMRT()** 的代码,并且会生成一个AutogenShaderHeaders.ush文件。其路径为:
|
||
`Engine\Intermediate\ShaderAutogen\PCD3D_SM5`或者`Engine\Intermediate\ShaderAutogen\PCD3D_ES3_1`
|
||
|
||
1. ***给FGBufferData添加结构体数据时需要在此添加额外代码逻辑***
|
||
2. GBuffer精度在FetchLegacyGBufferInfo()设置。
|
||
3. 是否往GBuffer中写入Velocity,主要靠这个宏**WRITES_VELOCITY_TO_GBUFFER**。具体决定其数值的逻辑位于**FShaderGlobalDefines FetchShaderGlobalDefines**。主要还是靠**r.VelocityOutputPass**进行开启。
|
||
1. PS. MSAA以及VR绝对不会开启Velocity输出选项。还有就是**r.Velocity.ForceOutput**,但经过测试不开启r.VelocityOutputPass依然无法输出。以及FPrimitiveSceneProxy的bAlwaysHasVelocity与bHasWorldPositionOffsetVelocity。
|
||
2. 其他相关FSR、TSR?
|
||
4. 如何添加GBuffer
|
||
1. https://zhuanlan.zhihu.com/p/568775542
|
||
2. https://zhuanlan.zhihu.com/p/677772284
|
||
|
||
## UE5 GBuffer内容:
|
||
[[UE GBuffer存储数据]]
|
||
```c#
|
||
OutGBufferA(MRT1) = WorldNormal/PerObjectGBufferData (GBT_Float_16_16_16_16/GBT_Unorm_11_11_10/GBT_Unorm_8_8_8_8)
|
||
OutGBufferB(MRT2) = Metallic/Specular/Roughness/EncodeShadingModelIdAndSelectiveOutputMask (GBT_Float_16_16_16_16/GBT_Unorm_8_8_8_8)
|
||
OutGBufferC(MRT3) = BaseColor/GBufferAO (GBT_Unorm_8_8_8_8)
|
||
OutGBufferD = GBuffer.CustomData (GBT_Unorm_8_8_8_8)
|
||
OutGBufferE = GBuffer.PrecomputedShadowFactors (GBT_Unorm_8_8_8_8)
|
||
TargetVelocity / OutGBufferF = velocity / tangent (默认不开启 带有深度<开启Lumen与距离场 或者 开启光线追踪> GBC_Raw_Float_16_16_16_16 不带深度 GBC_Raw_Float_16_16)
|
||
TargetSeparatedMainDirLight = SingleLayerWater相关 (有SingleLayerWater才会开启 GBC_Raw_Float_11_11_10)
|
||
|
||
// 0..1, 2 bits, use CastContactShadow(GBuffer) or HasDynamicIndirectShadowCasterRepresentation(GBuffer) to extract
|
||
half PerObjectGBufferData;
|
||
```
|
||
GBuffer相关信息(精度、顺序)可以参考FetchLegacyGBufferInfo()。
|
||
- 不存在Velocity与Tangent:
|
||
- OutGBufferD(MRT4)
|
||
- OutGBufferD(MRT5)
|
||
- TargetSeparatedMainDirLight(MRT6)
|
||
- 存在Velocity:
|
||
- TargetVelocity(MRT4)
|
||
- OutGBufferD(MRT5)
|
||
- OutGBufferE(MRT6)
|
||
- TargetSeparatedMainDirLight(MRT7)
|
||
- 存在Tangent:
|
||
- OutGBufferF(MRT4)
|
||
- OutGBufferD(MRT5)
|
||
- OutGBufferE(MRT6)
|
||
- TargetSeparatedMainDirLight(MRT7)
|
||
|
||
几个动态MRT的存在条件与Shader判断宏:
|
||
- OutGBufferE(PrecomputedShadowFactors):r.AllowStaticLighting = 1
|
||
- GBUFFER_HAS_PRECSHADOWFACTOR
|
||
- WRITES_PRECSHADOWFACTOR_ZERO
|
||
- WRITES_PRECSHADOWFACTOR_TO_GBUFFER
|
||
- TargetVelocity:(IsUsingBasePassVelocity(Platform) || Layout == GBL_ForceVelocity) ? 1 : 0;//r.VelocityOutputPass = 1
|
||
- r.VelocityOutputPass = 1时,会对骨骼物体以及WPO材质物体输出速度。因为大概率会使用距离场阴影以及VSM,所以会占用GBuffer Velocity所有通道。
|
||
- GBUFFER_HAS_VELOCITY
|
||
- WRITES_VELOCITY_TO_GBUFFER
|
||
- SingleLayerWater
|
||
- 默认不会写入GBuffer需要符合以下条件:const bool bNeedsSeparateMainDirLightTexture = IsWaterDistanceFieldShadowEnabled(Parameters.Platform) || IsWaterVirtualShadowMapFilteringEnabled(Parameters.Platform);
|
||
- r.Water.SingleLayer.ShadersSupportDistanceFieldShadow = 1
|
||
- r.Water.SingleLayer.ShadersSupportVSMFiltering = 1
|
||
- const bool bIsSingleLayerWater = Parameters.MaterialParameters.ShadingModels.HasShadingModel(MSM_SingleLayerWater);
|
||
- Tangent:false,目前单独使用另一组MRT来存储。
|
||
- ~~GBUFFER_HAS_TANGENT~`
|
||
|
||
### ToonGBuffer修改&数据存储
|
||
```c#
|
||
OutGBufferA:PerObjectGBufferData => 可以存储额外的有关Tonn渲染功能参数。
|
||
OutGBufferB:Metallic/Specular/Roughness =>
|
||
? / SpcularPower(控制高光亮度与Mask) / ? / ?
|
||
//ToonHairMask OffsetShadowMask/SpcularMask/SpecularValue
|
||
OutGBufferC:GBufferAO =>
|
||
ToonAO
|
||
OutGBufferD:CustomData.xyzw =>
|
||
ShadowColor.rgb / NoLOffset //ShadowColor这里可以在Material里通过主光向量、ShadowStep、Shadow羽化计算多层阴影效果。
|
||
OutGBufferE:GBuffer.PrecomputedShadowFactors.xyzw =>
|
||
ToonDataID/ ToonOutlineDataID / OutlineMask(控制Outline绘制以及Outline强度) / ToonObjectID(判断是否是一个物体)
|
||
TargetVelocity / OutGBufferF = velocity / tangent //目前先不考虑输出Velocity的情况
|
||
? / ? / ? / ?
|
||
```
|
||
|
||
ToonDataID在材质编辑器中会存在SubsurfaceColor.a中,ToonOutlineDataID在材质编辑器中会存在CustomData1(引脚名为ToonBufferB,考虑到Subsurface有一个CurvatureMap需要使用CustomData0,所以这里使用了CustomData1)。
|
||
|
||
|
||
蓝色协议的方案
|
||
![[蓝色协议的方案#GBuffer]]
|
||
|
||
***额外添加相关宏(逻辑位于ShaderCompiler.cpp)***
|
||
- **GBUFFER_HAS_TOONDATA**
|
||
|
||
### 修改GBuffer格式
|
||
- [[#ShaderMaterialDerivedHelpers.cpp中的CalculateDerivedMaterialParameters()]]控制在BasePassPixelShader.usf中的MRT宏是否为true。
|
||
- [[#BasePassRendering.cpp中ModifyBasePassCSPSCompilationEnvironment()]]控制Velocity与SingleLayerWater相关的RT精度。
|
||
- [[#GBufferInfo.cpp中的FetchLegacyGBufferInfo()]]控制GBuffer精度以及数据打包情况。
|
||
|
||
#### BasePassRendering.cpp中ModifyBasePassCSPSCompilationEnvironment()
|
||
```c++
|
||
void ModifyBasePassCSPSCompilationEnvironment()
|
||
{
|
||
...
|
||
const bool bOutputVelocity = (GBufferLayout == GBL_ForceVelocity) ||
|
||
FVelocityRendering::BasePassCanOutputVelocity(Parameters.Platform);
|
||
if (bOutputVelocity)
|
||
{
|
||
// As defined in BasePassPixelShader.usf. Also account for Strata setting velocity in slot 1 as described in FetchLegacyGBufferInfo.
|
||
const int32 VelocityIndex = Strata::IsStrataEnabled() ? 1 : (IsForwardShadingEnabled(Parameters.Platform) ? 1 : 4);
|
||
OutEnvironment.SetRenderTargetOutputFormat(VelocityIndex, PF_G16R16);
|
||
}
|
||
...
|
||
const bool bNeedsSeparateMainDirLightTexture = IsWaterDistanceFieldShadowEnabled(Parameters.Platform) || IsWaterVirtualShadowMapFilteringEnabled(Parameters.Platform);
|
||
if (bIsSingleLayerWater && bNeedsSeparateMainDirLightTexture)
|
||
{
|
||
// See FShaderCompileUtilities::FetchGBufferParamsRuntime for the details
|
||
const bool bHasTangent = false;
|
||
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
|
||
bool bHasPrecShadowFactor = (CVar ? (CVar->GetValueOnAnyThread() != 0) : 1);
|
||
|
||
uint32 TargetSeparatedMainDirLight = 5;
|
||
if (bOutputVelocity == false && bHasTangent == false)
|
||
{
|
||
TargetSeparatedMainDirLight = 5;
|
||
if (bHasPrecShadowFactor)
|
||
{
|
||
TargetSeparatedMainDirLight = 6;
|
||
}
|
||
}
|
||
else if (bOutputVelocity)
|
||
{
|
||
TargetSeparatedMainDirLight = 6;
|
||
if (bHasPrecShadowFactor)
|
||
{
|
||
TargetSeparatedMainDirLight = 7;
|
||
}
|
||
}
|
||
else if (bHasTangent)
|
||
{
|
||
TargetSeparatedMainDirLight = 6;
|
||
if (bHasPrecShadowFactor)
|
||
{
|
||
TargetSeparatedMainDirLight = 7;
|
||
}
|
||
}
|
||
OutEnvironment.SetRenderTargetOutputFormat(TargetSeparatedMainDirLight, PF_FloatR11G11B10);
|
||
...
|
||
}
|
||
```
|
||
|
||
#### GBufferInfo.cpp中的FetchLegacyGBufferInfo()
|
||
控制GBuffer精度以及数据打包情况。
|
||
|
||
#### ShaderMaterialDerivedHelpers.cpp中的CalculateDerivedMaterialParameters()
|
||
```c++
|
||
else if (Mat.IS_BASE_PASS)
|
||
{
|
||
Dst.PIXELSHADEROUTPUT_BASEPASS = 1;
|
||
if (Dst.USES_GBUFFER)
|
||
{
|
||
Dst.PIXELSHADEROUTPUT_MRT0 = (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || Dst.NEEDS_BASEPASS_VERTEX_FOGGING || Mat.USES_EMISSIVE_COLOR || SrcGlobal.ALLOW_STATIC_LIGHTING || Mat.MATERIAL_SHADINGMODEL_SINGLELAYERWATER);
|
||
Dst.PIXELSHADEROUTPUT_MRT1 = ((!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || !Mat.MATERIAL_SHADINGMODEL_UNLIT));
|
||
Dst.PIXELSHADEROUTPUT_MRT2 = ((!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || !Mat.MATERIAL_SHADINGMODEL_UNLIT));
|
||
Dst.PIXELSHADEROUTPUT_MRT3 = ((!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || !Mat.MATERIAL_SHADINGMODEL_UNLIT));
|
||
if (SrcGlobal.GBUFFER_HAS_VELOCITY || SrcGlobal.GBUFFER_HAS_TANGENT)
|
||
{
|
||
Dst.PIXELSHADEROUTPUT_MRT4 = Dst.WRITES_VELOCITY_TO_GBUFFER || SrcGlobal.GBUFFER_HAS_TANGENT;
|
||
Dst.PIXELSHADEROUTPUT_MRT5 = (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || Dst.WRITES_CUSTOMDATA_TO_GBUFFER);
|
||
Dst.PIXELSHADEROUTPUT_MRT6 = (Dst.GBUFFER_HAS_PRECSHADOWFACTOR && (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || (Dst.WRITES_PRECSHADOWFACTOR_TO_GBUFFER && !Mat.MATERIAL_SHADINGMODEL_UNLIT)));
|
||
}
|
||
else
|
||
{
|
||
Dst.PIXELSHADEROUTPUT_MRT4 = (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || Dst.WRITES_CUSTOMDATA_TO_GBUFFER);
|
||
Dst.PIXELSHADEROUTPUT_MRT5 = (Dst.GBUFFER_HAS_PRECSHADOWFACTOR && (!SrcGlobal.SELECTIVE_BASEPASS_OUTPUTS || (Dst.WRITES_PRECSHADOWFACTOR_TO_GBUFFER && !Mat.MATERIAL_SHADINGMODEL_UNLIT)));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Dst.PIXELSHADEROUTPUT_MRT0 = true;
|
||
// we also need MRT for thin translucency due to dual blending if we are not on the fallback path
|
||
Dst.PIXELSHADEROUTPUT_MRT1 = (Dst.WRITES_VELOCITY_TO_GBUFFER || (Mat.DUAL_SOURCE_COLOR_BLENDING_ENABLED && Dst.MATERIAL_WORKS_WITH_DUAL_SOURCE_COLOR_BLENDING));
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
位于FShaderCompileUtilities::ApplyDerivedDefines(),新版本逻辑遍历数据由GBufferInfo.cpp中的FetchLegacyGBufferInfo()处理。
|
||
```c++
|
||
#if 1
|
||
static bool bTestNewVersion = true;
|
||
if (bTestNewVersion)
|
||
{
|
||
//if (DerivedDefines.USES_GBUFFER)
|
||
{
|
||
for (int32 Iter = 0; Iter < FGBufferInfo::MaxTargets; Iter++)
|
||
{
|
||
if (bTargetUsage[Iter])
|
||
{
|
||
FString TargetName = FString::Printf(TEXT("PIXELSHADEROUTPUT_MRT%d"), Iter);
|
||
OutEnvironment.SetDefine(TargetName.GetCharArray().GetData(), TEXT("1"));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// This uses the legacy logic from CalculateDerivedMaterialParameters(); Just keeping it around momentarily for testing during the transition.
|
||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT0)
|
||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT1)
|
||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT2)
|
||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT3)
|
||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT4)
|
||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT5)
|
||
SET_COMPILE_BOOL_IF_TRUE(PIXELSHADEROUTPUT_MRT6)
|
||
}
|
||
#endif
|
||
```
|
||
|
||
### MaterialTemplate.ush
|
||
MaterialTemplate.ush中定义许多模版函数,里面的具体内容会在HLSLMaterialTranslator.h中的**GetMaterialShaderCode()** 中添加。最后这些函数会在BassPassPixelShader.usf中调用。
|
||
|
||
bool bEnableExecutionFlow的作用为是否使用新的材质HLSL生成器,默认为0。
|
||
```c++
|
||
static TAutoConsoleVariable<int32> CVarMaterialEnableNewHLSLGenerator(
|
||
TEXT("r.MaterialEnableNewHLSLGenerator"),
|
||
0,
|
||
TEXT("Enables the new (WIP) material HLSL generator.\n")
|
||
TEXT("0 - Don't allow\n")
|
||
TEXT("1 - Allow if enabled by material\n")
|
||
TEXT("2 - Force all materials to use new generator\n"),
|
||
ECVF_RenderThreadSafe | ECVF_ReadOnly);
|
||
```
|
||
|
||
bCompileForComputeShader = Material->IsLightFunction();
|
||
|
||
|
||
GetPerInstanceCustomDataX分为Vertex与Pixel版本。
|
||
|
||
#### FMaterialAttributes
|
||
MaterialTemplate.ush有一处`/** Material declarations */`之后会生成对应FMaterialAttributes结构体,可以在材质编辑器的HLSL中查看生成结果。这与
|
||
- MaterialAttributeDefinitionMap.cpp:FMaterialAttributeDefinitionMap::InitializeAttributeMap()中定义属性。
|
||
- HLSLMaterialTranslator.cpp:GetMaterialShaderCode()中的`for (const FGuid& AttributeID : OrderedVisibleAttributes)`:生成对应属性结构体以及属性获取函数。
|
||
|
||
#### DerivativeAutogen.GenerateUsedFunctions()
|
||
```c++
|
||
{
|
||
FString DerivativeHelpers = DerivativeAutogen.GenerateUsedFunctions(*this);
|
||
FString DerivativeHelpersAndResources = DerivativeHelpers + ResourcesString;
|
||
//LazyPrintf.PushParam(*ResourcesString);
|
||
LazyPrintf.PushParam(*DerivativeHelpersAndResources);
|
||
}
|
||
```
|
||
|
||
#### GetMaterialEmissiveForCS()以及其他函数
|
||
```c++
|
||
if (bCompileForComputeShader)
|
||
{
|
||
LazyPrintf.PushParam(*GenerateFunctionCode(CompiledMP_EmissiveColorCS, BaseDerivativeVariation));
|
||
}
|
||
else
|
||
{
|
||
LazyPrintf.PushParam(TEXT("return 0"));
|
||
}
|
||
|
||
{
|
||
FLinearColor Extinction = Material->GetTranslucentMultipleScatteringExtinction();
|
||
LazyPrintf.PushParam(*FString::Printf(TEXT("return MaterialFloat3(%.5f, %.5f, %.5f)"), Extinction.R, Extinction.G, Extinction.B));
|
||
}
|
||
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetOpacityMaskClipValue()));
|
||
{
|
||
const FDisplacementScaling DisplacementScaling = Material->GetDisplacementScaling();
|
||
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), FMath::Max(0.0f, DisplacementScaling.Magnitude)));
|
||
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), FMath::Clamp(DisplacementScaling.Center, 0.0f, 1.0f)));
|
||
}
|
||
|
||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_WorldPositionOffset, BaseDerivativeVariation) : TEXT("return Parameters.MaterialAttributes.WorldPositionOffset"));
|
||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(CompiledMP_PrevWorldPositionOffset, BaseDerivativeVariation) : TEXT("return 0.0f"));
|
||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_CustomData0, BaseDerivativeVariation) : TEXT("return 0.0f"));
|
||
LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_CustomData1, BaseDerivativeVariation) : TEXT("return 0.0f"));
|
||
```
|
||
%.5f:表示按浮点数输出,小数点后面取5位其余的舍弃;例如:5/2 “%.5f”输出为:2.50000
|
||
|
||
#### MaterialCustomizedUVs & CustomInterpolators
|
||
- `for (uint32 CustomUVIndex = 0; CustomUVIndex < NumUserTexCoords; CustomUVIndex++)`
|
||
- `for (UMaterialExpressionVertexInterpolator* Interpolator : CustomVertexInterpolators`
|
||
|
||
### 添加ToonDataAssetID 与 ToonOutlineDataAssetID笔记
|
||
1. FMaterialRenderProxy::UpdateDeferredCachedUniformExpressions()
|
||
2. FMaterialRenderProxy::EvaluateUniformExpressions()
|
||
3. FUniformExpressionSet::FillUniformBuffer()
|
||
4. EvaluatePreshader()
|
||
5. EvaluateParameter()
|
||
6. Context.MaterialRenderProxy->GetParameterValue()
|
||
|
||
可以看得出关键数据在UniformExpressionSet中,这里的ParameterIndex则通过`EvaluateParameter(Stack, UniformExpressionSet, ReadPreshaderValue<uint16>(Data), Context);`进行计算。
|
||
```c++
|
||
const FMaterialNumericParameterInfo& Parameter = UniformExpressionSet->GetNumericParameter(ParameterIndex);
|
||
bool bFoundParameter = false;
|
||
|
||
// First allow proxy the chance to override parameter
|
||
if (Context.MaterialRenderProxy)
|
||
{
|
||
FMaterialParameterValue ParameterValue;
|
||
if (Context.MaterialRenderProxy->GetParameterValue(Parameter.ParameterType, Parameter.ParameterInfo, ParameterValue, Context))
|
||
{
|
||
Stack.PushValue(ParameterValue.AsShaderValue());
|
||
bFoundParameter = true;
|
||
}
|
||
}
|
||
|
||
bool FMaterialInstanceResource::GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue, const FMaterialRenderContext& Context) const
|
||
{
|
||
checkSlow(IsInParallelRenderingThread());
|
||
|
||
bool bResult = false;
|
||
|
||
// Check for hard-coded parameters
|
||
if (Type == EMaterialParameterType::Scalar && ParameterInfo.Name == GetSubsurfaceProfileParameterName())
|
||
{
|
||
check(ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter);
|
||
const USubsurfaceProfile* MySubsurfaceProfileRT = GetSubsurfaceProfileRT();
|
||
OutValue = GetSubsurfaceProfileId(MySubsurfaceProfileRT);
|
||
bResult = true;
|
||
}
|
||
else if (Type == EMaterialParameterType::Scalar && NumSpecularProfileRT() > 0)
|
||
{
|
||
for (uint32 It=0,Count=NumSpecularProfileRT();It<Count;++It)
|
||
{
|
||
if (ParameterInfo.Name == SpecularProfileAtlas::GetSpecularProfileParameterName(GetSpecularProfileRT(It)))
|
||
{
|
||
check(ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter);
|
||
OutValue = SpecularProfileAtlas::GetSpecularProfileId(GetSpecularProfileRT(It));
|
||
bResult = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 是否需要Toon
|
||
在材质中:
|
||
```c++
|
||
FMaterialRelevance UMaterialInterface::GetRelevance_Internal(const UMaterial* Material, ERHIFeatureLevel::Type InFeatureLevel) const
|
||
{
|
||
if(Material)
|
||
{
|
||
//YivanLee's Modify 这里仅仅针对人物,因为它决定了是否开启ToonGBuffer,但是对于ToonLevel,ToonFoliage,ToonGrass这里并不需要开启
|
||
bool bUseToonData = MaterialResource->GetShadingModels().HasAnyShadingModel({ MSM_ToonStandard, MSM_ToonSkin, MSM_ToonHair, MSM_ToonFace, MSM_ToonEyeBrow });
|
||
}
|
||
|
||
···
|
||
MaterialRelevance.bUsesToonData = bUseToonData;
|
||
···
|
||
}
|
||
```
|
||
|
||
在渲染管线中:
|
||
```c++
|
||
//RenderUtils.cpp
|
||
bool IsUsingToonRendering(const FStaticShaderPlatform Platform)
|
||
{
|
||
static FShaderPlatformCachedIniValue<int32> PerPlatformCVar(TEXT("r.ToonRendering.Enable"));
|
||
if (IsMobilePlatform(Platform) || IsForwardShadingEnabled(Platform))//目前不考虑VR与移动端
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
return (PerPlatformCVar.Get(Platform) == 1);
|
||
}
|
||
}
|
||
|
||
bool IsUsingToonOutline(const FStaticShaderPlatform Platform)
|
||
{
|
||
static FShaderPlatformCachedIniValue<int32> PerPlatformCVar(TEXT("r.ToonRendering.ToonOutline"));
|
||
return (PerPlatformCVar.Get(Platform) == 1) && IsUsingToonRendering(Platform);
|
||
}
|
||
|
||
bool IsUsingToonRimLighting(const FStaticShaderPlatform Platform)
|
||
{
|
||
static FShaderPlatformCachedIniValue<int32> PerPlatformCVar(TEXT("r.ToonRendering.ToonRimLighting"));
|
||
return (PerPlatformCVar.Get(Platform) == 1) && IsUsingToonRendering(Platform);
|
||
}
|
||
```
|
||
|
||
|
||
李兄的ToonBuffer判断逻辑:
|
||
```c++
|
||
bool FDeferredShadingSceneRenderer::ShouldRenderToonDataPass() const
|
||
{
|
||
if (!SupportsToonDataMaterials(FeatureLevel, ShaderPlatform))
|
||
{
|
||
return false;
|
||
}
|
||
if (IsForwardShadingEnabled(GetFeatureLevelShaderPlatform(FeatureLevel)))
|
||
{
|
||
return false;
|
||
}
|
||
for (auto& View : Views)
|
||
{
|
||
if (View.ShouldRenderView() && View.ParallelMeshDrawCommandPasses[EMeshPass::ToonDataPass].HasAnyDraw())
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
```
|
||
|
||
## Toon PerObjectGBufferData具体功能表
|
||
从3开始,0、1、2已被占用。
|
||
- ?
|
||
|
||
## ToonData
|
||
|
||
|
||
## 高光
|
||
- PBR高光(使用Roughness控制是否可行?是否需要传入GBuffer一个Mask贴图)
|
||
- 自定义高光:高光贴图、高光颜色、参数化高光形状、多层高光
|
||
|
||
# BasePassPixelShader
|
||
Velocity相关代码段:
|
||
```c++
|
||
#if USES_GBUFFER
|
||
// -0.5 .. 0.5, could be optimzed as lower quality noise would be sufficient
|
||
float QuantizationBias = PseudoRandom( MaterialParameters.SvPosition.xy ) - 0.5f;
|
||
|
||
GBuffer.IndirectIrradiance = IndirectIrradiance;
|
||
|
||
// this is the new encode, the older encode is the #else, keeping it around briefly until the new version is confirmed stable.
|
||
#if 1
|
||
{
|
||
// change this so that we can pack everything into the gbuffer, but leave this for now
|
||
#if GBUFFER_HAS_DIFFUSE_SAMPLE_OCCLUSION
|
||
GBuffer.GenericAO = float(GBuffer.DiffuseIndirectSampleOcclusion) * (1.0f / 255.0f);
|
||
#elif ALLOW_STATIC_LIGHTING
|
||
// No space for AO. Multiply IndirectIrradiance by AO instead of storing.
|
||
GBuffer.GenericAO = EncodeIndirectIrradiance(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (1.0 / 255.0); // Stationary sky light path
|
||
#else
|
||
GBuffer.GenericAO = GBuffer.GBufferAO; // Movable sky light path
|
||
#endif
|
||
|
||
EncodeGBufferToMRT(Out, GBuffer, QuantizationBias);
|
||
|
||
if (GBuffer.ShadingModelID == SHADINGMODELID_UNLIT && !STRATA_ENABLED) // Do not touch what strata outputs
|
||
{
|
||
Out.MRT[1] = 0;
|
||
SetGBufferForUnlit(Out.MRT[2]);
|
||
Out.MRT[3] = 0;
|
||
Out.MRT[GBUFFER_HAS_VELOCITY ? 5 : 4] = 0;
|
||
Out.MRT[GBUFFER_HAS_VELOCITY ? 6 : 5] = 0;
|
||
}
|
||
|
||
#if SINGLE_LAYER_WATER_SEPARATED_MAIN_LIGHT
|
||
// In deferred, we always output the directional light in a separated buffer.
|
||
// This is used to apply distance field shadows or light function to the main directional light.
|
||
// Strata also writes it through MRT because this is faster than through UAV.
|
||
#if STRATA_ENABLED && STRATA_INLINE_SINGLELAYERWATER
|
||
Out.MRT[(GBUFFER_HAS_VELOCITY ? 2 : 1) + (GBUFFER_HAS_PRECSHADOWFACTOR ? 1 : 0)] = float4(SeparatedWaterMainDirLightLuminance * View.PreExposure, 1.0f);
|
||
#else
|
||
if (GBuffer.ShadingModelID == SHADINGMODELID_SINGLELAYERWATER)
|
||
{
|
||
Out.MRT[(GBUFFER_HAS_VELOCITY ? 6 : 5) + (GBUFFER_HAS_PRECSHADOWFACTOR ? 1 : 0)] = float4(SeparatedWaterMainDirLightLuminance * View.PreExposure, 1.0f);
|
||
}
|
||
#endif
|
||
#endif
|
||
}
|
||
```
|
||
|
||
# 顶点色
|
||
## 蓝色协议
|
||
用于存储一些低精度数据,插值即可
|
||
- 顶点色:
|
||
- R:阴影区域控制(强度) 0~1
|
||
- G:描边宽度
|
||
- B:ToonAO
|
||
- 第二套顶点色(UV Channel1):
|
||
- R:深度偏移
|
||
- G:用来区分内轮廓不同部位的ID
|
||
|
||
蓝色协议的R:阴影区域标记 与 B:AO,而罪恶装备使用贴图来传递信息。
|
||
## 罪恶装备
|
||
对阴影判断阈值的偏移。(见前面着色部分,顶点AO+手绘修正)
|
||
R:阴影偏移
|
||
G:轮廓线根据与相机的距离扩大多少的系数
|
||
B:等高线 Z 轴偏移值
|
||
|
||
# 罪恶装备
|
||
8,G为阴影控(AO),R为高光强度参数,金属和光滑材质的部分设置的更大一些。B通道:用于照明控制。最大值为高光,反之,值越小高光越淡。
|
||

|
||
|
||
https://zhuanlan.zhihu.com/p/360229590一文中介绍了崩坏3与原神的计算方式
|
||
|
||
崩坏3的LightMap计算方式:
|
||
```c++
|
||
half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv.xy);
|
||
half4 LightMapColor = SAMPLE_TEXTURE2D(_LightMap, sampler_LightMap, input.uv.xy);
|
||
half3 ShadowColor = baseColor.rgb * _ShadowMultColor.rgb;
|
||
half3 DarkShadowColor = baseColor.rgb * _DarkShadowMultColor.rgb;
|
||
|
||
//如果SFactor = 0,ShallowShadowColor为一级阴影色,否则为BaseColor。
|
||
float SWeight = (LightMapColor.g * input.color.r + input.lambert) * 0.5 + 1.125;
|
||
float SFactor = floor(SWeight - _ShadowArea);
|
||
half3 ShallowShadowColor = SFactor * baseColor.rgb + (1 - SFactor) * ShadowColor.rgb;
|
||
```
|
||
|
||
二级阴影计算:
|
||
```c++
|
||
//如果SFactor = 0,DarkShadowColor为二级阴影色,否则为一级阴影色。
|
||
SFactor = floor(SWeight - _DarkShadowArea);
|
||
DarkShadowColor = SFactor * (_FixDarkShadow * ShadowColor + (1 - _FixDarkShadow) * ShallowShadowColor) + (1 - SFactor) * DarkShadowColor;
|
||
|
||
// 平滑阴影边缘
|
||
half rampS = smoothstep(0, _ShadowSmooth, input.lambert - _ShadowArea);
|
||
half rampDS = smoothstep(0, _DarkShadowSmooth, input.lambert - _DarkShadowArea);
|
||
ShallowShadowColor.rgb = lerp(ShadowColor, baseColor.rgb, rampS);
|
||
DarkShadowColor.rgb = lerp(DarkShadowColor.rgb, ShadowColor, rampDS);
|
||
|
||
//如果SFactor = 0,FinalColor为二级阴影,否则为一级阴影。
|
||
SFactor = floor(LightMapColor.g * input.color.r + 0.9f);
|
||
half4 FinalColor;
|
||
FinalColor.rgb = SFactor * ShallowShadowColor + (1 - SFactor) * DarkShadowColor;
|
||
```
|
||
|
||
|
||
**罪恶装备**:
|
||
对阴影判断阈值的偏移。(见前面着色部分,顶点AO+手绘修正)
|
||
G : 轮廓线根据与相机的距离扩大多少的系数
|
||
B : 等高线 Z 轴偏移值
|
||
A : 轮廓厚度系数。0.5为标准,1为最大厚度,0为无等高线
|
||
|
||
# 蓝色协议
|
||
[[蓝色协议的方案]]
|
||
|
||
# 米哈游
|