上一篇我们实现了三维空间点到二维平面的映射,并且绘制了坐标轴。本文将进一步完善三维空间的视觉表现,如下所示,构建一个地平面网格, 并且具有透视效果:
1. 地平面绘制
在数学的立体几何中,一些辅助线可以更好地帮我们理解三维空间。 这里准备在 X-Y 平面绘制一个网格,体现出地平线,这可以在视觉上让我们更具有归属感:
绘制网格非常简单,主要就是找到坐标,遍历收集线条绘制。如下代码中,遍历 6 次,收集横纵方向上各 6 条线:
dart
void _drawGrid(Canvas canvas) {
List<(Point3D, Point3D)> lines = [];
for (double i = 0; i <= 5; i++) {
lines.add((Point3D(i, 0, 0), Point3D(i, 5, 0)));
lines.add((Point3D(0, i, 0), Point3D(5, i, 0)));
}
Paint paint = Paint() ..color = Colors.white..strokeWidth = 1;
for (var line in lines) {
Offset p0 = project(line.$1);
Offset p1 = project(line.$2);
canvas.drawLine(p0, p1, paint);
}
}
然后可以修改一下遍历的个数,以及线的长度,就可以轻松实现一个 10*10
的网格:
dart
List<(Point3D, Point3D)> lines = [];
for (double i = -5; i <= 5; i++) {
lines.add((Point3D(i, -5, 0), Point3D(i, 5, 0)));
lines.add((Point3D(-5, i, 0), Point3D(5, i, 0)));
}
2. 透视效果
仔细观察不难看出,目前的地平面每条线都是平行的,这对于近大远小的视觉感来说比较为何,特别是旋转角度后,这种绝对的平行会产生违和感:
那么该如何在当前的效果上施加 透视
的魔法呢?如下效果的对比可以看出,施加透视之后,感官上舒适了很多:
未透视 | 透视效果 |
---|---|
透视效果本质上还是在三维点到二维点映射过程,根据视觉规律进行的转换。如下所示,两行代码即可在转换过程中施加透视效果:
dart
// 3D点投影到2D平面
Offset project(Point3D p) {
double scale = 32.0; // 缩放系数
double angle = 30 / 180 * pi; // 30度弧度值(π/6)
// 绕Z轴旋转
final rx = p.x * cos(rotationZ) - p.y * sin(rotationZ);
final ry = p.x * sin(rotationZ) + p.y * cos(rotationZ);
// 等轴测投影
final xProj = (rx - ry) * cos(angle);
final yProj = (rx + ry) * sin(angle) - p.z;
// 增加透视
double d = 18.0;
double radio = d / (d - yProj);
return Offset(xProj * scale * radio, yProj * scale * radio);
}
当动态修改 d 的值,可以调整透视的效果,如下所示。至于为什么这样就可以实现透视,以及 d 的具体作用。不用着急,后续会一点点分析揭秘:
多绘制一些直线,可以更好地表现地平面和透视的效果。如下所示,在旋转过程中,外部延伸的线条可以联想成路面,越往远处路面线的长度越短,符合透视的规律:
dart
List<(Point3D, Point3D)> lines = [];
for (double i = -32; i <= 32; i++) {
lines.add((Point3D(i, -5, 0), Point3D(i, 5, 0)));
lines.add((Point3D(-5, i, 0), Point3D(5, i, 0)));
}
尾声
本想这一篇介绍一下映射原理的,但是发现增加透视会有更好的感官体验,绘制地平面也可以更好地为之后的分析做铺垫。下一篇我将会详细分析一下投影的映射逻辑,敬请期待 ~
更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。