3 年前 Web 3D

三维Z轴、透视图

Z轴

除X,Y轴外,三维存在另外一个维度:深度,用Z表示。
右手坐标系统:Z轴数值越大表示物体远离观察者,反之接近观察者。
左手坐标系统:Z轴数值越大表示物体接近观察者,反之远离观察者。

坐标系统

透视图

三维中,尽管两个物体有相同的xy坐标,但是由于深度(z)不一样,所以会在不同的位置,透视图可以知道应该把物体放到哪个位置。 物体的远离(Z轴增加),X和Y向消失点移动。因为缩放比例和接近消失点的比例相同,所以根据距离计算出这个比例就可以求出物体所处的位置。

透视图

透视图公式

const scale = fl / (fl + z)

fl 表示焦距,取值一般为200-300之间,计算出的 scale 正常情况下在0.0-1.0之间。当z为负数时(接近观察者,物体会越来越大),需要做特殊处理:

// zpos为0时,scale = 1,zpos为-fl时,会报错所以要做判断
if (zpos > -fl) {...} else {...}

用这个公式求出比例后再与物体的X、Y相乘就可以得到新的坐标,而物体的大小可以直接使用 scale 值。

模拟透视图效果

Demo (上下键改变物体Z值,物体会逐渐远离/接近)

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const ball = new Ball()
let xpos = 0
let ypos = 0
let zpos = 0
const fl = 250
// 消失点,会在vpx, vpy坐标消失,不定义的话物体会在坐标0,0点消失
const vpx = canvas.width / 2
const vpy = canvas.height / 2
const mouse = captureMouse(canvas)

window.addEventListener('keydown', keydown)

function keydown (e) {
  if (e.keyCode === 38) { // up zpos增加,物体远离
    zpos += 5
  } else if (e.keyCode === 40) { // down zpos减少,物体接近
    zpos -= 5
  }
}

;(function drawFrame() {
  window.requestAnimationFrame(drawFrame)
  ctx.clearRect(0, 0, canvas.width, canvas.height)

  if (zpos > -fl) {
    // 缩放比例
    let scale = fl / (fl + zpos)
    xpos = mouse.x - vpx
    ypos = mouse.y - vpy

    ball.scaleX = ball.scaleY = scale
    // 这里加上消失点的坐标
    ball.x = vpx + xpos * scale
    ball.y = vpy + ypos * scale
    ball.visible = true
  } else {
    // 当超出大小隐藏
    ball.visible = false
  }
  if (ball.visible) {
    ball.draw(ctx)
  }
}());