视差贴图的展示

图一:注意轮胎印中的自阴影,实际上路面只是一个平地

unity image 的 fillAmount 差值动画 unity 视差贴图_纹理贴图

 

图二:孤岛危机的成功,视差贴图功不可没

unity image 的 fillAmount 差值动画 unity 视差贴图_贴图_02

 

图三:视差贴图同样可用于人物,装备及道具

unity image 的 fillAmount 差值动画 unity 视差贴图_贴图_03

 

本讲分四个部分
一:优点特性

二:基本原理

三:使用方法

四:视差计算

 

第一部分 视差贴图的优点及特性

上一讲讲了法线贴图。法线贴图,无论是RGB法线贴图还是DXT5nm,都有一个缺点就是。越接近面的水平视角,则画面越不真实。因为使用法线贴图后,凸起的部分只是看起来凸起而已,网格模型却并没有凸起依然是平的。正常来说凸起的部分会遮挡后面的部分,法线贴图却没有这个效果。于是我们有了法线贴图的改进版 - 视差贴图

视差贴图的好处:

(1)在相对视角会发生变化时,很有立体感

(2)在入视角接近水平时,依然有不错的质量

下面两图对比可以看出,在入视角接近水平时,视差贴图要比法线贴图清晰不少。

注意:在入视角等于或接近垂直时,视差贴图与法线贴图的效果是几乎一样的。

 

unity image 的 fillAmount 差值动画 unity 视差贴图_灰度图_04

unity image 的 fillAmount 差值动画 unity 视差贴图_纹理贴图_05



 

 

 

第二部分 视差贴图的原理

视差贴图,由Tomomichi Kaneko在2001年发明,但是限于计算机的性能,真正的商业运用是从2007年开始的。视差贴图通过uv偏移实现。uv的偏移值由高度及由点指向摄像机的向量计算得出。所以视差贴图依然不改变真正的模型表面构造。

注意:如果使用的是Unity自带的Parallax 类shader,则高度必须位于A通道中(对应color.w),如果高度图是灰度图的话,则需转换至A通道,下面的使用部分有详细方法。有关高度的知识,参阅17讲的前半部分。

 

左图:凹凸不平的鹅卵石         右图:实际只是一个平面

unity image 的 fillAmount 差值动画 unity 视差贴图_纹理贴图_06

unity image 的 fillAmount 差值动画 unity 视差贴图_贴图_07




 

第三部分 视差贴图的使用

(1)准备好贴图,法线贴图,高度图。

(2)如果高度图的高度储存在透明通道,略过此步。如果是灰度图则需:选中图,勾选Alpha from Grayscale,然后Apply即可

unity image 的 fillAmount 差值动画 unity 视差贴图_灰度图_08



(3)创建plane及材质,Shader选Parallax Diffuse或者Parallax Diffuse

(4)把三个图拖到材质对应的栏里

(5)调整Height高度。

如果是初次使用视差贴图的话,把Height调到最高,然后旋转plane查看,这样能快速理解视差贴图的好处。

如果是真正运用的话,Height稍微低些,否则会失真。

 

 

第四部分 视差贴图的计算

打开Build-in shader里的 Normal-Parallax.Shader,即Parallax Diffuse。有如下代码:

1. half h = tex2D (_ParallaxMap, IN.uv_BumpMap).w;
2. float2 offset = ParallaxOffset (h, _Parallax, IN.viewDir);
3. IN.uv_MainTex += offset;
4. IN.uv_BumpMap += offset;

(1)纹理映射 取得 高度值h

  (2) 由高度值h及指向摄像机的向量计算偏移offset

  (3) 对纹理贴图以及法线贴图进行uv偏移offset

 

其中第二步是重点。打开UnityCG.cginc , 看到如下代码。

1. // Calculates UV offset for parallax bump mapping
2. inline float2 ParallaxOffset( half h, half height, half3 viewDir )
3. {
4. h = h * height - height/2.0;
5. float3 v = normalize(viewDir);
6. //单位指向相机向量.z +0.42
7. v.z += 0.42;
8. // 偏移.x = 高度*(单位指向相机向量的x / 单位指向相机向量的z)
9. // 偏移.y = 高度*(单位指向相机向量的y / 单位指向相机向量的z)
10. return h * (v.xy / v.z);
11. }

计算方法出来了。具体算法的理论依据感兴趣的同学可以自己去研究。