210 lines
8.8 KiB
Markdown
210 lines
8.8 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);
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
将SubsurfaceProfile 塞入 Material
|
|||
|
```c++
|
|||
|
int32 UMaterialExpressionStrataLegacyConversion::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex)
|
|||
|
{
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## 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;
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
```
|