贴图介绍
Mix1图:
- R:ShadingID
- G:Metallic
- B:SpecularMask
Mix0图,
- R: 还没看
- G: Roughness
- B: mask # MatcapMask(0~0.2)|EmissiveMask(0.2~1)
- MatCapMask = mask < 0.2 ? saturate(mask*5.1) : 0
- EmissiveMask = saturate((mask-0.2)*1.25)
MatCpas(Texture2DArray):三张图通常用于皮革、金属、丝袜。

Ramp
角色的Ramp
Ramp分为五层过渡,并且每个ID都会有这五层Ramp数据,参数5×5直接来到了25个,计算量会很大。如果我们使用曲线的话则要采样五次消耗也很大。
根据光照角度使用不同的层级
在UE复现时我是用了ColorCurveAtlas,每个材质通过5个Curve来调整不同ShadingID区域的Ramp颜色。
BaseColor与MatCap
以丝袜为例,可以看到丝袜主要是各向异性高光以及一个Ramp叠在一起
这个厚度纹理其实就是MatCapMask图,采样Matcap图后就是第三张图的样子,Matcap颜色是一个用户参数。
经查看几乎所有丝袜的材质都使用的第三张Matcap,并且有0.5的缩放和0.5的偏移,也就是只使用了左下角的部分。
BaseColor的计算公式为:
BaseColor = lerp( baseColor, baseColor * matcap * matcapTint , matcapMask )
下图第1张图就是混入了Matcap的BaseColor。
Specular
公式为
![Rendered by QuickLaTeX.com \[ S=SpecularArea*SepcularMask*UserSpecularColor*F0*Ramp \]](https://www.imxqy.com/wp-content/ql-cache/quicklatex.com-f53b7de6b00897da67846b36dd73571d_l3.png)
- SpecularArea的计算中身体部分使用的是UnityURP中的GGX计算,而头发使用的一个自定义的高光计算。
- SepcularMask通过采样纹理来控制哪部分区域拥有高光。
- F0是lerp(0.04, baseColor, metallic)计算而来。
在身体部分计算高光区域使用的是GGX后作为D,而G使用:
float G = saturate( NoL * 0.75 + 0.25 );
最后计算高光区域:
(D-(1-rough)) * G / (rough*rough);
头发高光计算为:
float s = min(1, 0.16666 / NoH2); NoL = saturate(NoL * 0.75 + 0.25); return s * NoL;
头发部分的高光:
待补充
Emissive
emissive的Mask存在Mix0图B通道的0.2~0.8范围内,需要做一个映射。
saturate((b - 0.2) * 1.25);
然后也是分五个ID做五个用户参数,最后乘上Color就可以了
saturate((b - 0.2) * 1.25) * GetEmissive(id) * color
Outline
描边是切线空间平滑法线的数据存放在TexCoord1的两个通道中。
顶点计算部分是在View空间进行法线外扩,并根据深度修改描边粗细。同时顶点色的R通道也会作为描边外扩的Mask来控制粗细。
在着色阶段使用背面渲染。颜色计算使用:
float3 hsv = ToHSV(basecolor - metallic); float a = (hsv.y - hsv.z) * 0.2 + 0.6; float3 b = smoothstep(0.28, 0.38, NoL) * 0.5 + 0.5; hsv.z *= a * b; return ToRGB(hsv) * UserOutlineTint;
关于S-V的值
| 颜色类型 | V | S | S-V | 基础因子 | 光照响应 |
|---|---|---|---|---|---|
| 鲜艳深色 | 0.5 | 1.0 | +0.5 | 0.7 | 强 |
| 鲜艳亮色 | 1.0 | 1.0 | 0 | 0.6 | 中 |
| 白色/浅灰 | 0.9 | 0.1 | -0.8 | 0.44 | 弱 |
Tonemap/LUT映射
绝区零使用LUT在LogC空间进行调色和tonemap,Unity有LogC的转换函数
在逆向时在UE中写了个
float3 LinearToLogC (float3 linearColor)
{
const float a = 5.5555558;
const float d = 0.047996;
const float b = 0.0734998;
const float c = 0.386036;
float3 x = linearColor * a + d;
float3 logC = c + b * log2(x);
return saturate(logC);
}












