普通视图

发现新文章,点击刷新页面。
昨天以前首页

从摄影新手到三维光影师:Three.js 核心要素的故事

作者 一千柯橘
2025年11月29日 16:43

当我第一次学习摄影时,老师告诉我一句话:

“你不是在拍东西,而是在拍光。”

后来我学习 Three.js 时突然意识到:
这句话原来依旧成立。

Three.js 不只是一个 3D 引擎,更像是一台虚拟相机。要拍好这张“虚拟的照片”,我们必须掌握三个核心要素:

场景(Scene)

相机(Camera)
灯光与材质(Light & Material)

于是,我把学习过程想象成一个摄影新手成长为三维光影师的故事。

空无一物的影棚 —— Scene 场景

故事从一个空影棚开始。

当我第一次打开 Three.js 时,教程告诉我:

const scene = new THREE.Scene();

这就像摄影师走进了一个空旷的工作室:
没有布景、没有模特、没有灯光,甚至连相机都还没架好, 在影棚这个场景中,摄影师可以在这个场景中放任何的东西:

  • 架好摄像机(Camera 📹)
  • 拍照的物体(Mesh 网格物体)、物体拥有着自己的形状(Geometry几何体)和材质(Material)
  • 摆设好灯光(Light)
  • 也可以是任意的对象 (Object3D)

摄影师往 Scene 里布置道具,而程序员的你往 Scene 里添加各种对象,因此 场景就是一个可以放任何东西的容器

找到你要观看的角度 —— Camera 相机

刚学摄影时,我最常做的事情,就是移动、蹲下、趴着、绕圈……
只为了找到一个“对的角度”。

Three.js 的相机就是你的眼睛。创建相机就像准备拍摄时拿起单反:

const camera = new THREE.PerspectiveCamera(const camera = new THREE.PerspectiveCamera(
  50, // 相机视野角度,摄像机的视野角度越大,摄像机看到的场景就越大,反之越小
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近平面(近端渲染距离),指定从距离相机多近的位置开始渲染,推荐默认值0.1
  1000 // 远平面(远端渲染距离)指定摄像机从它所在的位置最远能看到多远,太小场景中的远处物体会看不见,太大会浪费资源影响性能,推荐默认值1000
);

// 2.1 设置相机的位置,放在不同的位置看到的风景当然不一样
camera.position.set(5, 10, 10); // x, y, z

camera.lookAt(0, 0, 0); // 设置相机方向(这就是你女朋友让你找最佳角度的原因)

摄影师会说:“我走两步,让模特在背景中更突出。”
程序员会说:

camera.position.z = 3;
camera.lookAt(0, 0, 0)

本质完全一样:
都是在调整观察世界的方式。

让世界真正亮起来 —— Light & Material 灯光与材质

你可以有再漂亮的模特、再好的相机,如果没有光——
一切都会变成漆黑一片。

Three.js 也是如此。你搭了一个完美的 3D 模型,如果没有光,它看起来只是纯黑。

于是我制作“虚拟布光”:

const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 10, 5);
scene.add(light);

摄影师打灯,而我在代码里放置光源:

  • DirectionalLight(平行光)= 太阳光
  • PointLight(点光源)= 想象灯泡发光,由点向八方发射
  • SpotLight(聚光灯)= 舞台灯,从上打下来,呈现圆锥体,它离光越远,它的尺寸就越大。这种光源会产生阴影
  • AmbientLight(环境光)= 影棚柔光,环境光没有特定的来源方向,不会产生阴影

同时材质(Material)也等同于现实世界的“被光击中时的反应”:

  • 皮肤 = standard material
  • 金属 = metalness 高
  • 塑料 = roughness 较高
  • 玻璃 = transparent=True + envMap

想要一个皮肤质感的物体?
那么你就得给材质加入 roughness、metalness、normalMap 就像摄影师在打柔光,为人物皮肤创造质感。

光与材质的搭配,就是 Three.js 里的“布光艺术”。

最终章:按下快门 —— Renderer 渲染器

当场景布好、相机调好、灯光到位后——
摄影师要做的就是按下快门。

在 Three.js 里:

renderer.render(scene, camera);

渲染器就是那个“快门”,
真正把世界投射到屏幕上。

