ASCII 地球
打开地球实验。拖动可以换个角度观察;拖动时自转暂停,松开后继续。不支持缩放和平移。首页预览和 Three.js 小实验 也使用同一个组件。
图像怎样变成文字
底层仍然是一个 Three.js 球体,而不是平面地图。着色器读取两张纹理:海洋遮罩区分水域和陆地,地形图则给陆地增加亮度变化。海洋画成黑色,陆地亮度随地形变化。这是一种视觉处理,没有模拟太阳照射或昼夜变化。
随后,Three.js 的 AsciiEffect 渲染球体,把图像缩小采样,再将亮度值换成字符。最终显示的是 HTML 文字网格。着色器负责提供待采样的图像,并不直接绘制字符。
const effect = new AsciiEffect(renderer, ' .:-+*=%~@#01o', {
invert: true,
resolution: 0.16,
});
字符集开头的空格让暗色海洋保持留白,陆地就不需要靠外围的实心圆盘衬托。因此,从某些角度看,画面可能只剩一条狭窄的大陆轮廓;不能仅凭一个空旷的半球就判断自转停了。
这些字符的形状和视觉疏密不同,让亮度变化在文字中也能显现。其中 -、=、~ 在亮度相近的地形上连续出现时,可能拼成横向条纹。字体和字距也会影响成像:这里的 HTML 表格承载的是字符图像,普通表格的边框和单元格底纹会干扰地球轮廓。
旋转与资源释放
地球以每秒 0.28 弧度自转,绘制频率限制在每秒约 30 帧。拖动时,OrbitControls 改变相机视角。地球采用与吸引子相同的暂停/播放状态和帧计时方式。两个实验打开后都默认运动,不跟随浏览器的“减少动态效果”偏好。画面上的暂停/播放按钮可以直接控制运动,暂停时仍然可以手动拖动。
组件离屏或页面隐藏时跳过绘制。卸载时取消动画帧,断开观察者,移除事件监听,并释放控制器、纹理、几何体、材质和 WebGL 渲染器。
容器非零,采样画布仍可能为空
AsciiEffect 的采样尺寸是 floor(width × resolution) 和 floor(height × resolution)。只把外层尺寸限制为至少一个像素还不够:分辨率取 0.16 时,采样宽度仍可能变成零,随后调用 getImageData 就会报错。
组件先将显示尺寸取整,再检查宽、高是否都足以产生至少一个采样像素;不满足时跳过尺寸调整和绘制。如果在取整前检查,就会漏掉 6.3 像素这样的值:它会被取整成 6,采样尺寸仍然是零。隐藏预览、切换标签或浏览器重新排版时,都可能遇到这种短暂的小尺寸。后续恢复有效尺寸后,渲染也应继续。
修改这个实验时,除了截图,还要看不同经度下的连续画面,试验拖动与松开、启用减少动态效果后确认仍会自动播放、操作暂停/播放、隐藏后恢复,以及切到别的实验再回来。首页从默认设置开始展示,仍然可以直接拖动地球;只隐藏占空间的参数面板和播放按钮。点击实验名称,可进入提供完整控件的独立页。实现位于 src/components/AsciiEarth/index.js;布局样式不应给字符网格额外加上表格边框或底纹。