首页 > 其他分享 >法线纹理

法线纹理

时间:2024-09-07 12:51:59浏览次数:11  
标签:法线 end 切线 begin 纹理 bmatrix 顶点

切线空间

法线纹理用来呈现物体表面的凹凸细节,模型顶点自身的法线定义于模型空间(Object Space)中,模型的法线纹理一般存储在模型顶点的切线空间(Tangent Space)中,一般的,顶点本身为切线空间原点,选择顶点法线方向\(n\)为切线空间的正方向\(z\),法线贴图的\(u\)方向为切线方向\(t\),法线贴图的\(v\)方向为副切线方向\(b\)。

可以通过三角形顶点的位置和其UV坐标推导出切线方向\(t\)和副切线方向\(b\),如下图,\(P_i\)为三角形的顶点,\((u_i,v_i)\)为UV坐标,其中

\(du_1=u_1-u_0\)
\(du_2=u_2-u_0\)
\(dv_1=v_1-v_0\)
\(dv_2=v_2-v_0\)

\(Q_1=P_1-P_0\)
\(Q_2=P_2-P_0\)

由此可见:

\(Q_1=du_1t+dv_1b\)
\(Q_2=du_2t+dv_2b\)

将上式写为矩阵形式:

\(\begin{bmatrix} Q{_1}_x&Q{_1}_y&Q{_1}_z\\ Q{_2}_x&Q{_2}_y&Q{_2}_z \end{bmatrix}= \begin{bmatrix} du_1&dv_1\\ du_2&dv_2 \end{bmatrix} \begin{bmatrix} T_x&T_y&T_z\\ B_x&B_y&B_z \end{bmatrix}\)

对\(\begin{bmatrix} du_1&dv_1\\ du_2&dv_2 \end{bmatrix}\)求逆,可得

\(\begin{bmatrix} T_x&T_y&T_z\\ B_x&B_y&B_z \end{bmatrix}= \frac{1}{du_1dv_2-dv_1du_2} \begin{bmatrix} dv_2&-dv_1\\ -du_2&du_1 \end{bmatrix} \begin{bmatrix} Q{_1}_x&Q{_1}_y&Q{_1}_z\\ Q{_2}_x&Q{_2}_y&Q{_2}_z \end{bmatrix}\)

通过对\(T\)和\(B\)规范化后得到切线方向\(t\)和副切线方向\(b\)。

因为三角面是一个平面,只需要计算一个三角面的切线和副切线即可,它们对于三角形的顶点来说都是一样的,被多个三角面共享的顶点只需要将多个结果平均化即可。

采样计算

存储与采样

纹理颜色\(c\)各分量的范围为\([0,1]\),而法线向量\(n\)各分量的范围为\([-1,1]\),故在存储时需要做\(c=\frac{n+1}{2}\)的映射,在纹理采样时做\(n=2c-1\)的映射。

法线非统一缩放问题

在模型变换时,跟顶点相关的信息也需要一并变换,其中包括法线与切线等,由切线空间部分可知顶点的切线通过顶点之间的差值计算得来,根据线性变换的性质,用于顶点变换的矩阵\(M\)(矩阵\(M\)不包含平移)同样可以用于变换切线向量,但如果\(M\)不是正交矩阵,对变换后的空间可能在某些方向上产生“挤压”或“拉伸”,所以无法保证法线向量与原来的表面垂直。

因为切线向量\(t\)与法线向量\(n\)始终保持平行,所以对于同一个顶点来说满足\(n\cdot t=0\),变换后的\(t'\)和\(n'\)也应满足该等式,假设顶点的变换矩阵为\(M\),法线向量的变换矩阵为\(G\),则

\(n'\cdot t'=(Gn)\cdot (Mt)=0\)

将\((Gn)\cdot (Mt)\)展开可得

