t、b、l、r 分别代表camera投影面的上下左右,camera指向-z方向(指向屏幕内),n为近景距离,f为远景距离。
主要步骤:
let w = 1024; let h = 576; // 1/2宽作为近景在 X-O-Z 平面上,可以刚好使图像左右两边到camera的夹角为直角, // 且3个点组成的三角形为等腰直角三角形,便于后面计算。 let n = -w / 2; let f = -w * 1000; let r = 1; let l = -1; let t = 1; let b = -1; let m00 = 2 * n / (r - l); let m02 = (r + l) / (r - l); //0 let m11 = 2 * n / (t - b); let m12 = (t + b) / (t - b); //0 let m22 = (n + f) / (n - f); let m23 = 2 * f * n / (n - f); let m32 = -1; const aperspect = math.matrix([ [m00, 0, m02, 0], [0, m11, m12, 0], [0, 0, m22, m23], [0, 0, m32, 0] ]) const aperspectT = math.transpose(aperspect); var change3d = (rfactor, dfactor, mSlide) => { //thet θ,图像距离近景越近,图像完全移出画布的角度越逼近于90度,越远越接近45度。 let thet = 1 / 2 * rfactor * Math.PI; const rotationYT = math.matrix([ [Math.cos(thet), 0, -Math.sin(thet), 0], [0, 1, 0, 0], [Math.sin(thet), 0, Math.cos(thet), 0], [0, 0, 0, 1] ]) let result = math.identity(4); //把图像移动至原点(0, 0),方便计算绕Y轴旋转 //若拉远至近景距离的2倍,视觉上为图像缩小1倍 const transOT = math.matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [-w / 2, -h / 2, -w / 2 * dfactor, 1] ]); result = transOT; //y旋转 result = math.multiply(result, rotationYT); //透视 result = math.multiply(result, aperspectT); //还原至画布中央 const transCenterT = math.matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [w / 2, h / 2, 0, 1] ]); result = math.multiply(result, transCenterT); let mStr = math.flatten(result)._data.join(','); mSlide.style.transform = `matrix3d(${mStr})` }另外 CSS transform matrix3d 的值为列向量计算矩阵的转置,上面全部使用转置矩阵来计算。
matrix3d( a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4)还需要引入math.js文件。
DEMO 地址
参考资料:
3D游戏与计算机图形学中的数学方法-视截体透视投影矩阵推导三维旋转矩阵旋转矩阵