--- 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(It); } return 0; } template ParameterType* GameThread_FindParameterByName(TArray& 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 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; ```