X Motion: Prism
Prism 是核心 x-motion 方法的一个主题。请遵循该方法(简介、主题、故事板、动态分镜、制作、有限批评)。该文件增加了使其主题可识别并保持诚实的艺术指导、物理和声音内容。
视觉主题
让一种光学现象显得精准且安静地奇妙,将其像静物般在苍白的工作室中拍摄,让观众注意到光线的实际行为。
- 每部电影一个主角现象。 色散、焦散、透镜成像、全内反射、菲涅尔边缘。其他一切都是它落脚的平静表面。
- 物理决定颜色的去向。 世界是无彩色的:纸、石、石墨。光谱色仅出现在波长分离的地方(棱镜形成的扇形、透镜的边缘、焦散的边缘),而不在其他任何地方。
- 亮度即是情绪。 这与暗色空间主题相反。阴影是柔和的石墨色,永不黑色;高光是一种光感,永不霓虹。
- 相机是三脚架。 锁定或几乎不可察觉的推进。现象移动;观察者不动。
调色板
| 角色 | 十六进制 | 用途 |
|---|---|---|
| 纸 | #f2efe9 | 主表面,环境日光 |
| 阴影中的纸 | #d9d4cb | 主光区域之外的区域 |
| 苍白石头 | #e6e1d8 | 地板、水池、基座(使用低频噪声在 ±3% 范围内变化) |
| 石墨 | #5d5b57 | 印刷线条、接触阴影(透明度 20–60%) |
| 深石墨 | #3a3936 | 允许的最深值,仅在接触折痕中使用 |
| 水色 | #d8e6e4 | 在石头上方以“正片叠底”模式叠加用于表示浅层清水 |
| 光谱 | 计算得出 | 仅通过波长到 RGB 函数生成,绝不用手动选取的彩虹 |
在线性光中工作,叠加光能量,然后进行一次色调映射并转换为 sRGB。在明亮背景上的叠加光谱色会发白,因此需要局部降低背景亮度(在主角周围创建柔和的阴影区域,约 45–60% 线性亮度),以使颜色看起来饱和,并保持画面的其余部分通透。
灯光和材质
- 主光: 一个来自左上方的柔光日光源,与画面轴成约 30–45° 角。阴影长、柔和且略带暖灰色。
- 玻璃: 透明、无色、无瑕。其存在通过四件事来体现:后面物体的折射、一条细菲涅尔边缘、一个柔和的窗户反射以及它投射的焦散或光谱光。不要用倒角发光、有色玻璃或光晕。
- 折射率: Cauchy 色散公式
n(λ) = A + B/λ²,λ 以 µm 为单位。普通玻璃:A = 1.5046, B = 0.0042。当色散需要在屏幕上可读时,将B放大至多约 3 倍,并在你的备注中说明。 - 纸和石头: 哑光,对比度极低的纹理(亮度变化 2–4%),静态。精细的印刷线条是具有柔和衰减的石墨色,线宽 1.2–2 px。
- 接触: 每个接触表面的物体都有一个紧密、更深的接触阴影加一个宽而软的阴影。这是让玻璃看起来“搁置”而非“漂浮”的关键。
构图
- 充足的留白:主角约占画面的三分之一;其余部分为带有柔和渐变光照的纸或石头。
- 将事件放在三分之一线或黄金分割点上,除非影片是关于对称性,否则绝不放在正中心。
- 用光引导视线:光束、滚动方向或涟漪应朝向将要发生高潮的地方。
- 保持画面边缘安静且稍暗(来自照明而非黑圈的 6–10% 晕影)。
- 无文字、UI、标志或装饰性粒子。如果现象需要比例参考,请使用印刷网格、石头接缝或阴影。
运动词汇与节奏
动词:进入,弯曲,展开,扫描,聚焦,涟漪,稳定。 移动的名词:光、水、单个物体。相机和主体绝不能同时移动。
| 节拍 | 在 9–10 秒影片中的典型占比 | 行为 |
|---|---|---|
| 静止 | 10–15% | 空旷且灯光就绪的场景。让观众阅读表面。 |
| 到达 | 15–20% | 光或物体沿着清晰路径以减速缓和曲线进入。 |
| 现象 | 35–45% | 主角效果缓慢发展和变化(旋转几度、滚动、涟漪)。 |
| 稳定 | 20–25% | 运动被物理减振(摩擦力、阻尼、波扩散)并停止。 |
| 释放 | 5–8% | 光线变暗或图像静止,以便循环和音频尾部能闭合。 |
- 使用物理闭合运动:滚动用恒定减速,稳定用阻尼余弦,展开的环用
1/sqrt(r)幅度,旋转用缓和角度。 - 当动画化光束头部时,玻璃内的光线明显比空气中慢 (
v_玻璃 = v_空气 / n)。这是一个微小但观众能感受到的真实细节。 - 优先使用一个长姿势而不是几个短姿势。完整旋转太多;4–10° 足以扫出光谱。
声音调色板与混音
声音是玻璃、空气和水,经过调谐。它应该让人感觉像一个有坚硬、干净表面的房间。
- 玻璃泛音: 模态合成,带有几个非谐泛音(对于敲击的玻璃,接近
1, 2.32, 4.25, 6.63的比例),每个都有自己的指数衰减;高频泛音衰减更快。 - 手指在玻璃上: 一个或两个正弦波,具有缓慢的 0.3–0.6 秒膨胀、4–5 Hz 颤音(±0.15%)和一丝同音高的带通噪声。
- 空气: 粉红偏置种子噪声通过带通 (300–2500 Hz),非常低,缓慢呼吸。它是房间本身,而非风声。
- 水: 气泡“噗通”声是频率在 30–80 毫秒内指数上升约 30–60% 的正弦波;柔和的冲刷声是低通噪声。
- 和声: 选择一个调性并坚持使用(开放五度、加九和弦、利底亚色彩)。将现象映射到音高:光谱位置对应音级,速度对应滤波器截止,暂停对应解和弦的钟声。
- 空间: 一个种子合成卷积混响 (2.5–4.5 秒,尾部变暗),所有声音共享,干/湿约 60/40。
- 目标: 集成响度约 -18 LUFS,真峰值低于或等于 -1.5 dBFS,0.3 秒淡入,每个尾部在结束前衰减 0.4 秒。
技术方案
纯粹的关于时间的函数。 每一帧都从 t 重新计算整个场景:物体姿态、光束头部、波相位。仅使用种子随机性。
const mulberry32 = s => () => { s |= 0; s = s + 0x6D2B79F5 | 0;
let t = Math.imul(s ^ s >>> 15, 1 | s); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
return ((t ^ t >>> 14) >>> 0) / 4294967296; };
分析场景,少量渲染通道。 使用少量分析形状(平面、球体、作为一组平面的凸棱镜)进行求交,而不是光线行进。将在 JavaScript 中计算的随时间变化的状态(角度、位置、光线路径)作为 uniforms 传递。将帧拆分为小的单用途通道(地面、半分辨率下的光场到 RGBA16F 目标、顶部的玻璃),而不是一个包含分支的着色器。
从波长生成光谱色。 使用平滑的分析拟合,使扇形看起来像光,而不是渐变色条。对多个波长样本求和并进行归一化,使总亮度为中性白色。
float gauss(float x, float m, float s) { float u = (x - m) / s; return exp(-u * u); }
vec3 spectral(float nm) { // 平滑拟合,线性 RGB,可见窗口 372-712 nm
float w = smoothstep(372., 392., nm) * (1. - smoothstep(690., 712., nm));
return w * vec3(gauss(nm, 606., 46.) + 0.14 * gauss(nm, 420., 22.), // 红色在紫色部分仍有贡献
gauss(nm, 543., 44.),
gauss(nm, 452., 34.));
}
在明亮背景上,紫色端看起来是淡薰衣草色;这是诚实的,所以给它一个更暗的局部区域来取代强行提亮它。
每波长折射。 使用不同折射率(红、绿、蓝或更多)对同一条光线追踪两到三次。结果一致的地方,获得清晰的中性细节;结果偏离的地方,恰好获得物理规律所放置的色边。
vec3 col;
for (int k = 0; k < 3; k++) {
float n = 1.5046 + B * 3.0 / pow(LAMBDA[k], 2.0);
col[k] = traceThroughGlass(ro, rd, n)[k]; // 折射进入,折射离开,采样地面
}
无需模拟的焦散。 对于一个由几个解析波构建的水面 h(x, t),到达其下方地板上一点的光线强度与 1 / |det(I + d · Hess h)| 成比例,其中 d 是深度除以折射率。以解析形式计算 Hessian 矩阵(正弦波和径向环束均有闭合形式),用 1 / sqrt(det² + ε²) 软化奇点,并用三个略有不同的 d 值计算,以获得细薄的光谱边缘。
平面上的光线。 掠过纸张的光束是其轴线上的 1D 高斯函数 exp(-(dist/w)²),外加一个更宽、更淡的光晕。光束头部沿路径以缓和距离前进;用短的 smoothstep 截断,而非硬边。
带限印刷。 从点到最近线条的距离(使用碰撞点的 fwidth)绘制网格线,使得线条在任何放大倍数下保持 1.5–2 px 柔和,包括在透镜内部。当单元格缩小到几个像素以下时,淡出到印刷的平均色调,而非空白纸张。对放大的墨水给予最小柔和度(约其宽度的一半),否则球透镜下的线条会变成剃刀般锋利的楔形。
float lines(vec2 p, float s, float w, vec2 fw) {
vec2 d = abs(fract(p / s + .5) - .5) * s, a = max(fw * .8, vec2(w * .55)), wd = max(vec2(w), a);
vec2 l = (1. - smoothstep(wd - a, wd + a, d)) * clamp(vec2(w) / wd, 0., 1.);
return mix(max(l.x, l.y), 4. * w / s, smoothstep(.18, .42, max(fw.x, fw.y) / s));
}
玻璃需要有足够暗的东西来折射。 在白色工作室中,完美的球体看起来会呈现乳白色。在环境地平线附近设置灰色墙壁、一个紧凑的柔光箱和一个黑色挡板;球内部的倒置地平线和窗口将描绘出其形状。计算所有路径时不使用分支,以便 fwidth 保持有效,并输出解析覆盖率(光线到中心距离与半径之比,以像素为单位)作为 alpha。
色调与收尾。 指数型肩部 1 - exp(-1.85 x) 使纸张保持在 #f2efe9 附近,而焦散峰值柔和地滚降;然后转换到 sRGB。每个通道的肩部会将明亮的光谱带漂白成柔和色,因此对于彩色光,大约一半向保色调曲线混合(对最大通道进行色调映射并缩放),由色度门控,以便纸张不受影响。完全保色调或锐化带间颜色会将橙、黄、青压平成扁平的红色、绿色和蓝色条纹。以数值方式检查扇形:在其上采样一列,确认色相仅上升一次,从红色 (约 10°) 到紫色 (约 270°),无环绕。仅添加一个静态的亚单位抖色(±0.6/255)以打破浅色渐变中的条纹;永不逐帧动画化。
速度。 SwiftShader 使用软件渲染,并且对着色器中的每条指令(包括任何像素都不执行的分支)都进行计费。保持每个通道单一用途,以半分辨率渲染柔和光场,并将昂贵的通道(玻璃、棱镜内部)剪刀到对象的投影包围盒,使用 SRC_ALPHA, ONE_MINUS_SRC_ALPHA 与地面进行混合。在调整精修前,测量 1920×1080 下的每帧秒数;fbm 最多 5 个八度。
确定性音频图。 Chromium 以未指定的顺序将三个或更多连接合并到一个 AudioNode 中,因此两次渲染会相差约 1e-6 并失败字节检查。在 JavaScript 中将每个声音合成为一个立体声 Float32 混音(相位累加的正弦波、基于种子噪声的 RBJ 双二阶滤波器、采样精确的包络),然后仅将图形用于一个源到干增益和卷积器的路由,两者进入一个高通(两个输入可交换),然后是一个温和的压缩器(约 2.5:1,-20 dB 阈值)和一个线性淡出。
const M = A.mix(sr, D); // { L, R } Float32Arrays
A.glass(M, 6.6, hz(81), 0.15, { decay: 2.8, pan: 0.2 }); // 模态泛音,在 JS 中求和
const buf = await A.render(M, { seed: 31, room: 3.4, wet: 0.45, fade: [8.25, 8.8] });
return A.master(buf, { lufs: -18, ceiling: -1.7 });
在页面中完成后期母带处理。 在 startRendering() 之后,测量缓冲区(K 加权,门控集成响度和 4 倍过采样峰值),应用一个增益以达到目标,如果峰值天花板要求,则应用较低增益。确定且简单。
确定性渲染检查表
window.motion声明宽度、高度、时长、帧率以及一个关于最美丽帧的海报时间。renderFrame(t)仅从t设置每个 uniform;在渲染模式下不允许使用Date、performance.now、Math.random、计时器或累计状态。- 先寻址到较晚时间再回到较早时间能产生相同的像素;第一个和最后一个捕获的帧是有效的。
- 预览模式(无
?render=1)使用requestAnimationFrame循环;点击播放同一个渲染的音频缓冲区并同步播放。 renderAudio在 JavaScript 中混合声音,然后在OfflineAudioContext中通过一个种子卷积器渲染一个源;两次调用返回字节相同的样本(没有节点具有三个或更多输入)。- 提示映射表是一个在画面和声音之间共享的时间代码表格(撞击、停止、释放),并且在最后 0.3 秒内,混响尾部低于 -45 dB。
- 无网络、字体、图片或样本;每个纹理和声音都是程序化的。
避免
- 穿过画面的彩虹渐变、彩虹文字、彩虹色“全息”表面。
- 霓虹灯、紫蓝色“AI”渐变、黑色背景、镜头光晕和变形拉丝。
- 光泽 3D 渲染玻璃:厚而亮的倒角、有色玻璃、HDRI 镀铬反射、一切物体上的泛光。
- 无接触阴影的漂浮物体;颜色非来自物理原因。
- 相机轨道、快速剪辑、粒子、闪光、嗖嗖声和上升音调。
- 逐帧随机颗粒和单像素线条(在 H.264 压缩后闪烁)。
- 削波的声音、与调性冲突的和弦堆栈、过早结束的混响尾部。