\(\begin{bmatrix} G{_i}_x&G{_j}_x&G{_k}_x\\ G{_i}_y&G{_j}_y&G{_k}_y\\ G{_i}_z&G{_j}_z&G{_k}_z \end{bmatrix} \begin{bmatrix} n_x\\n_y\\n_z \end{bmatrix} \cdot \begin{bmatrix} M{_i}_x&M{_j}_x&M{_k}_x\\ M{_i}_y&M{_j}_y&M{_k}_y\\ M{_i}_z&M{_j}_z&M{_k}_z \end{bmatrix} \begin{bmatrix} t_x\\t_y\\t_z \end{bmatrix}= \begin{bmatrix} n_xG{_i}_x+n_yG{_j}_x+n_zG{_k}_x\\ n_xG{_i}_y+n_yG{_j}_y+n_zG{_k}_y\\ n_xG{_i}_z+n_yG{_j}_z+n_zG{_k}_z \end{bmatrix} \cdot \begin{bmatrix} t_xM{_i}_x+t_yM{_j}_x+t_zM{_k}_x\\ t_xM{_i}_y+t_yM{_j}_y+t_zM{_k}_y\\ t_xM{_i}_z+t_yM{_j}_z+t_zM{_k}_z \end{bmatrix}\)

由此可见

\((Gn)\cdot (Mt)=(Gn)^T(Mt)=n^TG^TMT\)

由于\(n^Tt=n\cdot t=0\),如果\(G^TM=I\),则\(n^TG^TMT=0\),由此可知\(G=(M^{-1})^T\)。

若\(M\)为正交矩阵,则\(M^{-1}=M^T\),可知\((M^{-1})^T=M\),此时可避免求逆和转置的操作。

在世界空间中采样计算法线

在顶点着色器中计算世界空间下的法线向量normalWS、切线向量tangentWS和副切线向量bitangentWS,在计算normalWS时,变换顶点到世界空间的矩阵\(M_{object\rightarrow world }\)其逆矩阵\(M_{object\rightarrow world }^{-1}\)为unity_WorldToObject,使用模型空间的法线向量normalOS左乘\(M_{object\rightarrow world }^{-1}\),相当于右乘其转置矩阵\((M_{object\rightarrow world }^{-1})^T\)。

half3 normalWS = normalize(mul(input.normalOS, (float3x3)unity_WorldToObject));
half3 tangentWS = normalize(mul((float3x3)UNITY_MATRIX_M, input.tangentOS.xyz));
half3 bitangentWS = cross(normalWS, tangentWS) * input.tangentOS.w;

在片元着色器中采样切线空间下的法线向量normalTS,由于纹理格式存在差异,使用Shader Lab中内置函数UnpackNormal()对存储的法线进行反映射,然后使用顶点着色器传入的切线向量tangentWS、副切线向量bitangentWS和法线向量构成变换矩阵\(G_{tangent\rightarrow world}\),也即构成新的坐标空间,这里注意half3x3(v1,v2,v3)是以行向量构建矩阵,故将切线空间的法线向量normalTS变换至世界空间时,需要使用normalTS左乘矩阵\(G_{tangent\rightarrow world}\)。

half3 normalTS = UnpackNormal(tex2D(_NormalMap, input.uv));
half3x3 tangentToWorld = half3x3(input.tangentWS.xyz, input.bitangentWS.xyz, input.normalWS.xyz);
half3 normalWS = mul(normalTS.xyz, tangentToWorld);

Unity中的切线设置

Unity默认使用 MikkTSpace 计算切线,在Model Import Settings中可进行设置。

切线最常用于凹凸贴图着色器中。切线是单位长度的矢量,它顺着网格表面沿水平 (U) 纹理方向。Unity 中的切线表示为 Vector4, 其“x,y,z”分量定义矢量,而 w 用于在需要时翻转副法线。Unity 计算另一个表面矢量(副法线)的方法是获取法线与切线之间的叉积,然后将结果乘以切线的 w。因此,w 应始终为 1 或 -1。

参考

《3D游戏与计算机图形学中的数学方法》

《Unity Shader入门精要》

法线贴图

标签:法线,end,切线,begin,纹理,bmatrix,顶点
From: https://www.cnblogs.com/WoBok/p/18401563

