欢迎光临散文网 会员登陆 & 注册

每天一个shader技巧 day1 - 从图片生成法线贴图 shadertoy实现

2023-02-21 18:11 作者:DaydreamLin  | 我要投稿

前置知识

法线贴图,图形学,向量运算,导数等

算法

先不去想如何从一张纹理去生成法线,因为图片是一个二维的存储结构,处理起来的话会比较复杂,写成方程的形式则是 f(u,v) = pixel color 

我们进行一个降维处理,假设我们现在有段曲线,f(x) = y,这样变量就变为一维的了。那么对于一个曲线,我们如何获取曲线一个点上的法线呢,自然可以想到对该函数求一阶导,因为某个点一阶导的结果为过这个点切线的斜率,那么该点的法线的斜率与切线的斜率乘积。现在我们知道点,知道斜率,自然可以求得法线的方程。

看看图

扩展到二维的话,也是同理,先对一个点的一个方向求出法线,再对另一个方向求出法线即可。但是!我们既然在这个过程中会求得两个切线,那直接做一个叉乘即可获得垂直于这两个向量的第三个向量,即是法线,所以思路就清晰了。

求切线->算法线

但是按这个思路对一张纹理进行运算会有一个问题,那就是纹理储存的是RGB值,这个值和法线是没有关系的,所以我们需要一张描述该纹理的高度图,然后用该高度图去求法线向量。

那没有高度图怎么办呢,那就用一个神奇的公式,奖图片转为灰度图(不要问我这个公式怎么来的,只能说基于经验)

pixel.r%20*%200.2126%20%2B%20pixel.g%20*%200.7152%20%2B%20pixel.b%20*%200.0722

代码如下

看看效果

原图
灰度图(当成高度图)

求切线

有了高度图之后就要求切线了。因为一张图是由许多个像素点组成的,所以是离散的,那么就需要用到导数的证明过程那个极限的写法了。

f'(x)%20%3D%20%5Clim_%7Bx%5Cto0%7D%20%5Cfrac%7Bf(x%20%2B%20%5CDelta%20x)%20-%20f(x)%7D%7B%5CDelta%20x%7D%20

但是在实际计算中,我们要的是切线向量,而这个求的是斜率,不是我们想要的。那么切线向量怎么构造呢,回到一维的情况,假设我们有切线方程 f(x)%20%3D%20kx%20%2B%20b,那么切线向量则是vector2(%5CDelta%20x%2C%20f(x2)%20-%20f(x1))

那么回到二维的情况,我们也可以分别对x, y去进行一个切线向量的构造

vector3(%5CDelta%20x%2C%200%2Cf(x2)%20-%20f(x1))

vector3(0%2C%5CDelta%20y%2C%20f(y2)%20-%20f(y1))

获得这两个向量后进行一个叉乘便可获得法线向量了。代码如下

代码中有几个要注意的点,首先是生成法线的函数,多了两个参数分别来scale采样的步长和高度。步长好理解,控制生成法线的精度。那为什么需要高度的scale系数呢,因为对图片进行采样的时候,我们所选取的步长是非常小的%5CDelta%20(1/1024),但是高度差却很大(最大可能到1.0),这是远大于%5CDelta%20的,这就会导致切线向量过于接近Z轴,计算出来的法线x,y的值就很大了,所以需要引入一个scale系数来进行高度差的缩放。

最后将处于(-1,-1,-1)到(1,1,1)的法线转化到(0,0,0)到 (1,1,1)的空间下

效果图


每天一个shader技巧 day1 - 从图片生成法线贴图 shadertoy实现的评论 (共 条)

分享到微博请遵守国家法律