减小字体
增大字体
Nemesis2k per-pixel lighting 纹理空间坐标基的计算方法
我知道的几种方法:
1. 对于参数化的表面,设其方程为 P = P (u, v),其中 P 为向量, 三个分量分别为 x, y z。也可以表示为: Px = Px (u ,v) Py = Py (u ,v) Pz = Pz (u ,v) 那在任意一个顶点 T = {dPx/du, dPy/du, dPz/du} B = {dPx/dv, dPy/dv, dPz/dv} N = T X B 然后把 T, B, N 归一化就行了。 这里的偏导数可以用差分计算。 这样计算出来的切空间是在每一个顶点的切空间。
2。对于由三角形面片组成的网格,在 MSDN 上的 Per-pixel lighting 文章里介绍了一种方法。 设三角形的三个顶点是 P0, P1, P2,其中每个顶点都有位置,法向量 和 2-D 纹理坐标。 Pi : {x, y, z}, {nx, ny, nz}, {s, t} 现在我们要计算在 P0 点的切空间。 这里要分辨两个切空间: 1)顶点上的切空间 2)三角形面片上的切空间 两个切空间是相同的吗?我觉得是不同的。方法 2 和方法 3 计算出来的 实际上都是三角形面片的切空间,顶点的切空间还要通过平均顶点所在 各个三角形面片的切空间基向量来计算。(是这样的吗?高手指教一下!)
设三角形面片所在的切空间的基向量为 T, B, N,坐标原点在 P0。 那么三角形面片中的任意向量应该可以表示为: Vec = x*T + y*B 因此,如果我们找到了两个向量 Vec1, Vec2 以及它们在 T, B 上的 分量,那么自然就可以解出 T, B 了。 令: Vec1 = P1 - P0 Vec2 = P2 - P0 dS1 = P1.s - P0.s dS2 = P2.s - P0.s dT1 = P1.t - P0.t dT2 = P2.t - p0.t 那么我们有 Vec1 = dS1*T + dT1*B (1) Vec2 = dS2*T + dT2*B (2) 联立 (1), (2) 就可以解出 B*(dS2*dT1 - dS1*dT2) = (dS2*Vec1 - dS1*Vec2) 所以: (dS2*dT1 - dS1*dT2) 是一个常数,反正我们之后要对 B 归一化, 可以不用管它。于是: B = normalize(dS2*Vec1 - dS1*Vec2) 这就是 MSDN 里那篇文章里的方法。 B 可以通过解方程获得,但是 T 就不行了,因为这样解出来的 T 和 B 不一定垂直。怎么处理呢? MSDN 中的方法是,利用顶点的 N 来求 T: T = B X N 然后再求 N N = T X B 但是这样可以吗?这里的 N 是顶点 P0 的 N,而不是三角形面片的 N。 是不是这样求出来的 T, N, B 恰好是顶点 P0 的切空间的坐标基,不需要 再平均了?(高手指教!) 我想的处理方法是这样的: 同样解出 T 来: T = normalize(dT2*Vec1 - dT1*Vec2) 然后 N = T X B。这个 N 是三角形面片的 N。 然后 T = B X N。这样 T, N, B 构成正交基,而且是三角形面片的。 要计算 P0 顶点的切空间基,还需要平均多个面片。 这种方法到是比较复杂。
一个问题是,为什么有 Vec1 = dS1*T + dT1*B (1) Vec2 = dS2*T + dT2*B (2) 这两个公式! 我想是因为在计算顶点的纹理坐标时,因为是从平面映射到平面,所以我们使用了 仿射变换: s = as*x + bs*y + cs t = as*x + bs*y + cs 反过来我们有 x = ax*s + bx*t + cx (3) y = ay*s + by*t + cy (4) z = az*s + bz*t + cz (5) 于是 Vec1.x = P1.x - P0.x = ax*(P1.s - P0.s) + bx*(P1.t - P0.t) Vec1.y = P1.y - P0.y = ay*(P1.s - P0.s) + by*(P1.t - P0.t) Vec1.z = P1.z - P0.z = az*(P1.s - P0.s) + bz*(P1.t - P0.t) 于是 Vec1 = {ax, ay, az}*dS1 + {bx, by, bz}*dT1 这和 (1) 已经很象了,那么 {ax, ay, az} 就是 T 吗? 答案是是的!事实上 (3), (4), (5) 就是三角形面片的参数表示,那么 T = {dx/ds, dy/ds, dz/ds} = {ax, ay, az} B = {dx/dt, dy/dt, dz/dt} = {bx, by, bz} 也就是说,如果我们能直接把 ax, ay, az, bx, by, bz 求出来,T 和 B 就求出来了
(当然要把他们正交归一化)
3 nVidia 网站上的方法。 我们可以假设 x = ax*s + bx*t + cx y = ay*s + by*t + cy z = az*s + bz*t + cz 如何求解 (3) ?这里有 3 个未知数,那我们需要 3 个方程。 将 3 个顶点的属性 {x, y ,z}, {s, t} 带入,刚好有三个方程: P0.x = ax*P0.s + bx*P0.t + cx (1) P1.x = ax*P1.s + bx*P1.t + cx (2) P2.x = ax*P2.s + bx*P2.t + cx (3) 解出来就得到 ax, bx, cx 了。 同理可得: ay, by, cy, az, bz, cz。 T = {ax, ay, az} B = {bx, by, bz} N = T X B T = B X N 然后都归一化即可。
nVidia 网站上的方法呢,是建立三个平面方程 Ax*x + Bx*s + Cx*t + Dx = 0 (4) Ay*y + By*s + Cy*t + Dy = 0 (5) Az*z + Bz*s + Cz*t + Dz = 0 (6)
并且指出,三角形面片上的所有点的 (x, s, t) 都在 方程 (4) 定义的平面中。那么 dx/ds = -Bx/Ax dx/dt = -Cx/Ax
同理 dy/ds = -By/Ay dy/dt = -Cy/Ay
dz/ds = -Bz/Az dz/dt = -Cz/Az
那么这些 Ax, Ay, Az, Bx, By, Bz, Cx, Cy, Cz 怎么求呢? 容易知道,{Ax, Bx, Cx} 其实是平面的法向量,那么可以 选平面中的三个点,计算出两个向量,然后叉乘。
{Ax, Bx, Cx} = {P1.x - P0.x, P1.s - P0.s, P1.t - P0.t} X {P2.x - P0.x, P2.s - P0.s, P2.t - P0.t}
这两种方法是等价的。
|