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