摄影师用快门把现实世界的光记录下来;
Three.js 用 GPU 把虚拟世界的光影计算出来。

本质上,两者做的是同一件事:

把真实或虚拟的三维世界,投射成一张二维图像。

import * as THREE from "three";

// 1. 创建场景
const scene = new THREE.Scene();

// 2. 创建相机(透视投影相机)
const camera = new THREE.PerspectiveCamera(
  50, // 相机视野角度,摄像机的视野角度越大,摄像机看到的场景就越大,反之越小
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近平面(近端渲染距离),指定从距离相机多近的位置开始渲染,推荐默认值0.1
  1000 // 远平面(远端渲染距离)指定摄像机从它所在的位置最远能看到多远,太小场景中的远处物体会看不见,太大会浪费资源影响性能,推荐默认值1000
);

// 2.1 设置相机的位置
camera.position.set(5, 10, 10); // x, y, z

camera.lookAt(0, 0, 0); // 设置相机方向(默认看向场景原点)

// 3. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true }); // 开启抗锯齿,使边缘更平滑
// 3.1 设置渲染器的大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 3.2 将渲染器的canvas内容添加到body
document.body.appendChild(renderer.domElement);

// 4. 创建一个立方体几何体
const geometry = new THREE.BoxGeometry(4, 4, 4); // 宽、高、深

// 为了让光源有效果,我们使用 MeshLambertMaterial 或 MeshPhongMaterial
//  创建材质 MeshLambertMaterial (兰伯特材质) 是一种非光泽材质,会受光照影响,但没有镜面高光
const material = new THREE.MeshLambertMaterial({
  color: 0x00ff00, // 颜色
  // wireframe: true, // 如果需要线框效果可以加上
});

// 6. 创建一个网格模型(网格模型由几何体和材质组成)
// Mesh 构造函数通常只接受一个材质。如果需要多材质,Three.js 有专门的 MultiMaterial 或 Group 来处理
const cube = new THREE.Mesh(geometry, material); // 使用 MeshLambertMaterial

// 6.1 将几何模型添加到场景中
scene.add(cube);

// 6.2 设置相机看向物体(拍摄对象)的位置(默认状态下相机看向的是场景的原点(0,0,0))
camera.lookAt(cube.position);

// 7. 创建光源
const spotLight = new THREE.SpotLight(0xffffff); // 创建聚光灯,颜色为白色
// 7.1 设置光源的位置
spotLight.position.set(0, 20, 20); // 调整光源位置,使其能够照亮立方体
// 7.2 设置光源照射的强度,默认值为1, 越大越亮
spotLight.intensity = 2;
// 7.3 将光源添加到场景中
scene.add(spotLight);

// 8. 为了方便观察 3D 图像,添加三维坐标系对象
const axesHelper = new THREE.AxesHelper(6); // 参数表示坐标系的大小 (x轴红色, y轴绿色, z轴蓝色)
scene.add(axesHelper); // 将坐标系添加到场景中

// 9. 渲染函数
function animate() {
  requestAnimationFrame(animate); // 请求再次执行渲染函数animate,形成循环

  // 让立方体动起来
  cube.rotation.x += 0.01; // 沿x轴旋转
  cube.rotation.y += 0.01; // 沿y轴旋转
  cube.rotation.z += 0.01;

  renderer.render(scene, camera); // 使用渲染器,通过相机将场景渲染出来
}

animate(); // 执行渲染函数,进入无限循环,完成渲染

OrbitControls 的完整原理

作者 big男孩
2025年11月28日 11:20

🎯 OrbitControls 的完整原理(最精简 + 最准确版)

OrbitControls 的核心目标很简单:

让相机围绕一个目标点(target)做旋转、缩放、平移,同时保持视角稳定。****

它的实现不是直接修改相机的 rotation,而是:


✅ 1. 使用球坐标系存储相机相对 target 的位置

OrbitControls 内部用 球坐标系 (spherical) 表示相机在球面上的位置:

spherical 属性 代表含义 决定什么
radius 相机到 target 的距离 缩放(远近)
theta 水平旋转(绕 Y 轴) 左右旋转
phi 垂直旋转(从 Y+ 向下量角) 上下旋转

