215 lines
9.2 KiB
Markdown
215 lines
9.2 KiB
Markdown
---
|
||
title: 参考SubsurfaceProfile 模改ToonDataAsset
|
||
date: 2023-02-04 20:38:39
|
||
excerpt:
|
||
tags:
|
||
rating: ⭐⭐
|
||
---
|
||
# 原理
|
||
## PreShader
|
||
在材质编译期,将名为 **__SubsurfaceProfile** 的Uniform表达式塞入材质中。
|
||
```c++
|
||
int32 FHLSLMaterialTranslator::NumericParameter(EMaterialParameterType ParameterType, FName ParameterName, const UE::Shader::FValue& InDefaultValue)
|
||
{
|
||
const UE::Shader::EValueType ValueType = GetShaderValueType(ParameterType);
|
||
check(InDefaultValue.GetType() == ValueType);
|
||
UE::Shader::FValue DefaultValue(InDefaultValue);
|
||
|
||
// If we're compiling a function, give the function a chance to override the default parameter value
|
||
FMaterialParameterMetadata Meta;
|
||
if (GetParameterOverrideValueForCurrentFunction(ParameterType, ParameterName, Meta))
|
||
{
|
||
DefaultValue = Meta.Value.AsShaderValue();
|
||
check(DefaultValue.GetType() == ValueType);
|
||
}
|
||
|
||
const uint32* PrevDefaultOffset = DefaultUniformValues.Find(DefaultValue);
|
||
uint32 DefaultOffset;
|
||
if (PrevDefaultOffset)
|
||
{
|
||
DefaultOffset = *PrevDefaultOffset;
|
||
}
|
||
else
|
||
{
|
||
DefaultOffset = MaterialCompilationOutput.UniformExpressionSet.AddDefaultParameterValue(DefaultValue);
|
||
DefaultUniformValues.Add(DefaultValue, DefaultOffset);
|
||
}
|
||
|
||
FMaterialParameterInfo ParameterInfo = GetParameterAssociationInfo();
|
||
ParameterInfo.Name = ParameterName;
|
||
|
||
const int32 ParameterIndex = MaterialCompilationOutput.UniformExpressionSet.FindOrAddNumericParameter(ParameterType, ParameterInfo, DefaultOffset);
|
||
return AddUniformExpression(new FMaterialUniformExpressionNumericParameter(ParameterInfo, ParameterIndex), GetMaterialValueType(ParameterType), TEXT(""));
|
||
}
|
||
```
|
||
|
||
`const int32 ParameterIndex = MaterialCompilationOutput.UniformExpressionSet.FindOrAddNumericParameter(ParameterType, ParameterInfo, DefaultOffset);`
|
||
`return AddUniformExpression(new FMaterialUniformExpressionNumericParameter(ParameterInfo, ParameterIndex), GetMaterialValueType(ParameterType), TEXT(""));`
|
||
|
||
之后在`Chunk[MP_SubsurfaceColor] = AppendVector(SubsurfaceColor, CodeSubsurfaceProfile);`将结果编译成`MaterialFloat4(MaterialFloat3(1.00000000,1.00000000,1.00000000),Material.PreshaderBuffer[2].x)`
|
||
|
||
## 填充PreShader结构体
|
||
1. 从MeshDraw框架的FMeshElementCollector::AddMesh()开始,执行`MeshBatch.MaterialRenderProxy->UpdateUniformExpressionCacheIfNeeded(Views[ViewIndex]->GetFeatureLevel());`,开始更新材质的UniformExpression。
|
||
2. `FMaterialRenderProxy::UpdateUniformExpressionCacheIfNeeded()`:取得材质指针之后评估材质表达式。
|
||
3. `FMaterialRenderProxy::EvaluateUniformExpressions()`:从渲染线程取得材质的ShaderMap,再从ShaderMap取得UniformExpressionSet。
|
||
4. `FUniformExpressionSet::FillUniformBuffer`:Dump preshader results into buffer.
|
||
1. FEmitContext::EmitPreshaderOrConstant:PreshaderHeader = &UniformExpressionSet.UniformPreshaders.AddDefaulted_GetRef();
|
||
|
||
# 将ToonData的ID塞入材质
|
||
|
||
存在问题,如何将SubsurfaceProfile Asset的ID塞入材质中:
|
||
```c++
|
||
int32 FMaterialCompiler::ScalarParameter(FName ParameterName, float DefaultValue)
|
||
{
|
||
return NumericParameter(EMaterialParameterType::Scalar, ParameterName, DefaultValue);
|
||
}
|
||
|
||
int32 FHLSLMaterialTranslator::NumericParameter(EMaterialParameterType ParameterType, FName ParameterName, const UE::Shader::FValue& InDefaultValue)
|
||
{
|
||
const UE::Shader::EValueType ValueType = GetShaderValueType(ParameterType);
|
||
check(InDefaultValue.GetType() == ValueType);
|
||
UE::Shader::FValue DefaultValue(InDefaultValue);
|
||
|
||
// If we're compiling a function, give the function a chance to override the default parameter value
|
||
FMaterialParameterMetadata Meta;
|
||
if (GetParameterOverrideValueForCurrentFunction(ParameterType, ParameterName, Meta))
|
||
{ DefaultValue = Meta.Value.AsShaderValue();
|
||
check(DefaultValue.GetType() == ValueType);
|
||
}
|
||
const uint32* PrevDefaultOffset = DefaultUniformValues.Find(DefaultValue);
|
||
uint32 DefaultOffset;
|
||
if (PrevDefaultOffset)
|
||
{
|
||
DefaultOffset = *PrevDefaultOffset;
|
||
}else
|
||
{
|
||
DefaultOffset = MaterialCompilationOutput.UniformExpressionSet.AddDefaultParameterValue(DefaultValue);
|
||
DefaultUniformValues.Add(DefaultValue, DefaultOffset);
|
||
}
|
||
FMaterialParameterInfo ParameterInfo = GetParameterAssociationInfo();
|
||
ParameterInfo.Name = ParameterName;
|
||
|
||
const int32 ParameterIndex = MaterialCompilationOutput.UniformExpressionSet.FindOrAddNumericParameter(ParameterType, ParameterInfo, DefaultOffset);
|
||
return AddUniformExpression(new FMaterialUniformExpressionNumericParameter(ParameterInfo, ParameterIndex), GetMaterialValueType(ParameterType), TEXT(""));
|
||
}
|
||
|
||
bool FMaterialHLSLGenerator::GetParameterOverrideValueForCurrentFunction(EMaterialParameterType ParameterType, FName ParameterName, FMaterialParameterMetadata& OutResult) const
|
||
{
|
||
bool bResult = false;
|
||
if (!ParameterName.IsNone())
|
||
{ // Give every function in the callstack on opportunity to override the parameter value
|
||
// Parameters in outer functions take priority // For example, if a layer instance calls a function instance that includes an overriden parameter, we want to use the value from the layer instance rather than the function instance for (const FFunctionCallEntry* FunctionEntry : FunctionCallStack)
|
||
{ const UMaterialFunctionInterface* CurrentFunction = FunctionEntry->MaterialFunction;
|
||
if (CurrentFunction)
|
||
{
|
||
if (CurrentFunction->GetParameterOverrideValue(ParameterType, ParameterName, OutResult))
|
||
{ bResult = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return bResult;
|
||
}
|
||
|
||
// Finds a parameter by name from the game thread, traversing the chain up to the BaseMaterial.
|
||
FScalarParameterValue* GameThread_GetScalarParameterValue(UMaterialInstance* MaterialInstance, FName Name)
|
||
{
|
||
UMaterialInterface* It = 0;
|
||
FMaterialParameterInfo ParameterInfo(Name); // @TODO: This will only work for non-layered parameters
|
||
|
||
while(MaterialInstance)
|
||
{
|
||
if(FScalarParameterValue* Ret = GameThread_FindParameterByName(MaterialInstance->ScalarParameterValues, ParameterInfo))
|
||
{
|
||
return Ret;
|
||
}
|
||
It = MaterialInstance->Parent;
|
||
MaterialInstance = Cast<UMaterialInstance>(It);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
template <typename ParameterType>
|
||
ParameterType* GameThread_FindParameterByName(TArray<ParameterType>& Parameters, const FHashedMaterialParameterInfo& ParameterInfo)
|
||
{
|
||
for (int32 ParameterIndex = 0; ParameterIndex < Parameters.Num(); ParameterIndex++)
|
||
{
|
||
ParameterType* Parameter = &Parameters[ParameterIndex];
|
||
if (Parameter->ParameterInfo == ParameterInfo)
|
||
{
|
||
return Parameter;
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
void UMaterialFunctionInstance::OverrideMaterialInstanceParameterValues(UMaterialInstance* Instance)
|
||
{
|
||
// Dynamic parameters
|
||
Instance->ScalarParameterValues = ScalarParameterValues;
|
||
Instance->VectorParameterValues = VectorParameterValues;
|
||
Instance->DoubleVectorParameterValues = DoubleVectorParameterValues;
|
||
Instance->TextureParameterValues = TextureParameterValues;
|
||
Instance->RuntimeVirtualTextureParameterValues = RuntimeVirtualTextureParameterValues;
|
||
Instance->FontParameterValues = FontParameterValues;
|
||
|
||
// Static parameters
|
||
FStaticParameterSet StaticParametersOverride = Instance->GetStaticParameters();
|
||
StaticParametersOverride.EditorOnly.StaticSwitchParameters = StaticSwitchParameterValues;
|
||
StaticParametersOverride.EditorOnly.StaticComponentMaskParameters = StaticComponentMaskParameterValues;
|
||
Instance->UpdateStaticPermutation(StaticParametersOverride);
|
||
}
|
||
```
|
||
|
||
和UMaterialExpressionStrataLegacyConversion::Compile()无关。
|
||
GetSubsurfaceProfileParameterName()
|
||
__SubsurfaceProfile
|
||
|
||
1. 在FHLSLMaterialTranslator::Translate() => NumericParameter()会往MaterialCompilationOutput.UniformExpressionSet以及DefaultUniformValues添加默认值以及Offset。最终会调用AddUniformExpression()
|
||
|
||
## MaterialRenderProxy
|
||
```c++
|
||
|
||
void SetSubsurfaceProfileRT(const USubsurfaceProfile* Ptr) { SubsurfaceProfileRT = Ptr; }
|
||
const USubsurfaceProfile* GetSubsurfaceProfileRT() const { return SubsurfaceProfileRT; }
|
||
|
||
/** 0 if not set, game thread pointer, do not dereference, only for comparison */
|
||
const USubsurfaceProfile* SubsurfaceProfileRT;
|
||
```
|
||
|
||
## UMaterialInterface
|
||
```c++
|
||
uint8 bOverrideSubsurfaceProfile:1;//UE5转移至UMaterialInstance中
|
||
|
||
TObjectPtr<class USubsurfaceProfile> SubsurfaceProfile;
|
||
|
||
void UMaterialInterface::UpdateMaterialRenderProxy(FMaterialRenderProxy& Proxy)
|
||
|
||
//还有所有子类实现 UMaterialInstance、UMaterial
|
||
USubsurfaceProfile* UMaterialInterface::GetSubsurfaceProfile_Internal() const
|
||
```
|
||
|
||
## MaterialShared.h
|
||
```c++
|
||
inline bool UseSubsurfaceProfile(FMaterialShadingModelField ShadingModel)
|
||
{
|
||
return ShadingModel.HasShadingModel(MSM_SubsurfaceProfile) || ShadingModel.HasShadingModel(MSM_Eye);
|
||
}
|
||
|
||
```
|
||
|
||
## UMaterial
|
||
```c++
|
||
USubsurfaceProfile* UMaterial::GetSubsurfaceProfile_Internal() const
|
||
{
|
||
checkSlow(IsInGameThread());
|
||
return SubsurfaceProfile;
|
||
}
|
||
```
|
||
|
||
## UMaterialInstance
|
||
```c++
|
||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = MaterialInstance)
|
||
uint8 bOverrideSubsurfaceProfile:1;
|
||
``` |