与法线贴图相同,可以模拟出物体得深度感,同时它得改进是能够随着视角得偏移显示不同得深度感,使得显示更加真实。
由于采样高度图会比较难以计算,因此可以采样深度图。(深度图其实是高度图得反相,白色表示凹陷)。
计算:
需要将视线向量转换到切线空间下(因为高度是物体表面信息,都是相对于物体表面空间(切线空间)来描述的)。
原本采样是H(A)点,uv值为A,根据A点记录的深度信息,将V的长度缩放为高度贴图在点A处H(A)采样得来的深度(高度)值的到P。
这里传入一张高度图代替深度图:_ParallaxMap
也可以传入深度图_DeepMap,两者为反相关系,_ParallaxMap = 1 - _DeepMap。
// 视差映射
half2 parallaxMapping(half2 uv, half3 view_dir_tangent)
{
float deep = (1. - tex2D(_ParallaxMap, uv).r) * _ParallaxIntensity;
// 向视线方向偏移, 眼睛 -> 像素
float2 P = view_dir_tangent.xy / view_dir_tangent.z * deep ;
return uv - P;
}
计算切线空间的视线向量
fixed3 normal_dir = normalize(i.normal_world);
fixed3 tangent_dir = normalize(i.tangent_world);
fixed3 binormal_dir = normalize(i.binormal_world);
float3x3 TBN = float3x3(tangent_dir, binormal_dir, normal_dir);
fixed3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
half3 view_dir_tangent = normalize(mul(TBN, view_dir)); // 视线转换到切线空间
float2 steepParallaxMapping(float2 uv, float3 view_dir_tangent)
{
const float numLayers = _ParallaxIteration;
float deep_step = 1.0 / numLayers;
float2 P = view_dir_tangent.xy * deep_step * _ParallaxIntensity;// 视角方向
float deep = (1. - tex2D(_ParallaxMap, uv).r);
float current_deep = 0.f;
float2 current_uv = uv;
[unroll(30)]
while (current_deep < deep) // 如果当前深度小于 深度贴图的深度,表示找到了近似值
{
current_uv -= P;
deep = (1. - tex2D(_ParallaxMap, current_uv).r);
current_deep += deep_step;
}
return current_uv;
}
下面是_ParallaxIteration取值10,——parallaxIntensity取值0.1
会有明显的锯齿感:
视差遮蔽映射(Parallax Occlusion Mapping)和陡峭视差映射的原则相同,但不是用触碰的第一个深度层的纹理坐标,而是在触碰之前和之后,在深度层之间进行线性插值。我们根据表面的高度距离啷个深度层的深度层值的距离来确定线性插值的大小。
大部分和陡峭视差映射一样,不一样的地方是有个额外的步骤,两个深度层的纹理坐标围绕着交叉点的线性插值。这也是近似的,但是比陡峭视差映射更精确。
float2 parallaxOcclusionMapping(float2 uv, float3 view_dir_tangent)
{
float deep_step = 1. / _ParallaxIteration;
float2 P = view_dir_tangent.xy * deep_step * _ParallaxIntensity;
float2 curr_uv = uv;
float curr_deep = 0.f;
float tex_deep = 1. - tex2D(_ParallaxMap, curr_uv).r;
[unroll(30)]
while (curr_deep < tex_deep)
{
curr_uv -= P;
tex_deep = 1. - tex2D(_ParallaxMap, curr_uv).r;
curr_deep += deep_step;
}
// 上面是陡峭视差映射的逻辑
// 进行插值
float2 prevTexCoords = curr_uv + P; // 上一个uv值
float afterDeep = tex_deep - curr_deep;
float beforeDeep = (1 - tex2D(_ParallaxMap, prevTexCoords).r) - curr_deep + deep_step;
// interpolation of texture coordinates
float weight = afterDeep / (afterDeep - beforeDeep);
float2 finalTexCoords = prevTexCoords * weight + curr_uv * (1.0 - weight);
return finalTexCoords;
}
我们知道深度数据的范围为[0-1],因此我们可以通过多次迭代让深的更深,从而产生更强的立体感:
float2 steepParallaxMapping_kerry(float2 uv, float3 view_dir_tangent)
{
half2 parallax_uv = uv;
// /z可以修正高度增加立体感
half2 P = view_dir_tangent.xy * _ParallaxIntensity; // / view_dir_tangent.z;
for (int j = 0; j < _ParallaxIteration; j++)
{ // 这里的1可以取0.5或者其他值,
const half deep = 1. - tex2D(_ParallaxMap, parallax_uv).r;
parallax_uv = parallax_uv - deep * P;
}
return parallax_uv.xy;
}
可以通过变更深度和高度的参数来提升立体感:
const half deep = 1. - tex2D(_ParallaxMap, parallax_uv).r; 取值0.5(一部分表示为深度,一部分为高度)