我们要做的不是「能跑的 Demo」,而是一条能稳定产出短视频素材的内容产线。
这一节我们不急着打开代码。先做一件更重要的事:拿诗贯山河的一帧画面,把它拆成工程结构。很多视觉项目之所以后期失控,是因为开发者一开始按「画面元素」拆:山是一块,水是一块,诗词是一块,按钮是一块。这样拆很直观,但不适合维护。
真正适合工程的拆法,是按「生产关系」拆:哪些东西是离线生产出来的,哪些东西是运行时加载的,哪些东西每帧都在更新,哪些东西只是最终交付时才需要。这个视角一换,你会发现诗贯山河不是一个页面,而是一条从地图资产到短视频画面的流水线。
先看用户看到的一帧
用户看到的画面大概是这样的:一个固定 4:5 竖屏画幅,中央是一张带起伏的中国古风地形,河流有轻微水色,某些点位附近会出现雨雪烟气,点击诗词点位后镜头飞过去,诗文卡片淡入,背景可能配合鸟鸣、风声或水声。
如果你是第一次做,可能会直接问:这个山怎么画?这个粒子怎么做?这个卡片怎么写 CSS?这些问题都对,但顺序不对。我们应该先问:这些画面元素分别由什么数据驱动?它们的生命周期是什么?它们要不要跨区域复用?
第一条线:地图不是背景图
最容易低估的是地图。很多人会把地图当成一张大背景图,觉得只要找一张好看的水墨中国地图铺上去就行。但诗贯山河里的地图至少包含四类信息:视觉底图、高程数据、水体遮罩、坐标 bounds。它们看起来像一张图,工程上却是四种不同职责。
- 视觉底图负责第一眼的风格,比如纸感、墨色、地貌明暗。
- 高程数据负责山体起伏,后面会进入 shader 影响顶点或法线。
- 水体遮罩负责告诉材质哪里是河流、湖泊、海岸线。
- bounds 负责把真实坐标映射到 Three.js 平面,不记录它就无法稳定放点。
所以地图不是一张图片,而是一个资产包。你后面要新增四川、陕西、洛阳,真正复用的不是图片,而是这套资产包规范。
第二条线:诗词不是文案
诗词也不能只当展示文案。它在系统里至少承担四个角色:卡片内容、地图点位、镜头目标、氛围触发器。比如同一首诗可以决定卡片里显示什么,也可以决定相机飞向哪里,还可以决定这一段镜头要不要叠加细雨或风声。
这就是为什么诗词数据必须结构化。title、author、dynasty、body 只是最表层;location、region、tags、effectCue、audioCue 才决定它能不能进入产线。如果只把诗写成一段字符串,后面所有交互都会变成硬编码。
第三条线:特效不是越多越好
雨、雪、风、沙、烟、瀑布听起来是六种效果,但底层都要解决同一批问题:在哪里出现、出现多大范围、强度多少、持续多久、是否跟随镜头、是否参与性能预算。你不能为每种效果单独发明一套参数,否则调试会非常痛苦。
更好的抽象是局部 Volume。Volume 是一个影响区域,它不关心自己最后表现成雨还是雪,只描述位置、半径、高度、强度、时间和随机种子。具体效果再消费这套配置。这样做的好处是:数据层可以统一管理特效点位,渲染层可以统一调度生命周期。
第四条线:镜头不是动画装饰
点击点位后镜头飞过去,看起来只是一个动画,但它其实是叙事结构。镜头从全局地图出发,飞向某个地点,最后停在一个适合阅读诗词卡片的构图上。这个过程里,相机位置、观察点、FOV、速度曲线、卡片入场、音频 cue 都要配合。
如果你只是 tween camera.position,会得到一个机械移动的镜头;如果你把相机看成一个状态机,区分 idle、flying、focused、returning,就能在不同阶段安排不同的 UI、特效和声音。这就是从动画思维到导演思维的差别。
第五条线:交付约束要提前进入设计
诗贯山河不是只在浏览器里给人随便点点,它还要服务短视频素材生产。所以 4:5 画幅不是最后导出时再裁一下,而是一开始就影响构图:地形主体放哪里,诗词卡片放哪里,点位密度多少,镜头结束时哪里留白,按钮和调试面板发布时如何隐藏。
很多项目做完才发现录屏不好看,就是因为交付约束来得太晚。工程上应该反过来:先确定最终画幅、目标平台、性能底线,再倒推场景、卡片、镜头和特效。
把这五条线串成产线
- 离线阶段:用 QGIS / GDAL 生成地图资产包,整理诗词与古地名数据。
- 加载阶段:前端按区域加载纹理、遮罩、高程、音频和结构化 JSON。
- 渲染阶段:Three.js 根据资产和数据创建地形、水体、点位、局部特效。
- 交互阶段:用户点击点位,镜头状态机、诗词卡片、音频和特效联动。
- 交付阶段:按 4:5 画幅录屏或上线访问,统计数据用于判断后续迭代。
为什么这套拆法很重要
因为它直接决定了后续扩展成本。如果地图资产包规范是稳定的,新增区域就是重复生产资产;如果诗词数据结构是稳定的,新增内容就是填表;如果 Volume 抽象是稳定的,新增天气只是多一种 renderer;如果镜头状态机是稳定的,新增交互就不会破坏已有节奏。
反过来,如果这些边界没立住,项目会变成一堆互相知道对方细节的代码:诗词数据里写材质参数,镜头动画里写 DOM 选择器,shader 里假设某张贴图尺寸,部署脚本里手动列文件名。它也许能上线,但每次迭代都像拆炸弹。
工程四象限
- 场景层:负责 Three.js scene、camera、renderer、后期处理和主循环。
- 资产层:负责地图贴图、GLB、音频、遮罩、高程等文件的命名、加载和缓存。
- 数据层:负责诗词、古地名、区域、特效点位、镜头目标等结构化内容。
- 运营层:负责竖屏画幅、统计埋点、发布脚本、OSS 上传和线上稳定性。
这门课的完整知识地图
下面这张图是课程结构。你可以把它当成一张工程地图:第一章建立全景,第二章解决地图资产,第三章解决内容数据,第四章解决局部特效,后面再进入镜头、音频、发布和运营。
学完能:先搞清楚这门课值不值得学,以及我是谁
- 我为什么要做这门课,以及我是谁
学完能:看懂整套架构,5 分钟把诗贯山河跑起来
- 诗贯山河在做什么:从一帧画面拆到一整条产线
- 本地跑起来:5 分钟拉起诗贯山河开发环境
- 工程分层:场景 / 资产 / 数据 / 后期的四象限
学完能:从 QGIS 出图到 GPU 地形渲染,独立换成任意省份
- QGIS 与 EPSG:3857 投影:底图哪里来
- Three.js 地形渲染:RG 高程编码到 GPU 法线光照
- 河流海岸线遮罩:让水面真的看起来是水
- 扩展新区域 SOP:四川 / 陕西 / 洛阳怎么加
学完能:把诗词与古地名做成可维护数据,竖屏卡片排到好看
- 56 首诗词数据:结构、采集与去重
- 45 个古地名标注:从历史地图到屏幕坐标
- 诗文卡片:标题 / 作者 / 朝代 / 全文的版式打磨
学完能:一套体系搓出雨 / 雪 / 风 / 沙 / 烟 / 瀑 6 种天气特效
- 6 种特效系统总览:细雨 / 飘雪 / 风 / 沙 / 烟 / 瀑布
- 细雨与飘雪:粒子系统的最朴素也最难调的两个
- 风与沙暴:方向场 + 噪声纹理的组合拳
- 烟气与瀑布:体积烟与曲线 UV 流动
学完能:影视级镜头飞行 + 8 段音频氛围,氛围感拉满
- 镜头飞行聚焦:点击点位后平滑飞向目标
- 8 个音频 cue:鸟鸣、风声、水声的时机控制
学完能:竖屏直出、流量埋点、一行命令部署上线
- 4:5 竖屏画幅:录屏直出的画框设计
- 前端埋点:百度统计 + 51.LA + DevTools 轻量防护
- OSS 增量上传 + 一键部署:发版只剩一行命令
学完能:把这套引擎延伸成你自己的诗境作品
- 走向自己的诗境工程:下一步去哪
读完这一节你应该能判断什么
- 一个视觉项目什么时候只是 Demo,什么时候已经需要资产管线。
- 哪些信息应该离线生产,哪些信息应该运行时计算。
- 为什么点位、镜头、特效都应该由数据驱动。
- 为什么画幅、录屏、部署这些交付问题要提前进入架构。