X Motion · 轨道
Orbit 扩展了 x-motion;它不会取代它。先运行核心方法(简报、剧情梗概、故事板、批评),再使用本文件作为艺术指导和配方。本技能专为 8-12 秒、1920×1080、30 fps 的影片编写,这些影片通过 renderFrame(t) 在无头 Chromium 浏览器中结合软件 WebGL 进行确定性渲染。
视觉理念
太空是黑色的,光则是事件。只有一个光源(太阳)以及它所触及的材料:大气层、海洋、云层、金属。每一部影片都围绕着光到达、越过边缘或挣脱束缚的单一瞬间构建。
静默的自信胜过宏大的场面。画面大部分是纯正的黑色;主体是一个巨大的、缓慢移动的物体(行星、卫星、保险库门)和一道薄而明亮的边缘。如果一帧画面看起来像壁纸或游戏菜单,那就移除元素,直到它看起来像由一台耐心的相机拍摄的照片。
调色板
在线性 HDR 空间中工作,最后再进行色调映射;以下是眼睛应该落脚的、以显示为参考的目标色值。
| 角色 | 十六进制色值 | 用途 |
|---|---|---|
| 纯黑色 | #000000 | 占据大多数帧的 60-80%。切勿用雾霾效果将其提亮。 |
| 虚空海军蓝 | #050b18 | 远离任何光源的深空渐变。 |
| 深海军蓝 | #0b1a33 | 夜间海洋、阴影中的金属、提亮后的黑暗天空。 |
| 瑞利蓝 | #2f6fd8 | 大气层在白天的本体颜色。 |
| 大气青色 | #8fd3ff | 菲涅尔边缘光,薄薄的边缘线。 |
| 日出金色 | #ffc46b | 太阳核心的光晕衰减、晨昏线、云层顶部的第一缕光。 |
| 余烬琥珀色 | #ffa448 | 夜半球的城市灯光(钠灯)。 |
| 珍珠母贝色 | #ece8de | 日冕、星星、铃铛般的高光。 |
| 钛金属色 | #8a8f96 | 拉丝金属的本体颜色。 |
| 镍色 | #c3c6c8 | 金属高光、倒角平面。 |
| 香槟色 | #e9d4a6 | 金属上的暖色镜面反射边缘;最多 2 个像素宽。 |
| 极光绿 | #4ff0a0 | 氧气极光;保持在其满值亮度的 20% 以下。 |
| H-α 玫瑰色 | #ff4a6e | 仅用于太阳耀斑和色球层。 |
光照与材质
- 单一光源。 太阳是唯一的光源。辉光、边缘光和闪光必须由其位置来解释。城市和极光是唯一的自发光体,且它们位于夜半球。
- 任何恒星盘面的临边昏暗:
I(mu) = 1 - 0.6*(1 - mu),其中mu = sqrt(1 - r*r);从金色过渡到白色的核心看起来炽热,而扁平的圆盘看起来像贴纸。 - 大气层是散射效果,不是描边。 照亮边缘呈现蓝色(瑞利散射),光线掠过晨昏线处呈现金色,当太阳位于行星后方时有前向散射的亮度。边缘很薄:约为行星半径的 1.5%。
- 夜半球 并非全黑:冷却的月光填充色使海洋保持深蓝(绝不为橄榄色),陆地呈较暗的中性色调,云层为银白色,圆盘边缘有青色菲涅尔边缘光,沿海岸线分布着琥珀色的城市集群,以及边缘处淡淡的绿色气辉线。暖金色仅出现在太阳实际照射到的地方。
- 海洋反光:太阳反射处出现柔和的镜面光斑,向边缘方向拉长;仅限海洋。
- 金属:拉丝钛合金是各向异性的。高光沿着拉丝方向拉伸,倒角捕捉到一条薄薄的香槟色边缘,接缝处有环境光遮蔽。没有铬镜面效果,没有凹凸贴图噪声。
- 镜头:一个变形宽银幕眩光(水平方向,细长,青色色调),沿着穿过画面中心的线有两到四个微弱的鬼影。鬼影的不透明度低于 4%。使用 ACES 风格的曲线进行色调映射;切勿将高光裁剪为平坦的白色超过几个像素。
构图
- 地平线位于三分之一线上,绝不穿过中心。行星边缘是大弧线(半径 1.5-4 倍画面高度);整个小星球看起来像个玩具。
- 光线事件(太阳升起、钻石环、第一缕光)落于三分之一线上,而不是正中心,除非镜头是关于对称性的(如保险库门、日食)。
- 保持星星稀疏(可见数百颗),大小为 1-2 像素,带有柔和的衰减,根据温度略微偏暖或偏冷。当画面变暗时它们显现,变亮时则消失,如同曝光效果。
- 没有文字、标志、HUD、星云壁纸或镜头污垢。一次只聚焦一个主体。
运动词汇与节奏
动词:封闭、解锁、分开、坠落、升起、达到顶点、滑翔、吞没、展开、挣脱、提升。相机从不摇晃,很少有切换;主体移动缓慢,光线移动迅速。
| 节拍 | 典型时长 | 曲线 |
|---|---|---|
| 停留在封闭或黑暗的画面 | 1.0-1.5 秒 | 静态,仅有光效扫过 |
| 机械解锁 | 0.10-0.20 秒 | 快速缓出,轻微稳定 |
| 门开启 / 坠落穿过 | 0.8-1.4 秒 | 慢速开始,指数加速 |
| 曲速(条纹) | 1.2-2.0 秒 | 早期达到峰值,平稳衰减至静止;稀疏,画面大部分保持黑色 |
| 行星升起 / 轨道滑行 | 3-6 秒 | 长缓入缓出,绝不线性 |
| 太阳升起或钻石环 | 0.3-0.6 秒至全耀斑 | 尖锐的开始,缓慢的绽放 |
| 解决并保持 | ≥ 1.5 秒 | 几乎静止;让和弦余音缭绕 |
运动通过 t 以闭合形式计算:位置是对设定的速度曲线的积分,永远不会逐帧累积。曲速旅行是 s(t) = integral of v,因此条纹长度是 s(t) - s(t - shutter)。保持条纹数量少(每层数十条,而非数百条),长而柔和,远处的层比近处的层更暗更细;密集且均匀的爆发看起来像现成的超空间效果。
声音调色板与混音
- 次声(30-60 Hz 正弦波,带缓慢的起伏)承载了质量感。将主输出进行大约 25 Hz 的高通滤波(在渲染后在 JS 中操作;请参阅音频主控配方)。
- 金属:模态合成。一个 5-10 毫秒的带通噪声喀哒声加上 4-6 个非谐波正弦泛音(例如基频的 1.00、2.32、3.86、5.40 倍),衰减时间为 0.1-0.8 秒。
- 空气与旅行:通过移动带通滤波的种子噪声,根据画面进行声像定位。没有现成的呼啸声。
- 和弦:悬置和弦(sus2、sus4)营造紧张感;在光线事件时解决为大调加九和弦。铺底音色每个音符由 3 个失谐锯齿波组成,通过 600-1800 Hz 的低通滤波器。
- 铃声:加性非谐波泛音(1、2.0、2.76、4.07、5.4),起音时间为 3-8 毫秒,衰减时间随泛音指数增加而缩短。
- 混响:一个
ConvolverNode,带有种子的立体声脉冲响应(2.5-4.5 秒指数衰减,20 毫秒预延迟,更暗的尾音)。 - 静默本身也是一种提示。 当画面变暗时弱化声音;让光线事件占据第一个响亮时刻。
- 目标:综合响度 -18 LUFS,真峰值 ≤ -1.5 dBFS,尾音在结束前 0.3 秒完全衰减。在代码中测量、归一化,然后限制。
技术配方
管线。 软件 WebGL 速度较慢,因此需分工协作:半分辨率通道将平滑的 HDR 场景(行星、大气、日冕)渲染到 RGBA16F 纹理中;全分辨率通道对其进行升采样并添加锐利细节(星星、城市灯光、耀斑、金属边缘),然后进行色调映射和抖动。测量每帧的秒数;在 1080p 下保持低于 1.5 秒。
射线-球体相交。 解析计算,无需表面步进:
vec2 sphere(vec3 ro, vec3 rd, float R) { // t near, t far; t < 0 表示未相交
float b = dot(ro, rd), c = dot(ro, ro) - R*R, h = b*b - c;
if (h < 0.0) return vec2(-1.0);
h = sqrt(h); return vec2(-b - h, -b + h);
}
大气。 在球壳内沿着视线方向进行单次散射(8-12 步),光路的近似光学深度在 3-4 步内完成。瑞利系数 vec3(5.8, 13.5, 33.1)(根据您的行星半径缩放),米氏散射采用 Henyey-Greenstein 相位函数 g = 0.76。跳过视线从未进入球壳的像素。
城市灯光。 集群哈希噪声,通过陆地和夜晚进行掩码:
float night = smoothstep(0.05, -0.15, dot(n, sunDir));
float c = pow(max(fbm3(p*40.0) - 0.45, 0.0)*3.0, 2.0) * land * night;
col += c * vec3(1.0, 0.62, 0.28) * 6.0; // 琥珀色,HDR
星星,带限的。 每个网格单元一颗星星,经过抖动;使用至少 0.8 像素 sigma 的高斯函数绘制,以便运动时永不出现频闪:
vec2 cell = floor(px / 24.0), f = px - (cell + hash22(cell)) * 24.0;
float star = exp(-dot(f, f) / 1.6) * pow(hash12(cell + 7.1), 18.0) * 3.0;
太阳升起耀斑。 所有内容都围绕太阳在屏幕上的位置 s 及其可见部分 v(在 JS 中计算)进行解析计算:
vec2 d = uv - s;
float core = exp(-dot(d, d) * 9000.0), halo = 0.04 / (1.0 + dot(d, d) * 400.0);
float streak = exp(-abs(d.y) * 900.0) * exp(-abs(d.x) * 2.2);
vec3 flare = v * (core * vec3(1.0, .9, .75) * 40.0 + halo * gold + streak * cyan * 2.0);
for (int i = 0; i < 3; i++) { vec2 g = -s * kGhost[i]; /* 在 g 处软圆盘,2–4 % */ }
拉丝钛合金。 各向异性高光,切线沿着拉丝方向,加上微弱的带限条纹(波长 ≥ 3 像素):
float TH = dot(T, H);
float aniso = pow(sqrt(max(1.0 - TH*TH, 0.0)), 60.0);
float grain = 0.92 + 0.08 * noise(vec2(r * 300.0, angle * 3.0));
vec3 metal = base * (0.25 + 0.75 * diffuse) * grain + champagne * aniso;
日食。 月球略大于太阳(1.04-1.08)。给月球边缘添加一些噪声,以便在接触点附近自然产生贝利珠和钻石环效应。日冕:亮度 pow(r, -3) 乘以来自 (角度, log r) 噪声的流线,加上来自高频角度、低频径向噪声的精细丝状结构。
将边缘轮廓编写为整数频率正弦波和包裹高斯槽口的和,然后将其作为 JS 和 GLSL 字符串发出。JS 随后可以逐帧积分可见光球层(每个以太阳为中心的射线进行一次二分法,约 700 条射线),从而得到可见部分、用于眩光的珠粒质心以及用于乐谱的精确接触时间。珠粒眩光需要对可见部分进行对数映射,例如 log(1 + f*1e6) / log(1 + 1e6)。从粗糙 mip 级别绘制的宽眩光会在 HDR 源上显示阶梯效应,因此使用四个双线性采样(在半个纹素偏移处)对每个级别进行模糊处理。
音频主控(确定性)。 在 startRendering() 之后,在 JS 中测量综合响度(BS.1770 K-weighting 和 gating),缩放至 -18,然后在 4 倍过采样的峰值上运行一个前视峰值限制器,阈值为 -1.5 dBFS。使用 mulberry32 为每个噪声缓冲区和脉冲响应设定种子。
在无头 Chromium 中,以下情况会破坏逐比特重复测试:通过主 BiquadFilterNode 或 DynamicsCompressorNode 的立体声信号,以及任何具有三个或更多输入的节点,因为求和顺序会变化 1 个 ULP。保持主总线无节点(增益直接到目标)。在渲染后于 JS 中运行高通滤波和总线压缩。将混响的高通滤波烘焙到其脉冲响应中。将所有扇入节点通过 2 输入增益节点链接,因为 a + b 是精确且可交换的。
function mulberry32(a){return()=>{a|=0;a=a+0x6D2B79F5|0;let t=Math.imul(a^a>>>15,1|a);
t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};}
确定性检查清单
renderFrame(t)仅根据t设置每个 uniform;渲染模式下不使用Date、performance.now、Math.random或逐帧累积。- 仅使用带种子的哈希和伪随机数生成器;当画面和声音需要保持一致时,使用相同的种子。
- 向后查找会产生相同的像素;在渲染较晚的帧后测试 t = 0。
preserveDrawingBuffer: true,antialias: false;在renderFrame结束时调用gl.finish()。- 预览模式使用
requestAnimationFrame循环;?render=1本身不绘制任何内容。 renderAudio每次调用返回相同的缓冲区,在两次渲染之间逐样本比较;响度和真峰值是测量的,而非猜测的。- 检查光线事件、它前一帧以及最后一帧的静止图像;逐帧检查时间。
避免
- 卡通轮廓、行星周围发光的描边、饱和的紫色星云、带有六边形环的镜头光晕预设。
- 旋转的球体、快速的相机移动、摇晃,或没有物理原因的速度线。
- 将高光裁剪为平坦的白色、提升黑色、条带色阶(在输出前添加低于 LSB 的抖动)。
- 逐帧的全屏颗粒或 1 像素闪光;它们会产生频闪并增加 H.264 码率。
- 每个动作都有音效、俗气的呼啸声、没有进展的上升音、缓冲区边缘的喀哒声、在影片结束时被切断的混响尾音。
- 文字、标志或界面装饰,除非简报要求。