每天一个shader技巧 day1 - 从图片生成法线贴图 shadertoy实现
前置知识
法线贴图,图形学,向量运算,导数等
算法
先不去想如何从一张纹理去生成法线,因为图片是一个二维的存储结构,处理起来的话会比较复杂,写成方程的形式则是 f(u,v) = pixel color
我们进行一个降维处理,假设我们现在有段曲线,f(x) = y,这样变量就变为一维的了。那么对于一个曲线,我们如何获取曲线一个点上的法线呢,自然可以想到对该函数求一阶导,因为某个点一阶导的结果为过这个点切线的斜率,那么该点的法线的斜率与切线的斜率乘积。现在我们知道点,知道斜率,自然可以求得法线的方程。

扩展到二维的话,也是同理,先对一个点的一个方向求出法线,再对另一个方向求出法线即可。但是!我们既然在这个过程中会求得两个切线,那直接做一个叉乘即可获得垂直于这两个向量的第三个向量,即是法线,所以思路就清晰了。
求切线->算法线
但是按这个思路对一张纹理进行运算会有一个问题,那就是纹理储存的是RGB值,这个值和法线是没有关系的,所以我们需要一张描述该纹理的高度图,然后用该高度图去求法线向量。
那没有高度图怎么办呢,那就用一个神奇的公式,奖图片转为灰度图(不要问我这个公式怎么来的,只能说基于经验)
代码如下
看看效果


求切线
有了高度图之后就要求切线了。因为一张图是由许多个像素点组成的,所以是离散的,那么就需要用到导数的证明过程那个极限的写法了。
但是在实际计算中,我们要的是切线向量,而这个求的是斜率,不是我们想要的。那么切线向量怎么构造呢,回到一维的情况,假设我们有切线方程 ,那么切线向量则是
。
那么回到二维的情况,我们也可以分别对x, y去进行一个切线向量的构造
获得这两个向量后进行一个叉乘便可获得法线向量了。代码如下
代码中有几个要注意的点,首先是生成法线的函数,多了两个参数分别来scale采样的步长和高度。步长好理解,控制生成法线的精度。那为什么需要高度的scale系数呢,因为对图片进行采样的时候,我们所选取的步长是非常小的(1/1024),但是高度差却很大(最大可能到1.0),这是远大于
的,这就会导致切线向量过于接近Z轴,计算出来的法线x,y的值就很大了,所以需要引入一个scale系数来进行高度差的缩放。
最后将处于(-1,-1,-1)到(1,1,1)的法线转化到(0,0,0)到 (1,1,1)的空间下
