shader 获取法线_【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图

【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图

写作本系列文章时使用的是Unity5.3。

写代码之前:

当然啦,如果Unity都没安装的话肯定不会来学Unity Shaders吧?

阅读本系列文章之前你需要有一些编程的概念。

在VS里面,Unity Shaders是没有语法高亮显示和智能提示的,VS党可以参考一下这篇文章使代码高亮显示,也可以下载shaderlabvs或NShader之类的插件使代码高亮显示。

这是针对小白的Unity Shaders的基础知识,如果你已经有了基础或者你是大神,那么这些文章不适合你。

由于作者水平的局限,文中或许会有谬误之处,恳请指出。

法线贴图是一种在低模上模拟高模的效果的技术。这是维基对它的介绍。

法线贴图类似凹凸贴图的升级版,凹凸贴图记录了物体表面凹凸的情况,法线贴图记录了物体表面凹凸的光照信息。光照信息即是入射光与法线的夹角信息。

为了提高性能,模型的面数越少越好,很多细节的东西都是用贴图去弥补。但是光照是基于顶点去计算的,这样高光阴影等光照的表现就不够真实。于是前辈们发明了法线贴图这个办法,用贴图记录表面的光照信息,也就是用RGB值存储法线坐标的XYZ值,使低模也能够有高模的光照信息,从而表现出高模的光照效果。这是一种存储空间换计算时间的方法。这篇文章介绍了3DMax制作法线贴图的方法,看完应该会更理解法线贴图的作用。

下面这张图便说明了法线贴图的原理:

接下来就来学习如何编写有法线贴图的Shader吧。

首先准备一张NormalMap,要将它的Texture Type改为Normal Map:

先定义几个Properties:

Properties

{

//Add these Properties

_MainTint ("Diffuse Tint", Color) = (1,1,1,1)

_NormalTex ("Normal Map", 2D) = "bump" {}

_NormalIntensity ("Normal Map Intensity", Range(0,2)) = 1

}

_NormalTex就是法线贴图,_NormalIntensity是法线的强度,也就是控制凹凸的强度。

在SubShader里定义同名的变量:

//Link the property to the CG program

sampler2D _NormalTex;

float4 _MainTint;

float _NormalIntensity;

在Input结构里定义法线贴图的纹理坐标:

//Make sure you get the uvs for the texture in the Struct

struct Input

{

float2 uv_NormalTex;

};

编写surf函数:

void surf (Input IN, inout SurfaceOutput o)

{

//Get the normal Data out of the normal map textures

//using the UnpackNormal() function.

float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));

normalMap = float3(normalMap.x * _NormalIntensity,

normalMap.y * _NormalIntensity, normalMap.z);

//Apply the new normals to the lighting model

o.Normal = normalMap.rgb;

o.Albedo = _MainTint.rgb;

o.Alpha = _MainTint.a;

}

解释

法线贴图中记录了高模的法线信息。法线的值是[-1,1],RGB的值是[0,1],所以将法线信息存储在RGB中需要转换。方法是((x,y,z)+(1,1,1))*0.5。也就是原先(-1,0,1)坐标变成了(0,0.5,1)。UnpackNormal函数的作用就是转换坐标点。

在Unity安装目录(/Editor/Data/CGIncludes/)下的Unity.cginc文件里有UnpackNormal函数的定义。

inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)

{

fixed3 normal;

normal.xy = packednormal.wy * 2 - 1;

normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));

return normal;

}

inline fixed3 UnpackNormal(fixed4 packednormal)

{

#if defined(UNITY_NO_DXT5nm)

return packednormal.xyz * 2 - 1;

#else

return UnpackNormalDXT5nm(packednormal);

#endif

}

我们看到,有两种转换法线的函数,这两种函数是针对不同法线贴图格式的。

如果是未压缩的格式,就将法线贴图里存储的xyz值乘2减1,变回原来的坐标。

如果是DXT5nm压缩格式(可以推测这应该是Unity使用的压缩格式),则要用另外一种转换方法。(这也是为什么要将纹理类型设置为Normal Map的原因,因为不同类型的图片要用不同的方法转换)

