除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)
}
}());