切线空间?切线空间的作用到底是什么?

什么是切线空间?

TangentSpace 就是一个 原点+三个坐标轴决定的一个相对空间。在Tangent Space中,坐标原点就是顶点的位置,一个坐标轴是该顶点本身的法线方向(N)。另外两个坐标轴就是和该点相切的两条切线,这样的切线有无数条,但是模型一般会给定该顶点的一个tangent,这个tangent方向一般是使用和纹理坐标方向相同的那条tangent(T)切线向量。而另一个坐标轴的方向(B)副切线方向,通过叉乘法线向量和和切线向量得到副切线向量。

什么是法线贴图?

Normal Mapping 记录法线信息的载体被称为法线贴图。为什么是Texture呢?一条法线是一个三维的向量,分别由x y z三个向量构成,于是前辈就以这三个分量当做红 黄 蓝三个颜色的存储值,这样便可以使用一张贴图来表示了,这就是我理解的法线贴图。
通常我们一般简单的法线贴图其实存储的是,每个顶点切线空间中法线的扰动方向。例如一个顶点的法线方向不变,那么在切线空间中它的值就是Z轴方向(0,0,1)。但是需要注意的是这并不是在法线贴图中存储区的真正的值,为什么?我们来思考一个问题。一个向量的维度是在【-1,1】之间的,而贴图的每个通道的值0-255是映射到【0,1】之间的, 因此想要利用一个贴图来存储一个向量的信息是需要通过一个映射 pixel = (normal + 1) / 2。这样例如之前的(0,0,1)向量存储到贴图RBG值就位(0.5,0.5,1),这个颜色值就是法线贴图当中蓝色的部分。这里也就相当于解释了为什么法线贴图当中为什么大部分是蓝色的值?我们知道在切线空间中Z轴朝上,X轴一般指向表面切线或者可以理解为UV中的U方向,而Y轴一般指向表面副切线或者可以理解为UV中的V方向。而法线一般都指向表面外侧,所以一般来说值都会在【0.5-1】之间,而这个值存储早RGB中的Blue通道当中,所以我们肉眼看到的一般都是偏蓝色。

为什么要有切线空间?

基于模型坐标系的法线贴图,在换到不同的模型下时候法线贴图不能复用。模型空间下的法线记录的是绝对法线信息,只能用于特定模型;而切线空间是相对信息。如果使用模型空间下的法线贴图,应用到另外一个模型上面,解析出来的法线值可能就不对了。而切线空间中的法线与纹理坐标在同一空间,也就说只要我们同时将漫反射等纹理贴图和法线贴图同时应用到另一个模型上,法线贴图中的值经过变换后依然是正确的。
UI动画,前者不能有正确的UV动画,后者可以
资源压缩,Tangent-Space的法线图,z方向总是正方向,可以仅存储XY方向,推导得到Z方向,这样的话可以进行压缩,通过时间换取空间

如何计算切线空间?

重要的不是切线空间,而是后续的光照计算

v2f Vertex(a2v v) {
    
    
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				//法线
				fixed3 worldNormal = normalize(v.normal);
				//切线
				fixed3 tangent = normalize(v.tangent.xyz);
				//副切线
				float3 binomal = cross(worldNormal,tangent);
				//模型到切线空间变换矩阵
				float3x3 _2tangentSpace = float3x3(tangent, binomal, worldNormal);
				//float3 ObjSpaceViewDir(float4 v)模型空间从这个点到摄像机的观察方向 =>光源方向变换到切线空间
				o.lightDir = mul(_2tangentSpace, ObjSpaceViewDir(v.vertex).xyz);
				return o;
			}
			fixed4 Fragment(v2f i) : SV_TARGET{
    
    
				fixed3 tangentLightDir = normalize(i.lightDir);
				//对法线纹理进行采样
				fixed4 packedNormal = tex2D(_BumpTex, i.uv);
				//所谓凹凸映射(Bump Mapping)就是通过一张纹理贴图来轻微的修改模型表面的法线,让它可以为模型提供更多的细节
				//使用法线纹理中的法线来代替模型中的法线进行光照计算 
				fixed3 tangentNormal;
				//一个法线的分量范围是[-1,1],然而一个纹理只能储存[0,1]的值,所以需要做一个映射 (+1)/2 
				//所以在Shader中进行采样后需要进行一个反映射 * 2 - 1
				tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
				//dot点积操作就是(x,y)(x,y) = x^2 + y^2		
				tangentNormal.z = sqrt(1-saturate(dot(tangentNormal.xy,tangentNormal.xy)));

				fixed3 albedo = tex2D(_MainTex,i.uv).xyz * _BaseColor.xyz;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.xyz * albedo * saturate(dot(tangentLightDir,tangentNormal));
				return fixed4(diffuse + ambient,1.0);
			}

猜你喜欢

转载自blog.csdn.net/qq_39691716/article/details/126371874