Flutter 伪 3D 绘制#02 | 地平面与透视

上一篇我们实现了三维空间点到二维平面的映射,并且绘制了坐标轴。本文将进一步完善三维空间的视觉表现,如下所示,构建一个地平面网格, 并且具有透视效果:


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 站 。

相关推荐
椒盐煎蛋11 分钟前
新建的Flutter插件工程,无法索引andorid工程代码;无法索引io.flutter包下代码。
flutter
hiii13 分钟前
【ART 内存管理】堆内存分布与分配
android
张风捷特烈18 分钟前
每日一题 Flutter#3 | 说说 Widget 的派生体系
android·flutter·面试
冰糖葫芦三剑客1 小时前
Android Studio 打包时遇到了签名报错问题:Invalid keystore format
android·ide·android studio
顾林海2 小时前
Android Native 内存泄漏检测全解析:从原理到工具的深度实践
android·面试·性能优化·源码·android虚拟内存
移动开发者1号3 小时前
详解图片内存占用的计算原理与代码验证(以500×500 PNG为例)
android·kotlin
移动开发者1号3 小时前
进程优先级与组件存活关系解析
android·kotlin
移动开发者1号3 小时前
你用对了吗Requestlayout,onlayout,onDraw,DrawChild
android·kotlin
快乐觉主吖5 小时前
Unity安卓平台开发,启动app并传参
android·unity·游戏引擎
原来如此。5 小时前
Android 轻松实现 增强版灵活的 滑动式表格视图
android