为假三维动画添加透视

Adding perspective to fake 3D animation

本文关键字:透视 添加 三维动画      更新时间:2023-09-26

我正在制作一个基于画布的动画,并试图在2D画布中获得3D效果。

到目前为止,一切进展顺利!我的"三角形轨道线"运行得很好:

var c = document.createElement('canvas');
c.width = c.height = 100;
document.body.appendChild(c);
var ctx = c.getContext("2d");
function Triangles() {
  this.rotation = {
    x: Math.random()*Math.PI*2,
    y: Math.random()*Math.PI*2,
    z: Math.random()*Math.PI*2
  };
  /* Uncomment this for testing perspective...
  this.rotation = {
    x: Math.PI/2,
    y: 0,
    z: 0
  };
  */
}
Triangles.prototype.draw = function(t) {
  this.rotation.z += t/1000;
  var i, points;
  for( i=0; i<15; i++) {
    points = [
      this.computeRotation(Math.cos(0.25*i),-Math.sin(0.25*i),0),
      this.computeRotation(Math.cos(0.25*(i+1)),-Math.sin(0.25*(i+1)),-0.1),
      this.computeRotation(Math.cos(0.25*(i+1)),-Math.sin(0.25*(i+1)),0.1)
    ];
    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.moveTo(50+40*points[0][0],50+40*points[0][1]);
    ctx.lineTo(50+40*points[1][0],50+40*points[1][1]);
    ctx.lineTo(50+40*points[2][0],50+40*points[2][1]);
    ctx.closePath();
    ctx.fill();
  }
};
Triangles.prototype.computeRotation = function(x,y,z) {
  var rz, ry, rx;
  rz = [
    Math.cos(this.rotation.z) * x - Math.sin(this.rotation.z) * y,
    Math.sin(this.rotation.z) * x + Math.cos(this.rotation.z) * y,
    z
  ];
  ry = [
    Math.cos(this.rotation.y) * rz[0] + Math.sin(this.rotation.y) * rz[2],
    rz[1],
    -Math.sin(this.rotation.y) * rz[0] + Math.cos(this.rotation.y) * rz[2]
  ];
  rx = [
    ry[0],
    Math.cos(this.rotation.x) * ry[1] - Math.sin(this.rotation.x) * ry[2],
    Math.sin(this.rotation.x) * ry[1] + Math.cos(this.rotation.x) * ry[2]
  ];
  return rx;
};
var tri = new Triangles();
requestAnimationFrame(function(start) {
  function step(t) {
    var delta = t-start;
    ctx.clearRect(0,0,100,100)
    tri.draw(delta);
    start = t;
    requestAnimationFrame(step);
  }
  step(start);
});

正如你所看到的,它使用旋转矩阵来计算点旋转后的位置,我使用它来使用输出的x和y坐标绘制三角形。

我想通过使用z坐标并在动画中添加透视图来进一步提高这一点,这将使三角形在前景中稍微大一点,在背景中稍微小一点。然而,我不知道该怎么做。

我想这更像是一道数学题,而不是一道编程题,对不起!

定义焦距以控制透视量。值越大,透视的数量就越少。然后

var fl = 200; // focal length;
var px = 100; // point in 3D space
var py = 200;
var pz = 500;

然后获取屏幕X,Y

var sx = (px * fl) / pz;
var sy = (py * fl) / pz;

生成的点相对于面纱的中心,因此需要将其居中到画布。

sx += canvas.width/2;
sy += canvas.height/2;

这是一点。

它假设被观察的点在视图的前面,并且距离焦点的焦距更远。

我已经设法找到了一个基本的解决方案,但我相信还有更好的解决方案。因此,如果您有更完整的答案,请随时添加!但现在。。。

由于坐标系已经基于原点,视点直接在Z轴上观察(x,y)平面,因此实际上只需将(x,y)坐标乘以与z成比例的值就足够了。例如,x * (z+2)/2在这种情况下做得很好

不过,肯定会有一个更合适、更通用的解决方案!