486 lines
21 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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);
- Tangentfalse目前单独使用另一组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.cppFMaterialAttributeDefinitionMap::InitializeAttributeMap()中定义属性。
- HLSLMaterialTranslator.cppGetMaterialShaderCode()中的`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`
### 是否需要Toon
在材质中:
```c++
FMaterialRelevance UMaterialInterface::GetRelevance_Internal(const UMaterial* Material, ERHIFeatureLevel::Type InFeatureLevel) const
{
if(Material)
{
//YivanLee's Modify 这里仅仅针对人物因为它决定了是否开启ToonGBuffer但是对于ToonLevelToonFoliageToonGrass这里并不需要开启
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 轴偏移值
# 罪恶装备
![](https://pic2.zhimg.com/80/v2-56012886fafbaf36932f03b0ad65a165_720w.jpg)8,G为阴影控AOR为高光强度参数金属和光滑材质的部分设置的更大一些。B通道用于照明控制。最大值为高光反之值越小高光越淡。![](https://pic4.zhimg.com/80/v2-748ebbdd4da3efe74054c8215be8b023_720w.jpg)
![](https://pic2.zhimg.com/80/v2-74e1a9fba264af2b18e66616d9f86831_720w.jpg)
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为无等高线
# 蓝色协议
[[蓝色协议的方案]]
# 米哈游