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