getPerspective 实现原理

来源:6-3 透视投影

核桃丷

2023-04-15

我开发中发现,老师实现的透视投影矩阵与第三方库,在相同参数的情况下,得到的结果不一致,请问下老师这两种实现方式有什么区别。

我测试三种方案

  • 老师的
  • gl-matrixmat4perspective
  • chatgpt 生成的
import {mat4} from 'gl-matrix'
const WIDTH = 500
const HEIGHT = 500

/**
 * getPerspectiveMatrix 获取透视投影矩阵 (chatgpt 给出的)
 * @param {number} fieldOfView 视野角度,单位为弧度
 * @param {number} aspectRatio 宽高比
 * @param {number} nearPlane 近平面距离
 * @param {number} farPlane 远平面距离
 * @returns {Float32Array<number>} 透视投影矩阵,使用 Float32Array 表示
 */
const getPerspectiveMatrix = (fieldOfView, aspectRatio, nearPlane, farPlane) => {
    const f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfView);
    const rangeInv = 1.0 / (nearPlane - farPlane);

    return new Float32Array([
        f / aspectRatio, 0,                                     0,  0,
        0,               f,                                     0,  0,
        0,               0,   (nearPlane + farPlane) * rangeInv,    -1,
        0,               0,   nearPlane * farPlane * rangeInv * 2,  0
    ]);
}

// 获取透视投影矩阵 (老师的)
function getPerspective(fov, aspect, far, near) {
    fov = fov * Math.PI / 180;
    return new Float32Array([
        1/(aspect*Math.tan(fov / 2)), 0, 0, 0,
        0, 1/(Math.tan(fov/2)),0,0,
        0,0,-(far+near)/(far-near),-(2*far*near)/(far-near),
        0,0,-1,0,
    ])
}

const perspectiveMatrix1 = getPerspective(150, WIDTH / HEIGHT, 100, 1) // 老师的
const perspectiveMatrix2 = mat4.perspective(new Float32Array(16), 150, WIDTH / HEIGHT, 1, 100)  // gl-matrix
const perspectiveMatrix3 = getPerspectiveMatrix(150, WIDTH / HEIGHT, 1, 100) // chatgpt

console.log(perspectiveMatrix1) // 老师的
console.log(perspectiveMatrix2) // gl-matrix
console.log(perspectiveMatrix3) // chatgpt

返回结果:
图片描述

得出的结果: gl-matrix = chatgpt != 老师

写回答

6回答

核桃丷

提问者

2023-04-17

//img.mukewang.com/szimg/643cba930916731f08350865.jpg

如果按照着矩阵公式的形状来实现js矩阵函数,这个坐标完全就对应不上

1
0

核桃丷

提问者

2023-04-17

官方给出的实现代码是

class Mat4 {
  static perspective(fovy, aspect, near, far, out = []) {
    const f = 1 / Math.tan(fovy / 2);
    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[11] = -1;
    out[12] = 0;
    out[13] = 0;
    out[15] = 0;
    if (far != null && far !== Infinity) {
      const nf = 1 / (near - far);
      out[10] = (far + near) * nf;
      out[14] = 2 * far * near * nf;
    } else {
      out[10] = -1;
      out[14] = -2 * near;
    }
    return out;
  }
}

//img.mukewang.com/szimg/643cb75209182f6203690122.jpg

这个 -1 明明在下标 14上,但是却赋值在了 11上,这个11是竖着数的,但是js数组是横着来的,就不是很理解,老师可以帮忙解释下吗

1
3
核桃丷
回复
慕运维2471188
你可以对比下第三方库的实现,老师的是错的
2023-07-06
共3条回复

yancy

2023-04-17

可以看下lib文件里的使用,有说明要做矩阵转换

0
0

yancy

2023-04-17

可以看下相关实现,比如:z轴的方向,归一化的处理,坐标的处理。一般的不同是由于z轴方向不同导致的

0
1
核桃丷
老师,你在根据推导矩阵,实现js函数的时候没有把行矩阵转成列矩阵
2023-04-17
共1条回复

核桃丷

提问者

2023-04-17

0
1
核桃丷
老师在根据矩阵实现js函数的时候没有做行列转换
2023-04-17
共1条回复

核桃丷

提问者

2023-04-17

关于矩阵顺序:如图

//img.mukewang.com/szimg/643cbccd0946ec3007310432.jpg

0
0

WebGL+Three.js 入门与实战,系统学习 Web3D 技术

前端的技术蓝海,涨薪好选择

1081 学习 · 294 问题

查看课程