use*_*230 43 opengl math 3d glsl
在OpenGL着色语言(GLSL)中讨论凹凸贴图,镜面高光和这些东西
我有:
现在,我如何计算每个顶点的Binormal和Tangent?我的意思是,计算Binormals的公式是什么,我必须根据这些信息使用什么?和切线相关?
无论如何我都会构建TBN矩阵,所以如果你知道根据这些信息直接构造矩阵的公式会很好!
哦,是的,如果需要,我也有纹理坐标.正如我所说的GLSL,我认为,每个顶点解决方案都是不错的,它不需要一次访问多个顶点信息.
----更新-----
我找到了这个解决方案
vec3 tangent;
vec3 binormal;
vec3 c1 = cross(a_normal, vec3(0.0, 0.0, 1.0));
vec3 c2 = cross(a_normal, vec3(0.0, 1.0, 0.0));
if (length(c1)>length(c2))
{
tangent = c1;
}
else
{
tangent = c2;
}
tangent = normalize(tangent);
binormal = cross(v_nglNormal, tangent);
binormal = normalize(binormal);
但我不知道它是否100%正确.
dat*_*olf 38
问题的相关输入数据是纹理坐标.Tangent和Binormal是局部平行于对象表面的向量.在正常映射的情况下,它们描述了正常纹理的局部方向.
因此,您必须计算纹理向量指向的方向(在模型的空间中).假设您有一个三角形ABC,纹理坐标为HKL.这给了我们矢量:
D = B-A
E = C-A
F = K-H
G = L-H
Run Code Online (Sandbox Code Playgroud)
现在我们想用切线空间T,U表示D和E.
D = F.s * T + F.t * U
E = G.s * T + G.t * U
Run Code Online (Sandbox Code Playgroud)
这是一个具有6个未知数和6个方程的线性方程组,它可以写成
| D.x D.y D.z | | F.s F.t | | T.x T.y T.z |
| | = | | | |
| E.x E.y E.z | | G.s G.t | | U.x U.y U.z |
Run Code Online (Sandbox Code Playgroud)
反转FG矩阵产生
| T.x T.y T.z | 1 | G.t -F.t | | D.x D.y D.z |
| | = ----------------- | | | |
| U.x U.y U.z | F.s G.t - F.t G.s | -G.s F.s | | E.x E.y E.z |
Run Code Online (Sandbox Code Playgroud)
与顶点法线T和U一起形成局部空间基础,称为切线空间,由矩阵描述
| T.x U.x N.x |
| T.y U.y N.y |
| T.z U.z N.z |
Run Code Online (Sandbox Code Playgroud)
从切线空间转换为对象空间.要进行照明计算,需要与此相反.通过一点运动,我发现:
T' = T - (N·T) N
U' = U - (N·U) N - (T'·U) T'
Run Code Online (Sandbox Code Playgroud)
归一化矢量T'和U',将它们称为切线和副法线,我们得到矩阵从物体转换到切线空间,我们在那里进行照明:
| T'.x T'.y T'.z |
| U'.x U'.y U'.z |
| N.x N.y N.z |
Run Code Online (Sandbox Code Playgroud)
我们将T'和U'与顶点法线一起存储为模型几何的一部分(作为顶点属性),以便我们可以在着色器中使用它们进行光照计算.我再说一遍:你没有在着色器中确定切线和副法线,你预先计算它们并将它们存储为模型几何的一部分(就像法线一样).
(上面的垂直条之间的符号都是矩阵,从不是决定因素,它们的符号通常使用竖线而不是括号.)
kva*_*ark 17
通常,您有两种生成TBN矩阵的方法:离线和在线.
使用派生指令在片段着色器中在线 =右.这些推导为多边形的每个点提供了平坦的TBN基础.为了得到平滑的,我们必须基于给定的(平滑的)顶点法线重新正交化它.这个过程在GPU上比初始TBN提取更加繁重.
// compute derivations of the world position
vec3 p_dx = dFdx(pw_i);
vec3 p_dy = dFdy(pw_i);
// compute derivations of the texture coordinate
vec2 tc_dx = dFdx(tc_i);
vec2 tc_dy = dFdy(tc_i);
// compute initial tangent and bi-tangent
vec3 t = normalize( tc_dy.y * p_dx - tc_dx.y * p_dy );
vec3 b = normalize( tc_dy.x * p_dx - tc_dx.x * p_dy ); // sign inversion
// get new tangent from a given mesh normal
vec3 n = normalize(n_obj_i);
vec3 x = cross(n, t);
t = cross(x, n);
t = normalize(t);
// get updated bi-tangent
x = cross(b, n);
b = cross(n, x);
b = normalize(b);
mat3 tbn = mat3(t, b, n);
Run Code Online (Sandbox Code Playgroud)离线 =准备切线作为顶点属性.这更难以获得,因为它不仅会添加另一个顶点属性,还需要重新组合所有其他属性.此外,它不会100%为您提供更好的性能,因为您将获得存储/传递/动画(!)vector3顶点属性的额外成本.
数学在许多地方描述(google it),包括@datenwolf帖子.
这里的问题是2个顶点可能具有相同的法线和纹理坐标但是不同的切线.这意味着您不能只是将顶点属性添加到顶点,您需要将顶点拆分为2并为克隆指定不同的切线.
获得每个顶点的唯一切线(和其他属性)的最佳方法是尽可能早地在导出器中执行此操作.在按属性排序纯顶点的阶段,您只需将切线向量添加到排序键即可.
作为问题的根本解决方案,请考虑使用四元数.单个四元数(vec4)可以成功地表示预定义的手感的切向空间.保持正交(包括传递给片段着色器)很容易,如果需要,存储和提取正常.有关KRI维基的更多信息.
根据kvark的回答,我想补充更多的想法。
\n\n如果您需要正交归一化的切线空间矩阵,则必须以任何方式做一些工作。\n即使您添加切线和副法线属性,它们也会在着色器阶段进行插值\n并且最后它们既不会标准化,也不会被标准化。彼此都很正常。
\n\n假设我们有一个归一化的法线向量 n,并且我们有正切t和副法线b,或者我们可以根据推导来计算它们,如下所示:
// derivations of the fragment position\nvec3 pos_dx = dFdx( fragPos );\nvec3 pos_dy = dFdy( fragPos );\n// derivations of the texture coordinate\nvec2 texC_dx = dFdx( texCoord );\nvec2 texC_dy = dFdy( texCoord );\n// tangent vector and binormal vector\nvec3 t = texC_dy.y * pos_dx - texC_dx.y * pos_dy;\nvec3 b = texC_dx.x * pos_dy - texC_dy.x * pos_dx;\nRun Code Online (Sandbox Code Playgroud)\n\n当然,可以使用叉积来计算正交化切空间矩阵,但这仅适用于右手系统。如果矩阵被镜像(左手系统),它将转向右手系统:
\n\nt = cross( cross( n, t ), t ); // orthonormalization of the tangent vector\nb = cross( n, t ); // orthonormalization of the binormal vector \n // may invert the binormal vector\nmat3 tbn = mat3( normalize(t), normalize(b), n );\nRun Code Online (Sandbox Code Playgroud)\n\n在上面的代码片段中,如果切线空间是左手系,则副法线向量会反转。\n为了避免这种情况,必须克服困难:
\n\nt = cross( cross( n, t ), t ); // orthonormalization of the tangent vector\nb = cross( b, cross( b, n ) ); // orthonormalization of the binormal vectors to the normal vector \nb = cross( cross( t, b ), t ); // orthonormalization of the binormal vectors to the tangent vector\nmat3 tbn = mat3( normalize(t), normalize(b), n );\nRun Code Online (Sandbox Code Playgroud)\n\n正交化任何矩阵的常用方法是Gram\xe2\x80\x93Schmidt 过程:
\n\nt = t - n * dot( t, n ); // orthonormalization ot the tangent vectors\nb = b - n * dot( b, n ); // orthonormalization of the binormal vectors to the normal vector \nb = b - t * dot( b, t ); // orthonormalization of the binormal vectors to the tangent vector\nmat3 tbn = mat3( normalize(t), normalize(b), n );\nRun Code Online (Sandbox Code Playgroud)\n\n另一种可能性是使用 2*2 矩阵的行列式,该行列式由纹理坐标 , 的推导得出texC_dx,texC_dy以考虑副法向量的方向。这个想法是正交矩阵的行列式是1,正交镜像矩阵的确定行列式是-1。
行列式既可以通过 GLSL 函数计算determinant( mat2( texC_dx, texC_dy ),也可以通过其公式计算texC_dx.x * texC_dy.y - texC_dy.x * texC_dx.y。
对于正交归一化切空间矩阵的计算,不再需要副法向量,并且normalize可以回避副法向量的单位向量\n(·)的计算。
float texDet = texC_dx.x * texC_dy.y - texC_dy.x * texC_dx.y;\nvec3 t = texC_dy.y * pos_dx - texC_dx.y * pos_dy;\nt = normalize( t - n * dot( t, n ) );\nvec3 b = cross( n, t ); // b is normlized because n and t are orthonormalized unit vectors\nmat3 tbn = mat3( t, sign( texDet ) * b, n ); // take in account the direction of the binormal vector\nRun Code Online (Sandbox Code Playgroud)\n