相关文章

  • 第109期 DAGM2007纹理背景缺陷数据集
    引言亲爱的读者们,您是否在寻找某个特定的数据集,用于研究或项目实践?欢迎您在评论区留言,或者通过公众号私信告诉我,您想要的数据集的类型主题。小编会竭尽全力为您寻找,并在找到后第一时间与您分享。一、研究背景随着计算机视觉技术的不断发展,缺陷检测在工业质量控制、医学影像分析......
  • 绘制ply模型顶点的法线(通过两点)
    importtrimeshimportmatplotlib.pyplotasplt#读取点云文件mesh=trimesh.load_mesh('test.ply')#计算法线mesh.vertex_normals#创建一个新的图形窗口fig=plt.figure()ax=fig.add_subplot(111,projection='3d')#绘制顶点ax.scatter(mesh.vertices[:,......
  • 计算ply模型的法线及显示
    importopen3daso3dimportnumpyasnp#加载点云数据pcd=o3d.io.read_point_cloud("test.ply")#设置法线估计的搜索参数search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1,max_nn=30)#计算点云的法线pcd.estimate_normals(search_param=search_......
  • Qt+OpenGL混合两个纹理,并可以改变纹理透明度
    一、概述需求:1.将两纹理进行融合,改变其中一个透明度让其渐渐显示或隐藏2.控制x轴像素互换,让笑脸达到向左看或向右看的目的。 大部分的代码都是常规代码。还是那一套流程:1.定义顶点、纹理、顶点索引坐标。2.创建并绑定VAO、VB......
  • QT+OpenGL纹理与颜色混合
    一、概述使用OpenGL将纹理与颜色混合输出。ps:此处并无实际的意义只是单纯的为了好玩。步骤:1.定义顶点坐标、颜色、纹理及顶点索引floatvertices[]={//----位置---------颜色------纹理坐标-1.0f,1.0f,0.0f,1.0......
  • 八、OpenTK中的纹理操作(万字详细教程)
    文章目录一、纹理加载和创建(一)从图像文件加载纹理(如PNG、JPEG)(二)生成程序纹理(如噪声纹理、棋盘格纹理)二、纹理坐标(一)2D纹理坐标计算(二)3D纹理坐标映射(三)纹理坐标环绕和过滤模式三、纹理单元(一)、多纹理的使用(二)、纹理混合和叠加(三)、立方体贴图纹理......
  • QT+OpenGL简单纹理贴图
    一、概述之前的文章都是绘制一些纯色的三角形和正方形以及控制他们的相对位置。在实际的开发中一般都需要给绘制出的图形贴上漂亮的纹理。纹理贴图步骤:1.创建纹理glGenTextures(1,&texture);//参数1:创建纹理的数量,参数2:用户定义的纹理id,一般为unsignedint......
  • PCL 法线微分分割(Don)
    1.原理对于点云中的每个点云点P,使用一大一小两个半径计算该点的两个法向量,然后用两个单位法向量的差异计算Don特征。如下图:图1为大半径计算得到的法向量;图2为小半径计算得到的法向量;图3为一大一小两个法向量计算得出的Don特征。步骤:(1)建KD树,计算点云平均密度(即点云点之间......
  • Unity Editor 保存图片、缩放纹理
    usingSystem.IO;usingUnityEditor;usingUnityEngine;publicclassConvertIconToMultipleSizes:Editor{[MenuItem("Assets/ConvertIconToMultipleSizes",true)]privatestaticboolValidateSplitFbxAnimation(){if(Selection.......
  • Opengl: 使用glsl 将纹理处理成圆角的几种思路
    在GLSL中实现纹理圆角效果可以通过多种方法,这些方法各有其优缺点和适用场景。以下是几种常见的实现方法:1.使用距离函数最常见的方法是计算像素到中心的距离,并使用该距离来决定像素是否显示。#version330coreinvec2TexCoord;outvec4FragColor;uniformsampler2Dte......