OrbitControls 并不直接存相机 position,而是依赖 spherical。


✅ 2. 每次更新,都会根据 spherical 重新计算 camera.position

伪代码:

// 球坐标转换为世界坐标
offset.setFromSpherical(spherical);

// camera.position = target + offset
camera.position.copy(target).add(offset);

// 相机永远朝向 target
camera.lookAt(target);

也就是说:

相机的位置永远落在一个以 target 为圆心的球面上。****


✅ 3. 用户的三种交互对应修改 spherical 或 target

✔(1)旋转(左键)

修改 spherical 的角度:

theta ← 左右拖动
phi   ← 上下拖动

效果:相机绕 target 转圈。


✔(2)缩放(滚轮)

修改 spherical.radius:

radius += delta

效果:相机沿着射线(camera → target)前进或后退。


✔(3)平移(右键)

修改 target(以及 camera.position 同步移动):

target += panDelta
camera.position += panDelta

这样保持相机与 target 的相对距离不变,视野整体平移。


📌 Why?为什么不直接改 camera.rotation?

因为:

  • 欧拉角 rotation 会出现万向节锁 (gimbal lock)

  • 旋转结果顺序依赖 Euler 的 order

  • 不能保证相机固定绕某点旋转

  • 平移与旋转混合会很混乱

使用 spherical + lookAt(target) 是最稳定、最可控的方案。


🎨 图示(概念图)

          Y+
          |
          |      camera ●
          |       (theta, phi, radius)
          |        /
          |       /
          |      /
          |     /
          ●----+------------------ X+
         target

📌 相机始终在半径为 radius 的球面上

📌 用户的操作实质是改变球坐标的位置


🎯 OrbitControls 本质上做的两件事

无论用户怎么操作,都只是修改:

  1. camera.position****

  2. controls.target

最终调用:

camera.lookAt(target);

而不动 rotation。


📦 大总结(你可以直接放到文档里)

OrbitControls = 基于球坐标的相机系统,通过改变 spherical(旋转/缩放)和 target(平移),生成 camera.position,然后使用 lookAt 保持视角稳定。****

  • 旋转 = 改 theta 和 phi

  • 缩放 = 改 radius

  • 平移 = 改 target

相机不会直接修改 rotation,而是由 target 和 spherical 决定最终的相机朝向和位置。


一些经典的3D编辑器开源项目

作者 答案answer
2025年11月27日 08:56

前言

给大家分享一下个人在探索开发three.js编辑器项目期间发现的一些比较不错的3D编辑器类型的开源项目,如果你也正打算做类似相关的项目,那么这些开源项目会是一个不错的参考借鉴

以下排名不分先后🙏🏻

项目一:Astral3D

描述:基于Vue3 + THREE.JS 免费开源的三维引擎及配套编辑器,包含BIM轻量化、CAD解析预览、粒子系统、插件系统等功能。

特点:强大的3D场景内容元素的编辑和保存功能和丰富多样的3D元素内容,同时支持BIMCAD等工业建模文件的加载渲染

注意⚠️:项目是Apache-2.0 license 的开源协议,项目作者本人也声明了项目可用于个人学习,如有商用需要向作者申请商用授权

界面

image.png

在线地址editor.astraljs.com/#/

Github: github.com/mlt131220/A…

项目二:thebrowserlab

描述:一个「运行在浏览器里的 3D 编辑器 + 创意编码 (creative-coding) 环境」

特点:支持加载视频、文本、图片、粒子等内容并提供了丰富的编辑表单参数可视化编辑配置,同时还支持在线代码的脚本内容写入设置3D场景内容。

注意⚠️:项目使用 MIT 授权 (MIT license),意味着你可以自由地 fork、修改、商用 (遵守 MIT 即可)

界面

image.png

在线地址thebrowserlab.com/

Github: github.com/icurtis1/th…

项目三:threepipe

描述:一个基于 Three.js 构建的现代 3D 框架

特点:项目基于了Three.js进行了二次封装,提供了不少高级功能,使其适合从简单 3D 模型预览到复杂 交互 / 渲染应用,通过简单的API 使用就可以快速创建复杂的3D模型预览器,模型编辑器等内容。

