🎬 开场白
上次的蹦迪派对是不是还没嗨够?💃🕺
立方体在旋转跳跃,机器人在展示着机械舞步……但你有没有觉得少了点什么?🤔 对!少了你这位指挥家🎩。
想象一下,如果舞台上这些模型不再是“自顾自”地表演,而是能听从你的指挥:
- 你用鼠标轻轻一推,镜头就跟着舞动🎥
- 你按下键盘,舞者就立刻走位换阵👣
- 你拉动一个滑块,舞台灯光颜色瞬间切换🔘
- 甚至音乐一起,舞台和角色立刻跟着节奏燃爆全场🔥
这一次,你不只是观众,而是 整场派对的 DJ + 灯光师 + 舞蹈总监。
欢迎来到 Three.js 互动派对,在这里,模型们会乖乖听你指挥,舞台由你掌控🎛️!
🗺️先认识几种常见“坐标语言”
在 3D 世界里,我们会遇到好几种“坐标语言”,学会它们,就像掌握了不同国家的手势翻译,鼠标、模型和相机才能愉快交流:
| 坐标类型 | 范围/形式 | 使用场景 | 通俗理解 |
|---|---|---|---|
| 世界坐标(World Coordinates) | x, y, z ∈ 无界或场景边界 | 放置模型、场景布局 | 就像你在自家客厅里摆家具:沙发在左、茶几在中间、电视靠右,不管你站哪儿,家具位置不变 |
| 相机/视图坐标(Camera/View Coordinates) | 相机为原点,z向前 | 摄像机观察物体、裁剪视锥 | 就像戴上相机眼镜看世界,所有东西都相对于你眼睛位置,前面的东西 z>0,左边右边都有 x,y |
| 裁剪坐标(Clip Coordinates) | -1 ≤ x,y,z ≤ 1(透视除法前) | GPU 裁剪、投影计算 | 就像把世界压进一个立方体盒子里,超出盒子的都被裁掉,不管原来多大 |
| 归一化设备坐标(NDC) | -1 ≤ x,y,z ≤ 1(透视除法后) | 方便 GPU 映射到屏幕 | 世界被拉成 [-1,1] 的标准尺寸,像把不同大小的水果都切成同一大小的方块,方便统一处理 |
| 屏幕/像素坐标(Screen Coordinates) | x ∈ [0, 屏幕宽], y ∈ [0, 屏幕高] | UI、鼠标点击、2D 渲染 | 最直观的:你的鼠标点了屏幕左上角(0,0),右下角(屏宽,屏高),完全跟显示器坐标挂钩 |
| DOM 坐标 / 客户端坐标(clientX, clientY) | x ∈ [0, 可视窗口宽], y ∈ [0, 可视窗口高] | Web 鼠标事件、拖拽 | 就像在浏览器窗口里玩“找茬游戏”,坐标相对于浏览器窗口,不管滚动条怎么动 |
透视除法(Perspective Divide)
基本概念
透视除法是将 裁剪坐标(Clip Coordinates) 转换为 归一化设备坐标(NDC) 的关键步骤。
公式
NDCx = clipX / clipW
NDCy = clipY / clipW
NDCz = clipZ / clipW
clipX, clipY, clipZ, clipW:裁剪坐标(4D 齐次坐标)
透视除法就是把裁剪坐标除以 W,得到最终的屏幕坐标(NDC),范围在 [-1, 1]。
为什么要除以 W?W可以理解成“离相机的距离”。
- 离相机越远,
W 越大→ 除以 W 后,坐标会变小→ 在屏幕上看起来更远、更小。 - 离相机越近,
W 越小→ 除以 W 后,坐标会变大→ 在屏幕上看起来更近、更大。
举例说明
假设有两个物体在相机前:
- 物体 A 离相机近,裁剪坐标:
clipX = 2, clipY = 1, clipZ = 5, clipW = 5 - 物体 B 离相机远,裁剪坐标:
clipX = 4, clipY = 2, clipZ = 20, clipW = 20
// A 的 NDC 坐标:
ndcX = clipX / clipW = 2 / 5 = 0.4
ndcY = clipY / clipW = 1 / 5 = 0.2
// B 的 NDC 坐标:
ndcX = clipX / clipW = 4 / 20 = 0.2
ndcY = clipY / clipW = 2 / 20 = 0.1
注意:clipZ 也会除以 clipW 得到 ndcZ,但是 ndcZ 不用来画在屏幕上,而是用作深度比较(Z-buffer)来判断遮挡关系。
通俗理解:
X、Y 控制“你看到的画面位置”,所以要受透视影响
Z 控制“谁在前谁在后”,屏幕上你看不到 Z 的变化,只用来判断深度
归一化设备坐标(NDC)的作用总结
| 作用 | 通俗解释 | 计算方法 |
|---|---|---|
| 统一坐标系 | 不管屏幕大小、分辨率多少,所有坐标都缩放到 [-1,1],方便计算 📏 | NDCx = (2*screenX / screenWidth) - 1NDCy = 1 - (2*screenY / screenHeight) |
| 可见性判断 | 在 [-1,1] 里的会显示,超出就裁掉,就像舞台聚光灯圈内才是主角 💡 | NDC 坐标超出 [-1,1] → 被裁剪掉 |
| 交互桥梁 | 鼠标/触摸屏坐标必须转成 NDC 才能和 3D 世界对话,否则两边鸡同鸭讲 🐔🦆 | NDCx = (2*screenX / screenWidth) - 1NDCy = 1 - (2*screenY / screenHeight) |
| 设备无关 | 不管是 4K 屏、1080P 还是 VR 眼镜,NDC 都一样,超强通用性 🌍 | NDC 范围固定 [-1,1],和设备分辨率无关 |
举例说明
- 屏幕尺寸:
screenWidth = 1920px,screenHeight = 1080px - 鼠标位置:
screenX = 960px,screenY = 540px(屏幕中心)
公式
NDCx = (2 * screenX / screenWidth) - 1
NDCy = 1 - (2 * screenY / screenHeight)
计算过程
| 屏幕坐标 (screenX, screenY) | 计算公式 | NDC 结果 (NDCx, NDCy) |
|---|---|---|
| (960, 540) 中心点 | NDCx = (2x960/1920)-1 = 0 NDCy = 1-(2*540/1080)=0 | (0, 0) |
| (0, 0) 左上角 | NDCx = (2x0/1920)-1 = -1 NDCy = 1-(2x0/1080)=1 | (-1, 1) |
| (1920, 1080) 右下角 | NDCx = (2x1920/1920)-1 = 1 NDCy = 1-(2x1080/1080)=-1 | (1, -1) |
小幽默记忆
屏幕左上角是舞台左边 -1,上面 +1;右下角是舞台右边 +1,下面 -1。中心点就是舞台正中央 0,0 🎭
🎥 鼠标控制:让镜头跟随你的手势
| 分类 | 重点 / 难点 | 文案解读 |
|---|---|---|
| 基础概念 | 鼠标坐标转换 | 鼠标在屏幕上动,模型得听懂你说的“向左走”“向右飘”,坐标转换就是翻译官 🕵️♂️ |
| 相机旋转与位置控制 | 镜头不是定格摄影,要跟着你甩鼠标转圈圈,别让小球跑丢了!🎡 | |
| 事件监听 | mousemove / mousedown / mouseup | 给鼠标装上耳朵,点下、拖动、松开都能被捕捉 👂 |
阻止默认事件 (preventDefault) | 不让浏览器耍小聪明,滚轮滚屏幕也能被你掌控 💪 | |
| 平滑动画 | 使用 requestAnimationFrame 做插值 | 镜头不能一顿抖,像跳舞一样顺滑,别像踩到香蕉皮滑倒 🍌 |
| 阻尼(damping) | 给镜头加点惯性,甩一下鼠标,镜头慢慢跟上,好像滑雪 🏂 | |
| 工具库 | OrbitControls / PointerLockControls | 有现成神器可以用,别非得自己搬砖,少点心累,多点快乐 🛠️ |
| 常见难点 | 坐标系和角度容易搞混 | 三维世界和屏幕世界差别大,小心别让镜头绕到背后 😵 |
| 鼠标速率太快或太慢 | 太快像坐过山车,太慢像乌龟漫步,要找个黄金平衡 ⚖️ |
👣 键盘操作:舞者走位换阵
键盘就像舞台上的脚步控制器:
- W/A/S/D → 前后左右移动,让你的角色在场景中穿梭
- Q/E 或 Shift/Ctrl → 快速旋转、蹲下或加速,让动作更灵活
- 空格键 → 跳跃或腾空,就像舞者腾起换位置
🔘 滑块交互:实时调灯光
想象一下舞台表演 🎤:
有灯光师在控制台前,手里推来推去的推子(滑块)。
他一推,舞台上的光就变亮;一拉,光又变暗。
换个颜色,舞台氛围瞬间就从“浪漫小夜曲”变成了“摇滚现场”。⚡
在 Three.js 里,我们就能通过 滑块 (Slider) 来实现类似的效果:
- 🔆 强度滑块 → 灯光亮度,拉满就是“聚光灯打爆全场”,拉低就是“柔光小夜灯”。
- 🎨 颜色滑块 → 灯光颜色,滑一滑,舞台从冷色变暖色,好像切换场景。
- 📍 位置滑块 → 灯光位置,像移动天花板上的灯,让舞者“被 spotlight 追随”。
💡 滑块分类与效果
| 滑块类型 | 控制参数 | 场景效果示例 |
|---|---|---|
| 强度滑块 🔆 | light.intensity | 调整光亮度,拉高 → 聚光灯,拉低 → 柔光 |
| 颜色滑块 🎨 | light.color | 改变灯光颜色,冷色 → 暖色,瞬间切换场景氛围 |
| 位置滑块 📍 | light.position | 移动灯光位置,让光束追随舞者或固定某个点 |
🎶 音乐驱动:让模型随节奏舞动
想象一下夜店或者舞台 🎵:
DJ 手里抓着唱盘,音乐一响,节奏一到,舞台上的舞者就跟着节拍动起来:
- 低音震动 → 舞者脚下轻轻跳动
- 高音旋律 → 手臂优雅摆动
- 鼓点猛烈 → 全身大幅律动,观众跟着嗨
在 Three.js 里,我们可以让 模型跟随音乐节奏跳舞:
- 🔊 音频分析 (Audio Analysis): 使用
AudioContext或THREE.AudioAnalyser获取音频频谱 - 📈 频谱数据映射到动作: 将高低音、鼓点映射到模型的不同关节或位置
- 💃 实时更新模型: 每一帧根据音量、节奏调整模型的旋转、位移或缩放,让舞者仿佛“活”起来
🌟 结尾
恭喜你!🎉 舞台上的灯光、音乐、镜头、舞者,终于不再是“各跳各的”,而是乖乖听从你这位指挥家的号令了。现在的你,已经不仅仅是“动画大师”,更是集 DJ + 灯光师 + 摄影师 + 编舞师 于一身的超级舞台导演。🎩✨
不过……舞台氛围是不是还少了点“魔法味”?🤔
别急,第六章我们就要请出压轴好戏:
✨ 粒子烟花、流光残影、Shader 魔法 ✨
让舞台直接从“蹦迪广场”升级到“国际演唱会现场”!🔥