VYBE Cinematic 3D
在 HyperFrames 影片中,3D 是一個 <canvas>,其每一幀都是時間的純函數。
HyperFrames 的 three 適配器發布時間;你負責渲染它。
權威來源: /hyperframes-animation → adapters/three.md(安裝後)。
合約(不可協商)
- 導入鎖定:
import * as THREE from "https://cdn.jsdelivr.net/npm/[email protected]/+esm";(或使用 importmap 鎖定相同版本以支援附加功能)。 - 場景、攝影機、渲染器、材質需同步在載入時構建;在渲染關鍵區域前載入任何模型/紋理(Three 的
DefaultLoadingManager會自動等待)。對於耗費 CPU 的設定,請註冊window.__hf.buildReady["<名稱>"] = promise。 - 僅從時間渲染:
window.addEventListener("hf-seek", e => renderAt(e.detail.time)); renderAt(window.__hfThreeTime || 0); - 不使用
requestAnimationFrame迴圈、時鐘、delta 值或未種子化的Math.random()。 renderer.setPixelRatio(1); renderer.setSize(W, H, false),其中 W/H 來自根元素的data-width/height。根元素必須有data-duration。- 將全出血 canvas 標記為
data-layout-allow-overflow。
骨架
<canvas id="gl" width="1920" height="1080" data-layout-allow-overflow></canvas>
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/[email protected]/+esm";
const root = document.getElementById("vybe-root");
const W = +root.dataset.width, H = +root.dataset.height, D = +root.dataset.duration;
const css = getComputedStyle(document.documentElement);
const tok = (n, f) => css.getPropertyValue(n).trim() || f; // 讀取 VYBE 代碼
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById("gl"), antialias: true });
renderer.setPixelRatio(1); renderer.setSize(W, H, false);
renderer.setClearColor(new THREE.Color(tok("--vy-bg", "#000")), 1);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(W > H ? 40 : 60, W / H, 0.1, 200); // 直向時視角較寬
// … 構建 …
function renderAt(t) { const p = t / D; /* 從 t 設定攝影機 + 物體 */ renderer.render(scene, camera); }
window.addEventListener("hf-seek", (e) => renderAt(e.detail.time));
renderAt(window.__hfThreeTime || 0);
</script>
當 3D 世界跨越場景時(例如在整部影片中攝影機弧線移動),將 canvas 放在主機 (index.html) 中;僅當 3D 世界存在於單一場景時,再將其放在場景檔案中。在上方疊加 HTML 類型;當 DOM 文字可以達成時,切勿在 WebGL 中渲染文字。
配方
- 粒子 / 灰塵 / 星星:
THREE.Points,搭配已種子化的 PRNG 用於位置,從調色盤取得頂點顏色,一個圓形 sprite(CanvasTexture徑向漸層作為map)— 預設點為方形 —AdditiveBlending,depthWrite: false。讓每個粒子保持在攝影機最近的 z 外,否則當攝影機經過它時會變成一個巨大的 sprite(這是真實的 VYBE 缺陷)。 - 英雄球體 / 行星:
SphereGeometry(r, 96, 64)+MeshStandardMaterial(金屬度/粗糙度) + 自發光光暈TorusGeometry環;隨著時間點亮它。 - 黑洞: 暗色球體 + 吸積盤 =
RingGeometry,帶有 canvas 漸層紋理,傾斜 70–80°,旋轉;微引力透鏡環 = 第二個面向攝影機的薄環;星星彎曲 = 根據半徑縮放背景點圖層。 - 分子: 在原子位置放置球體(使用
InstancedMesh處理大量),在它們之間放置圓柱體(使用quaternion.setFromUnitVectors定向);讓攝影機繞行。 - 產品旋轉台: 透過
GLTFLoader載入 GLTF(使用 importmap),使用AnimationMixer.setTime(t)處理已製作的剪輯;下方放置工作室燈光;在鏡頭中緩慢旋轉 20–35°。 - 數據雕塑: 使用
InstancedMesh方塊,其高度來自數據,並隨著時間使用smoothstep增長。 - 程式化地形/波浪: 在 JS 中從
t置換PlaneGeometry(每幀重新計算頂點位置,適用於 ≤40k 頂點),或在ShaderMaterial中使用renderAt中設定的uTime統一變數。
燈光
載入 references/lighting.md。最低配置: 主光(點光源/定向光)、補光/背光(對比色調)、以及一個 HemisphereLight 補光 — 僅有一個光源的網格物體在暗面會渲染為黑色剪影。以固定比例從相同的時間函數驅動光源強度。
攝影機
根據標準化進度 p = t / D 推導位置,並依幕次使用 smoothstep 分段(參見電影模板的 renderAt)。對於切換,請宣告 const CUT_B = 23 一次,並同時用於攝影機鏡頭選擇器和物體可見性;檢查切換前後的幀(snapshot --at 22.9667,23)。
效能(無頭 Chrome 可能使用軟體 GL)
- ≤ 100k 三角形,≤ 20k 粒子,無需幀歷史的後處理傳遞。光暈: 使用疊加的精靈/光暈來偽造。
- 避免在低記憶體機器上使用
antialias搭配 4K 巨型畫布;請測試草稿渲染。 - 如果 WebGL 在渲染環境中失敗,canvas 會變黑:品管會將黑幀視為問題;請回退到 2D canvas 或場景的 CSS 3D 版本。