DXT5nm压缩格式,只有G和A通道。因为法线是单位向量,所以只要知道任意两个坐标值,就能求出另一个坐标值(z2=1-x2-y2),只要两个通道存储两个坐标值即可。因为这样的计算并不复杂,所以可以节省空间以使用更多的贴图。DXT5nm将R通道的值移到了Alpha通道,保留G通道的值,R和B通道则以某种颜色填充。

所以转换DXT5nm的法线贴图,要先将A通道和G通道的值(wy)转换为xy,再计算z值。

后面的代码就好解释了。转换了法线坐标以后将法线赋值给输出结构体的Normal变量。因为法线(0,0,1)就表明这点的法线和高模的法线相同,所以Z坐标没有必要乘_NormalIntensity。

法线贴图原理

法线贴图的基本原理前面已经讲过了,就是将高模的法线信息xyz存储在rgb中,应用法线贴图的时候将rgb值转换为xyz,在计算光照的时候,使用高模的法线进行计算,从而得到高模的光影效果,造成低模呈现高模较为平滑的表面的假象,这是一种视觉欺骗。

这个小节里要再详细谈谈法线的坐标。

要表示一个向量在三维空间的位置需要一个三维坐标系作参考。要表示法线的方位可以用世界坐标(World Space),也可以用模型坐标(Object Space)。这两个坐标系大家应该听说过。

如果用世界坐标,那么模型就不能旋转和移动了。因为模型的位置改变了,它在世界坐标系里的坐标也改变了,但法线的坐标是基于世界坐标系的,这样法线的位置就不对了。不然读取了法线信息还要再进行世界坐标的转换。

如果用模型坐标,就没有世界坐标不能旋转和移动的局限了。不管模型怎么旋转移动,法线的位置是相对于模型的,所以法线的坐标不会变。相比于世界坐标,使用模型坐标还要进行模型坐标到世界坐标的转换,效率低点,但灵活性更好了。

但是使用模型坐标还有局限,就是模型不能变形。因为模型变形了,法线相对模型的坐标也变了,一些会有形变的物体(比如有骨骼蒙皮动画的模型)就不适用基于模型坐标的法线贴图。

所以还需要另外一种坐标系,使法线贴图不仅仅适用于某个模型,还可以用在其他模型上。

这种坐标系就是切线坐标(Tangent Space)。切线坐标就以表面上某一点的切线作为XY轴,法线作为Z轴(即垂至表面的轴)。这样的话,符合要求的坐标系有无数种。我们可以直接将纹理的UV坐标当作XY轴,这样就有一个现成的坐标系可以使用。

可以这样理解切线坐标,它表示的是高模上的法线相对对应点上的低模的法线的扰动程度。当法线坐标是(0,0,1)的时候,说明高模法线和低模法线一致,相当于切线坐标表示的是高模法线相对低模法线在XY方向上的偏移程度。这也解释了上文代码中为什么Z轴不用乘_NormalIntensity。

法线贴图是用于表现表面微小的凹凸的,比如皮肤皱纹、鱼鳞等,所以法线的扰动值不会太大,也就是Z坐标的值是比较大,所以B通道的值比较大,所以法线贴图通常都是蓝盈盈的。

使用切线坐标也就是说每个面都有一个自己的坐标系。在Vertex Shader里进行光照计算的时候要将光线向量的坐标从World Space转换到Tangent Space。所以我们可以在Surface Shader里使用lightDir变量和normal变量进行计算,因为Unity帮我们做了很多工作。

相比模型坐标,切线坐标更加灵活,可以应用在与原模型形状不同的模型上,比如可以把花岗岩的法线贴图应用在一个圆柱体表面,使圆柱体表面也具有花岗岩的凹凸效果。

法线贴图通常有两种,一种是Object Space Normal Map,一种是Tangent Space Normal Map。如果模型没有动画,那么可以使用Object Space Normal Map,如果模型有动画,或者会变形(比如流动的水、火焰)那就要使用Tangent Space Normal Map。因为Tangent Space Normal Map要灵活的多,可以应用于不同的物体,所以Tangent Space Normal Map使用得更多一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值