您所描述的透视变换称为foreshortening,我建议不仅查看我的答案,还要查找更多相关信息,因为它是一个具有各种应用的概念,并且可以使用不同的方法解决。
请注意,Javascript不是我的强项,如果我犯了任何语法错误,请谅解并纠正我。
针对您特定的形状,简单使用相似三角形应该可以给出顶点坐标,然后您可以使用这些坐标来构造您的形状。您可以将顶点坐标定义为空间中的向量。
我将从在2D空间中定义您原始形状的4个顶点开始(左下角我将定义为原点)。在Javascript中,您应该定义一个由4个3值数组组成的2D数组。2D数组中的每个数组都将是形如[x,y,z]的顶点坐标。x值将是顶点的水平值,y值将是高度值,而z值将是其在3D空间中的距离。可以通过将坡道的斜边长(在本例中为2000)乘以坡道角度的余弦值来获得y值。为了进行余弦运算,我们必须将角度从度数转换为弧度。转换始终为pi = 180,因此30度的弧度为pi / 6。x值可以通过将斜边长乘以相同角度的正弦值来获得。
var hypotenuse = 2000;
var theta = Math.PI/6;
var x = Math.sin(theta)*hypotenuse;
var y = Math.cos(theta)*hypotenuse;
var z = 1200;
var vertices = [[0,y,0], [0,y,z], [x,0,0], [x,0,z]];
![在xyz平面上的顶点坐标。](https://istack.dev59.com/BGDd0.webp)
透视变换是由于观察者与物体之间的视角关系引起的。因此,为了得到实际的透视变换,我们需要定义观察者的位置。我将任意地将其定义为点(x/2,y+y/2,z/2)。
var viewer = [x/2,y+y/2,z/2];
![观察者位置](https://istack.dev59.com/8kLIY.webp)
然后我们选择一个焦距来定义图像的位置。焦距只是从观察者到图像的距离,因此它只被定义为单个点。我会任意地将焦距放在 (viewer_x,(y+y/2)/2,viewer_z) 这里,因为它是观察者下方 (y+y/2)/2 的距离。但是,您必须确保焦点与观察者具有相同的 x 和 z 坐标,并且只更改了 y 轴。 更多关于焦距和透视变换的信息可以在http://en.wikipedia.org/wiki/3D_projection找到。
var focal_length = [viewer[0],(y+y/2)/2,viewer[2]];
![焦距坐标](https://istack.dev59.com/6k1nR.webp)
1.我们先沿着y轴(应用透视变换的轴)获取矩形每个顶点到观察者的距离。
2.然后获取焦距的y坐标到观察者的距离。
3.接着获取焦距和顶点距离的比率。
4.最后将每个顶点的x和z坐标乘以该比率,得到它们的新的缩短坐标。
距离是通过减去y坐标并取结果的绝对值来获得的。为了获得更有效的代码,我会在一个for循环中完成这一切。
var focal_distance = Math.abs(viewer[1] - focal_length[1]);
//gets distance between focal point y and viewer y
var vert_distance;
for(var i = 0; i< vertices.length-1; i++){
vert_distance = Math.abs(viewer[1]-vertices[i][1]);
//access each individual vertex and and get the distance between vertex y and viewer y
for (var j = 0; j<vertices[i].length-1; j++){
vertices[i][j] = vertices[i][j]*(focal_distance/vert_distance);
//gets the ratio between focal distance and vertex distance and multiplies each vertex by it
}
}
最后,由于我们已经沿着y轴缩短了距离,所以每个顶点的y坐标不再必要,因此我们现在将每个顶点保存到一个新的2d顶点数组中,而不是3d顶点。
var vertices2d = [[0,0],[0,0],[0,0],[0,0]]
//creates a new 2d vertex array of 4 empty 2d vertex coordinates
for(var i = 0
vertices2d[i][0] = vertices[i][0]
//sets the x values of the 2d vertices = to the x values of 3d vertices
vertices2d[i][1] = vertices[i][2]
//sets the y values of the 2d vertices = to the z values of the 3d vertices
}
现在只需将这些2D顶点坐标放入任何绘图程序中,即可得到所需的结果。
无论如何,我会让程序打印出顶点坐标:
for (var i = 0; i<vertices2d.length-1; i++){
document.write("(" + vertices2d[i][0] + "," + vertices2d[i][1] + ")");
}
随意调整查看器坐标、焦距y值、平面的长度和宽度以及斜坡角度,以获得不同的结果。祝福并欢迎提出任何问题,如果您不理解。
完整的代码如下:
var hypotenuse = 2000;
var theta = Math.PI/6;
var x = Math.Sin(theta)*hypotenuse;
var y = Math.Cos(theta)*hypotenuse;
var z = 1200;
var vertices = [[0,y,0], [0,y,z], [x,0,0], [x,0,z]];
var viewer = [x/2,y+y/2,z/2];
var focal_length = [viewer[0],(y+y/2)/2,viewer[2]];
var focal_distance = Math.abs(viewer[1] - focal_length[1]);
var vert_distance;
for(var i = 0; i< vertices.length-1; i++){
vert_distance = Math.abs(viewer[1]-vertices[i][1]);
for (var j = 0; j<vertices[i].length-1; j++){
vertices[i][j] = vertices[i][j]*(focal_distance/vert_distance);
}
}
var vertices2d = [[0,0],[0,0],[0,0],[0,0]];
for(var i = 0; i<vertices.length-1; i++){
vertices2d[i][0] = vertices[i][0];
vertices2d[i][1] = vertices[i][2];
}
for (var i = 0; i<vertices2d.length-1; i++){
document.write("(" + vertices2d[i][0] + "," + vertices2d[i][1] + ")");
}