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

相关推荐
brycegao19 小时前
Flutter Dart JSON 解析必坑!金额精度丢失为什么必须在网络层处理?附工业级解决方案
flutter·dart
tangweiguo0305198719 小时前
Flutter GetIt 完全指南:告别 BuildContext 依赖的终极方案
flutter
韩曙亮20 小时前
【Flutter】Flutter 中的 Android / iOS 特殊配置 ① ( 网络权限配置 | HTTP 明文传输配置 | 应用名称配置 )
android·网络·flutter·http·ios·网络权限
_李小白20 小时前
【android opencv学习笔记】Day 31:提取轮廓之Canny算法
android·opencv·学习
tangweiguo0305198720 小时前
Flutter中的StreamController完全指南
flutter
hashiqimiya20 小时前
每日android布局xml文件
android·xml·gitee
m0_7381207221 小时前
渗透测试基础——PHP 序列化数据结构与反序列化机制详解
android·服务器·网络·数据结构·安全·php
故渊at21 小时前
第二板块:Android 四大组件标准化学理 | 第十一篇:组件间通信(IPC)与 Binder 深度解析
android·binder·组件化·组件间通信
ZC跨境爬虫21 小时前
跟着 MDN 学JavaScript day_10:数组——数据的有序集合
android·java·开发语言·前端·javascript