我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:六合生肖表 > 法线流 >

如何用Unity从纹理中生成法线贴图的基本方法

归档日期:04-13       文本归类:法线流      文章编辑:爱尚语录

  一张二维纹理有两个维度 u 和 v,但其实,高度(h)可以算第三个维度。有了高度,一张二维纹理就可以想象成一个三维的物体了。

  先来考虑只有 u 方向的情况,如图所示, A 和 B 是纹理中的两个点, uv 坐标分别是 (0, 0) 和 (1, 0),上方黑线表示点对应的高度,那么显然,只要求出 u 方向上的高度函数在某一点的切线,就能求出垂直于他的法线了。同理, v 方向也是如此。也就是说,如果有纹理的高度信息,那么就能计算出纹理中每一个像素的法线了。

  求出切线后,就得到了两个方向上的切线向量和。之所以是这种形式的二维向量,是因为这里是按照 uoh 平面和 voh 平面分别计算的,具体的向量形式需要根据实际情况去组合。这里可以做一个优化,在求导数的时候公式里做了一个除法,因为法线最终会归一化,切线向量长度不影响叉积后的结果向量方向,所以其实可以直接把求导数时候的除法去掉。

  _DeltaScale 是一个控制步长缩放的变量,在这个例子中为 0.5,乘以 _DeltaScale 是用来控制法线生成的精确度的,就如之前所说, 越小,生成的法线就越精确。通常我们会向当前采样点两侧去采样,以获得更精准的结果,这个方法叫做中心差分法。然后可以根据步长分别取当前像素左右两侧的高度值(在这个例子里就是灰度值),在按照上面提到的计算方法计算切线即可。注释掉的代码是原始代码,下面没注释的是优化后的代码,这个也是上面提到的。

  同理,第二段可以计算出 v 方向的高度函数切线,两个切线向量,做叉积,再归一化,即可获得当前像素点表面的法线向量。叉积的顺序很重要,因为纹理是朝向 -z 轴的,所以一般来说会让法线也顺着表面所在的朝向,这就是为什么是 cross(tangentv, tangentu) 而不是 cross(tangentu, tangentv) 的原因。

  基于切线空间的法线贴图,z 也就是 b 通道的值都是 0.5 到 1,而 x 和 y 也就是 r 和 g 通道都是 0 到 1,所以看起来会偏蓝一些,当然不是绝对。而上面计算出来的法线贴图,由于叉积的顺序,z 分量是朝向 -z 轴的,所以 b 通道都是 0 到 0.5,不信可以用截屏工具看下颜色值。在这个例子里,想要变成切线空间下的法线贴图是非常简单的,只需要将 z 分量乘以 -1 即可。

  在计算切线向量的时候,是直接用高度差和值做计算的,这其实是不合理的,因为是非常非常小的,一张 1024 * 1024 大小的图,只有 1 / 1024 = 0.00097656,但是高度差却是 0 到 1 之间某两个数的差,例如高度为 0.6 和高度为 0.2,正常来说是远大于的,这就导致了切线向量很接近 -z 轴,计算出的法线就很接近于 xoy 平面了,这样就看起来有很多红色和绿色,因为 x 和 y 的分量更大。为了解决这个问题,需要引入一个 _HeightScale 变量,来控制高度差的比例。

本文链接:http://oms15.com/faxianliu/18.html