关于Unity Shader 的学习笔记(十三)
最近在B站看庄懂的技术美术入门课(美术向),前面几节刚好讲Shader部分,发觉程序员思维和美术人员的思维不只是不一样那么简单,我的Shader笔记比较偏向程序员思维,从内部实现推导表象,而庄懂的Shader 部分从材质节点连接讲起,从表象推导内部实现,当然到最后还是要从连连看转到代码编写,不过开始的切入点不同,程序员思维就是比较抽象,美术人员的思维其实深了说也比较抽象,不过相对于程序员来说还是比
·
最近在B站看庄懂的技术美术入门课(美术向),前面几节刚好讲Shader部分,发觉程序员思维和美术人员的思维不只是不一样那么简单,我的Shader笔记比较偏向程序员思维,从内部实现推导表象,而庄懂的Shader 部分从材质节点连接讲起,从表象推导内部实现,当然到最后还是要从连连看转到代码编写,不过开始的切入点不同,程序员思维就是比较抽象,美术人员的思维其实深了说也比较抽象,不过相对于程序员来说还是比较容易理解一些,我看了以后感触挺深的,弥补了我很多理解上的不足,感兴趣的可以去看一看。
在前面笔记中我们分别整理了一个物体基本漫反射、高光以及物体表面纹理的shader实现效果,背后的原理需要自己花时间整理清楚,对后面的学习和拓展非常有帮助。下面主要来记录一下凹凸纹理效果是如何实现的。
凹凸纹理主要是用纹理来表现物体表面的凹凸细节,实现主要有两种方法,分别是用高度纹理和法线纹理,对应贴图来讲就是高度置换贴图和法线贴图。
高度纹理:主要存储的是强度值,用于表示模型表面局部海拔高度。定义比较抽象,可以先简单理解为一张黑白灰的贴图,颜色越浅接近白色表示该位置的表面越向外凸起,颜色越深接近黑色表示该位置表面越向里凹。高度纹理计算比较复杂耗费性能,通常和法线贴图一起使用,高度图提供细节,法线来修改光照。高度纹理如下图: 法线纹理:主要存储的是物体表面的法线方向。法线的分量范围在[-1,1],图片像素分量范围在[0,1],所以一般在Shader中对法线纹理采样的时候需要做一个范围映射,公式如下: pixel=(normal+1)/2; 等在Shader中采样后还得经过反映射过程得到原先的法线方向,公式如下: normal=pixel*2-1;

法线纹理既然是存储物体表面法线方向,因此就涉及到坐标空间,根据坐标空间的不同可以将法线分为模型空间法线和切线空间法线。模型空间法线很好理解,就是模型自身空间下模型顶点自带的法线。而对于切线空间法线来说,因为模型的每个顶点都有属于自己的切线空间,这个切线空间的原点就是该顶点的本身,而z轴是顶点法线方向(normal),x轴是顶点的切线方向(tangent),y轴可以由法线和切线叉积得到。模型空间法线(左)和切线空间法线(右)样图如下(图片来源网络): 相较于模型空间法线,切线空间有可UV动画,可压缩等更多优点,因此一般工作中用的是切线空间法线。

下面我们尝试着编写Shader 代码,了解一下实现过程。在Shader 中计算光照需要将各个方向矢量统一在一个坐标空间中,由于法线纹理中存储的法线是切线空间下的方向,因此,通常情况下有两种选择:1、在切线空间下进行光照计算;2、在世界空间下进行光照计算;当然也可以在其他空间下,不过最常用的就是以上两种。 在切线空间下进行光照计算: 基本思路是在顶点函数中将视角方向、光照方向从模型空间变换到切线空间,之后在片元函数中通过纹理采样得到切线空间下的法线,再与切线空间下的视角方向,光照方向等进行计算得到最终的光照结果。这里有一点需要注意的是从模型空间到切线空间的变换矩阵的求法。通过前面的笔记我们知道如果一个变换中仅存在平移和旋转变换,那么这个变换的逆矩阵就等于它的转置矩阵。从切线空间到模型空间的变换矩阵就符合这个要求。因此从模型空间到切线空间的变换矩阵就是从切线空间到模型空间的变换矩阵的转置矩阵,我们将模型顶点的x轴(切线),y轴(副切线),z轴(法线)的顺序按列排列即可得到这个变换矩阵。
在Unity中新建一个Shader、Material,打开Shader清空里面的代码,先把Shader的基本结构写上:

之后我们在Properties中添加需要的属性:

接下来我们完善SubShader 代码块:

顶点函数部分:

片元函数部分:

完整的代码如下: 之后如果没有什么意外情况的话,在场景中新建一个物体将写好的shader应用上去,调整面板参数就可以看见法线贴图的效果了,例图如下:


在世界空间下进行光照计算: 基本思路是在顶点着色器中计算从切线空间到世界空间的变换矩阵,并把它传递给片元着色器。变换矩阵的计算可以由顶点切线、副切线和法线在世界空间下的表示得到。最后只需要在片元着色器中把法线纹理中的方法从切线空间变换到世界空间下即可。虽然这种方法需要更多计算,但是在需要使用Cubemap进行环境映射的情况下,就需要使用这种方法。 我们直接改写上面的shader代码,完整代码如下:

两种空间下的计算从效果上看基本看不出什么差别, 然而 string str1="世界空间下进行计算"; string str2="切线空间下进行计算"; string str3=“需要用Cubemap进行环境映射”; string result=string.Empty; result=string.Equals(result,str3)?str1:str2;
接下来看一下法线贴图导入面板属性,如下图: 在切线空间计算中我们没有将纹理类型设置为Normal map,在世界空间下进行计算时我们设置了纹理类型为Normal map. 当我们将纹理类型设置为Normal map后,会有一个复选框Create from Grayscale,这个的作用就是之前提到的从高度图中生成法线纹理,当我们把一张法线贴图标识为Normal map类型后,需要勾选这个选项。当勾选了Create from Grayscale后还会多出Bumpiness和Filtering两个选项,Bumpiness 用来控制凹凸程度,Filtering 决定了用哪种方式计算凹凸程度,有Smooth和Sharp两个选项,这里先不做深究,根据需要按字面意思选择即可。

更多推荐
所有评论(0)