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