0%

ConvertSDF2NormalTS

摘要

一种简单的方法将类原神SDF面部贴图转换为Tangent Space Normal映射图.

集团有一个项目是山寨出来一个东南亚原神, SDF图的方法无法适用于Unreal的多光源, 显然直接用法线才是合理的方法.

核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vec3 TransformLocalVectorToTangent (vec3 InLocalVector)
{
return InLocalVector * Transpose(TBN_Matrix) * (mat3)ObjectToWolrdMatrix;
}

vec3 ConvertSdf2NormTS (float InputSDF, float OffsetSDF = 0.0)
{
float PI = acos(-1.0);
float InvertScaledNum = 2 / PI;
float DecodedSDF = asin(saturate(InputSDF + OffsetSDF) * 2.0 - 1.0) * InvertScaledNum;
vec3 TangentU = TransformLocalVectorToTangent(vec3(1.0, 0.0, 0.0));
vec3 TangentV = TransformLocalVectorToTangent(vec3(0.5, 1.0, 0.0));
return normalize(lerp(TangentV, TangentU, DecodedSDF));
}

原理

  • 首先,我们需要将SDF计算确定在切线空间, 这是我们统一的计算空间, 我们的目标就是输出切线空间的法线映射图.
  • 我们可以简单的将输入SDF看作为切空间法线的偏转角度.
  • 由于类原神SDF的特殊性, 我们可以简单将角度限定在半球.
  • 将SDF从[0-1]转换到[-1, 1], 来表示在半球内偏转的方向的弧度值.
  • 使用asin将[-1 , 1]的弧度值转换为[-Π/2 , Π/2].
  • 归一化操作后,直接作为切值的依据.
  • 由于我们只有一个方向的数据来源, 我们直接用非归一的且朝向切线的副切线来处理一个垂直方向的角度.
  • 最终进行归一的切线空间法线输出.

Unreal直接作为CustomNode使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Convert Genshine Liked SDF Face map to Tangent NormalMap
struct SdfConvertToNormalLib
{
#define InvertScaledNum 0.6366197724

FMaterialPixelParameters Parameters;

inline MaterialFloat3 TransformLocalVectorToTangent (MaterialFloat3 InLocalVector)
{ // Transform LocalVector to TangentSpace, for Unreal
MaterialFloat3 VectorWS = TransformLocalVectorToWorld(Parameters, InLocalVector);
MaterialFloat3 VectorTS = mul((MaterialFloat3x3)(Parameters.TangentToWorld), VectorWS);
return VectorTS;
}

float3 ConvertSdf2NormTS (float InputSDF)
{
float DecodedSDF = asin(saturate(InputSDF) * 2.0 - 1.0) * InvertScaledNum;
float3 TangentU = TransformLocalVectorToTangent(float3(1.0, 0.0, 0.0));
float3 TangentV = TransformLocalVectorToTangent(float3(0.5, 1.0, 0.0));
return normalize(lerp(TangentV, TangentU, DecodedSDF));
}

}UnrealCustomNode;

UnrealCustomNode.Parameters = Parameters;
return UnrealCustomNode.ConvertSdf2NormTS(INPUT.x);

歡迎關注我的其它發布渠道