1

就像我是自虐狂一样,我正在尝试学习创建模型视图和透视矩阵背后的所有矩阵数学,以便我可以编写自己的函数来生成它们,而无需使用 JS 库。

我了解矩阵的概念,但不了解如何实际生成它们。我一直在密切关注 glMatrix 库,我有以下问题:

1)以下mat4.perspecive方法发生了什么?

/**
 * Generates a perspective projection matrix with the given bounds
 *
 * @param {mat4} out mat4 frustum matrix will be written into
 * @param {number} fovy Vertical field of view in radians
 * @param {number} aspect Aspect ratio. typically viewport width/height
 * @param {number} near Near bound of the frustum
 * @param {number} far Far bound of the frustum
 * @returns {mat4} out
 */
mat4.perspective = function (out, fovy, aspect, near, far) {
    var f = 1.0 / Math.tan(fovy / 2),
        nf = 1 / (near - far);
    out[0] = f / aspect;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 0;
    out[5] = f;
    out[6] = 0;
    out[7] = 0;
    out[8] = 0;
    out[9] = 0;
    out[10] = (far + near) * nf;
    out[11] = -1;
    out[12] = 0;
    out[13] = 0;
    out[14] = (2 * far * near) * nf;
    out[15] = 0;
    return out;
};

具体来说,我得到了Math.tan(fovy / 2)正在计算的内容,但为什么要取它的倒数呢?同样,为什么要取近边界和远边界之差的倒数?另外,为什么out[11]设置为-1以及存储的值是什么out[14]

2)库中的以下mat4.lookAt方法也让我感到困惑:

/**
 * Generates a look-at matrix with the given eye position, focal point, 
 * and up axis
 *
 * @param {mat4} out mat4 frustum matrix will be written into
 * @param {vec3} eye Position of the viewer
 * @param {vec3} center Point the viewer is looking at
 * @param {vec3} up vec3 pointing up
 * @returns {mat4} out
 */
mat4.lookAt = function (out, eye, center, up) {
    var x0, x1, x2, y0, y1, y2, z0, z1, z2, len,
        eyex = eye[0],
        eyey = eye[1],
        eyez = eye[2],
        upx = up[0],
        upy = up[1],
        upz = up[2],
        centerx = center[0],
        centery = center[1],
        centerz = center[2];

    if (Math.abs(eyex - centerx) < GLMAT_EPSILON &&
        Math.abs(eyey - centery) < GLMAT_EPSILON &&
        Math.abs(eyez - centerz) < GLMAT_EPSILON) {
        return mat4.identity(out);
    }

    z0 = eyex - centerx;
    z1 = eyey - centery;
    z2 = eyez - centerz;

    len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
    z0 *= len;
    z1 *= len;
    z2 *= len;

    x0 = upy * z2 - upz * z1;
    x1 = upz * z0 - upx * z2;
    x2 = upx * z1 - upy * z0;
    len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
    if (!len) {
        x0 = 0;
        x1 = 0;
        x2 = 0;
    } else {
        len = 1 / len;
        x0 *= len;
        x1 *= len;
        x2 *= len;
    }

    y0 = z1 * x2 - z2 * x1;
    y1 = z2 * x0 - z0 * x2;
    y2 = z0 * x1 - z1 * x0;

    len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
    if (!len) {
        y0 = 0;
        y1 = 0;
        y2 = 0;
    } else {
        len = 1 / len;
        y0 *= len;
        y1 *= len;
        y2 *= len;
    }

    out[0] = x0;
    out[1] = y0;
    out[2] = z0;
    out[3] = 0;
    out[4] = x1;
    out[5] = y1;
    out[6] = z1;
    out[7] = 0;
    out[8] = x2;
    out[9] = y2;
    out[10] = z2;
    out[11] = 0;
    out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
    out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
    out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
    out[15] = 1;

    return out;
};

mat4.perspecive方法类似,为什么要计算向量长度的倒数?另外,为什么该值会乘以z0,z1z2值?对x0-x2变量和y0-y2变量执行相同的操作。为什么?out[12]最后,为-设置的值是什么意思out[14]

3)最后,我对这个方法有几个问题mat4.translate。具体来说,我买了《Professional WebGL Programming: Developing 3D Graphics for the Web 》一书,它说以下 4x4 矩阵用于转换顶点:

1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1 

但是,当我查看 glMatrix 库中的以下mat4.translate方法时,我发现out[12]-out[15]是通过一些复杂的方程设置的。为什么要设置这些值?

/**
 * Translate a mat4 by the given vector
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to translate
 * @param {vec3} v vector to translate by
 * @returns {mat4} out
 */
mat4.translate = function (out, a, v) {
    var x = v[0], y = v[1], z = v[2],
        a00, a01, a02, a03,
        a10, a11, a12, a13,
        a20, a21, a22, a23;

    if (a === out) {
        out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
        out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
        out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
        out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
    } else {
        a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
        a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
        a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];

        out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
        out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
        out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;

        out[12] = a00 * x + a10 * y + a20 * z + a[12];
        out[13] = a01 * x + a11 * y + a21 * z + a[13];
        out[14] = a02 * x + a12 * y + a22 * z + a[14];
        out[15] = a03 * x + a13 * y + a23 * z + a[15];
    }

    return out;
};

谢谢大家的时间,很抱歉所有的问题。我来自 JS 背景,而不是 OpenGL/3D 编程背景,所以我很难理解所有矩阵背后的数学。

如果有任何很好的资源可以解释用于这些方程式/方法的数学,那也很好。谢谢。

4

1 回答 1

3

具体来说,我得到了 Math.tan(fovy / 2) 正在计算的内容,但为什么要取它的倒数呢?

因为焦距d来自公式

Math.tan(fovy / 2) = y / d

要获得您需要乘以的焦距

1 / Math.tan(fovy / 2)

为什么取近边界和远边界之差的倒数?另外,为什么 out[11] 设置为 -1 以及存储在 out[14] 中的值是什么?

你可以用焦距投影(x,y,z)进去。这已经足够了,但是 OpenGL 需要进行线性变换,例如投影在. 这种归一化坐标简化了裁剪并保留了用于移除隐藏表面的 z 信息。(x*d/z, y*d/z)d(x,y,z)[-1,1]

out[11] 设置为 -1,因为除非应用反射,否则没有提供归一化坐标的线性变换。这个 -1 导致系统的手性与归一化坐标切换。

out[14] 与 out[10] 一起使用,在投影后将 z 从 [-n -f] 转换为 [-1 1]。

和mat4.perspecive方法类似,为什么要计算向量长度的倒数呢?另外,为什么该值要乘以 z0、z1 和 z2 值?对 x0-x2 变量和 y0-y2 变量执行相同的操作。为什么?

标准化向量 x、y 和 z

为 out[12]-out[14] 设置的值是什么意思?

摄像机由向量基和位置组成。out[12]-out[14] 应用反向平移来设置相机位置。

但是,当我查看 glMatrix 库中的以下 mat4.translate 方法时,我发现 out[12]-out[15] 是通过一些复杂的方程设置的。为什么要设置这些值?

这些方程看起来很复杂,因为它是一个平移矩阵和一个现有矩阵的乘积a

专业的 WebGL 编程:为 Web 开发 3D 图形

我不知道这本书,它可能会解释一些数学,但如果你需要详细解释,你应该考虑 Eric Lengyel 的,它解释和推导了 3d 光栅图形中使用的重要数学。

于 2013-07-14T23:56:44.213 回答