1、_TexelSize
:纹理以这个为后缀, 后2个则是像素的数量如256x128。那么后2个就是256和128。而前两个值为x = 1/256, y = 1 / 128
2、有限差分近似
根据这个图来理解编写,利用两点之间切线表示其深度
void InitializeFragmentNormal(inout Interpolator i){
float2 delta = float2(_HeightMap_TexelSize.x, 0); //如何贴图为256x128的 那么delta = 1 / 256
float h1 = tex2D(_HeightMap, i.uv); //普通采样
float h2 = tex2D(_HeightMap, i.uv + delta); //向后挪一个像素采样
// y结合上图, 会发现是斜率
//i.normal = float3(1, (h2-h1)/delta.x, 0);
或者 i.normal = float3(delta.x, h2-h1, 0);
i.normal = normalize(i.normal);
}
注意此时得到是切线, 并不是真正的法线, 而是切线,也就是图中红色的部分
真正的法线绕旋z轴90度i.normal = float3(h1-h2, 1, 0);
这个旋转怎么理解?
得到的这个切线由于delta很小可以理解为(0, h2-h1, 0),然后右乘z轴旋转的旋转矩阵,其中旋转角度为90度。最终得到(h1-h2, 0, 0),而y轴变成了1,是为了让没有那么明显的差异感,进行了缩放。
3、中心差法
:教程中提到了中心差法:
其实就是不在原来的顶点处直接取值为h1,而是向后偏移原来的一般。但是x的总距离是没有变化的
//中心差法
float2 delta = float2(_HeightMap_TexelSize.x * 0.5, 0);
float h1 = tex2D(_HeightMap, i.uv - delta);
float h2 = tex2D(_HeightMap, i.uv + delta);
i.normal = float3(h1 - h2, 1, 0);
效果是更好与高度场对齐, 并不会改变形状
注意:此时是x轴向维度的, 并没有y轴维度的
4、添加uv中的v的维度,此时平面时yz面
//另一个维度
float2 deltaV = float2(0, _HeightMap_TexelSize.y * 0.5);
float v1 = tex2D(_HeightMap, i.uv - deltaV);
float v2 = tex2D(_HeightMap, i.uv + deltaV);
//切线为 float3(0, v2-v1, deltaV) =>(缩放) float3(0, v2-v1, 1)
//绕x轴转-90度, 这里右乘的是x轴旋转矩阵
float3(0, 1, v1-v2)
i.normal = float3(0, 1, v1-v2);
5、通过UV2个维度的切线构造生产法向量
float3 tu = float3(1, h2-h1, 0); //u切线
float3 tv = float3(0, v2-v1, 1); //v 方向的切线
//通过叉乘 可以确定垂直于这2条切线的向量 从而成为法向量
i.normal = cross(tv, tu);
注意:
tv和tu两个不能互换, 因为叉乘有2个方向
6、法线贴图
假如有多个纹理图,我们需要进行差分近似采样就需要进行多次,这样很浪费。所以我们将纹理图设置为Normal Map,并勾选Create From Grayscale应用。将得到法线贴图(法线贴图为什么是便蓝色的?因为其将向上的向量存储在z变量,对应RGB中的Blue颜色
)
7、使用法线贴图
//非DXT5 移动端
i.normal = tex2D(_NormalMap, i.uv).xyz * 2 - 1; //将其归一至 -1 ~ 1
i.normal = i.normal.xzy; //zy交换
//支持DXT5
i.normal.xy = tex2D(_NormalMap, i.uv).wy * 2 - 1; //DXT5只用2个通道存储,x存储在a通道, y存储在y
//根据上图,法线是单位向量,所有距离是1,可以通过xy计算出z
i.normal.z = sqrt(1 - saturate(dot(i.normal.xy, i.normal.xy)));
i.normal = i.normal.xzy;
8、缩放凹凸
向上的向量存储在z方向向量中,所以我们可以缩放xy从而达到缩放z的效果
// _BumpScale = 0时平面将会变得平坦, 越大将会越凹凸
i.normal.xy = tex2D(_NormalMap, i.uv).wy * 2 - 1; //将其归一至 -1 ~ 1
i.normal.xy = _BumpScale * i.normal.xy;
其中Unity自带函数
//使用unity自带的函数 进行法线纹理的解码和缩放
i.normal = UnpackScaleNormal(tex2D(_NormalMap, i.uv), _BumpScale);
i.normal = i.normal.xzy;
9、细节法线
同样细节法线和普通法线纹理差不多,只是利用细节uv进行采样而已
//细节法线贴图
i.normal = UnpackScaleNormal(tex2D(_DetailNormalMap, i.uv.zw), _DetailBumpScale);
10、融合法线
:BlendNormals可以将2个法线混合,并且使用的是泛白混合(原理用得到了再看原理)
//细节法线贴图
float3 detailNormal = UnpackScaleNormal(tex2D(_DetailNormalMap, i.uv.zw), _DetailBumpScale);
i.normal = BlendNormals(mainNormal, detailNormal);
i.normal = i.normal.xzy;
// i.normal = normalize(i.normal);
此处连接切线空间文章