注意⚠️:既然是封装好的框架,在享受使用的便利时,新的学习成本也是不可避免的,项目使用 Apache-2.0-1协议,商用也许需要授权,不过毕竟是歪果仁开发的,即使未授权也难以知晓

界面

image.png在线地址editor.threepipe.org/

Github: github.com/repalash/th…

项目四:ShadowEditor

描述:基于Three.js、Go语言和MongoDB的跨平台的3D场景编辑器,支持桌面版和Web版。

特点:跨平台的支持 Windows / Linux / Mac,在桌面 (desktop) 和浏览器 (web) 中都能运行,前后端一体的项目

注意⚠️:使用 MIT 许可证的项目,可以自由用于学习、实验或商业用途。从界面不难看出,应该是属于上古时期的项目了,three.js版本也是107的。作者也推出了商业版的,如有需要也可以试用一下商业版的

界面

image.png在线地址www.hylab.cn/shadowedito…

Github: gitee.com/tengge1/Sha…

项目五:three-editor

描述:一个基于 Three.js 的 可视化 / 低代码 3D 编辑器 / 内核/框架。它的目标是降低使用 Three.js 的门槛,让构建 Web 3D 场景更简单、更迅速

特点:提供了一整套“可视化 + 配置 + 编辑 + 渲染”的能力,使得即使不深入了解 Three.js,也能快速构建 3D 场景 / 项目,:如果你只是想在网页中展示某个 3D 模型、场景或交互,而不想编写大量 Three.js boilerplate,three-editor 能极大降低门槛

注意⚠️:因为场景内容都是封装处理好的,提供的可编辑参数内容配置并不多,如果你的自定义需求很多的话使用这个项目前需要谨慎考虑一下

界面

image.png

在线地址z2586300277.github.io/threejs-edi…

Github: github.com/z2586300277…

项目六:scene-editor

描述:vis-three/scene-editor 是基于 vis-three 框架构建的 —— vis-three 本身是一个封装自 Three.js 的前端 3D 开发框架,用于简化 Web3D 开发

特点:基于vis-three 衍生开发的一个3D编辑器提供了一套较为完整的 Web 3D 场景编辑功能 — 目标是让你即使对 3D 或 Three.js 不熟,也能比较轻松地 “拖/配/编辑” 出一个 3D 场景

注意⚠️:仓库地址的代码是Vue3项目编译打包后的,作者并没有直接提供Vue3项目的源代码,如果有二次开发需求,无法直接性修改源代码

界面

image.png

在线地址z2586300277.github.io/threejs-edi…

Github: github.com/Shiotsukika…

Gitee:gitee.com/vis-three/s…

项目七: three.js官方编辑器

描述Three.js(著名的 WebGL / Web 3D 渲染库)自带 / 官方提供的可视化编辑器,接触过three.js的应该都知道吧

特点3D编辑器的鼻祖了也是唯一一个能和three.js最新版本保持随时同步的编辑器,很多现有的商业项目和开源项目的功能,或多或少都参考了这个项目去实现的

注意⚠️:使用原生js 去实现的,二次开发和扩展功能成本较大

界面

image.png

在线地址threejs.org/editor/

Github: github.com/mrdoob/thre…

项目八: threejs-3dmodel-edit

描述:一个基于 Three.js + Vue 3 + TypeScript + Pinia 的前端 3D 模型编辑器 / 可视化编辑平台

特点:是一个比较完整、现代、易用的 Web-based 3D 模型编辑器 — 它把 Three.js 的功能通过 Vue / TS / Pinia 封装起来,让非专业 3D 建模背景的人也能比较容易地加载 /编辑 /导出 /展示 3D 模型。基于企业级项目代码开发的标准规范,如果你正在开发自己的第一个企业级Three.js 项目那么这个项目的代码设计思路将会是一个不错的参考

注意⚠️:作者本人的3D开源项目,毛遂自荐一下,哈哈哈哈

界面

image.png

在线地址threeflowx.cn/open/#/

Github: github.com/zhangbo126/…

Gitee:gitee.com/ZHANG_6666/…

结语

ok以上就是作者本人已知的一些不错的开源3D编辑器合集了,如果你还知道一些好的3D编辑器项目欢迎评论区补充

❌
❌