BlueRoseNote/03-UnrealEngine/Rendering/RenderingPipeline/参考SubsurfaceProfile 模改ToonDataAsset.md
2023-06-29 11:55:02 +08:00

8.8 KiB
Raw Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
参考SubsurfaceProfile 模改ToonDataAsset 2023-02-04 20:38:39

原理

PreShader

在材质编译期,将名为 __SubsurfaceProfile 的Uniform表达式塞入材质中。

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::FillUniformBufferDump preshader results into buffer.
    1. FEmitContext::EmitPreshaderOrConstantPreshaderHeader = &UniformExpressionSet.UniformPreshaders.AddDefaulted_GetRef();

将ToonData的ID塞入材质

存在问题如何将SubsurfaceProfile Asset的ID塞入材质中

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);  
}

将SubsurfaceProfile 塞入 Material

int32 UMaterialExpressionStrataLegacyConversion::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex)  
{
}

MaterialRenderProxy

  
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

uint8 bOverrideSubsurfaceProfile:1;

TObjectPtr<class USubsurfaceProfile> SubsurfaceProfile;

void UMaterialInterface::UpdateMaterialRenderProxy(FMaterialRenderProxy& Proxy)

//还有所有子类实现 UMaterialInstance、UMaterial
USubsurfaceProfile* UMaterialInterface::GetSubsurfaceProfile_Internal() const 

MaterialShared.h

inline bool UseSubsurfaceProfile(FMaterialShadingModelField ShadingModel)  
{  
   return ShadingModel.HasShadingModel(MSM_SubsurfaceProfile) || ShadingModel.HasShadingModel(MSM_Eye);  
}

UMaterial

USubsurfaceProfile* UMaterial::GetSubsurfaceProfile_Internal() const  
{  
   checkSlow(IsInGameThread());  
   return SubsurfaceProfile; 
}