Three.js 高级开发面试题集锦 (简易篇)

87 阅读16分钟

本文档旨在覆盖从 Three.js 基础到图形学底层、性能优化及 WebGPU 前沿的 100 道面试题。


第一部分:基础核心 (Q1-Q20)

Q1: Three.js 的三大核心组件是什么?

  • Scene (场景):所有物体的容器。
  • Camera (相机):决定观察视角(透视或正交)。
  • Renderer (渲染器):将场景和相机结合,渲染出最终画面 (Canvas)。

Q2: BufferGeometry 和 Geometry (已废弃) 的区别?

  • BufferGeometry:数据存储在 TypedArray (如 Float32Array) 中,直接映射到 GPU 的 Buffer,性能极高,内存占用低。
  • Geometry:使用 Vector3Face3 对象存储,内存占用大,转换到 GPU 慢,现已从 Three.js 核心移除。

Q3: Mesh, Line, Points 的区别?

  • Mesh:三角形网格,由顶点索引构成面。
  • Line:线段,连接相邻顶点。
  • Points:粒子点,每个顶点渲染为一个面向屏幕的方形/圆形。

Q4: 什么是 Scene Graph (场景图)?

  • 一种树状结构,用于管理场景中的物体。
  • 子物体 (children) 会继承父物体 (parent) 的变换(位置、旋转、缩放)。
  • 核心方法:add(), remove(), attach()

Q5: PerspectiveCamera 和 OrthographicCamera 的区别?

  • PerspectiveCamera (透视):近大远小,模拟人眼,用于 3D 场景。参数:fov, aspect, near, far。
  • OrthographicCamera (正交):无透视效果,物体大小与距离无关,用于 2D UI、工程制图。参数:left, right, top, bottom, near, far。

Q6: 解释 Three.js 中的材质 (Material) 基类及其共有属性。

  • 所有材质继承自 Material
  • 共有属性:opacity (透明度), transparent (是否透明), side (Front/Back/Double), visible (可见性), depthTest (深度测试), depthWrite (深度写入)。

Q7: MeshBasicMaterial, MeshLambertMaterial, MeshPhongMaterial 的区别?

  • Basic:不受光照影响,纯色。
  • Lambert:基于 Lambert 光照模型,漫反射,不支持镜面高光,适合粗糙表面(性能好)。
  • Phong:基于 Blinn-Phong 模型,支持镜面高光 (Shininess),适合光滑表面。

Q8: 什么是 WebGLRenderer 的 antialias (抗锯齿)?

  • 开启后,WebGL 会尝试使用 MSAA (多重采样抗锯齿) 处理边缘锯齿。
  • 注意:在高 DPI (Retina) 屏幕上,通常不需要开启,或者配合 setPixelRatio 使用,否则性能开销大。

Q9: 如何处理窗口大小调整 (Resize)?

  • 更新相机的 aspect (长宽比)。
  • 调用 camera.updateProjectionMatrix()
  • 调用 renderer.setSize(width, height)
  • 调用 renderer.setPixelRatio(window.devicePixelRatio)

Q10: Group 和 Object3D 有什么区别?

  • 功能上几乎完全相同。
  • Group 语义更明确,表示“一组物体”。
  • Object3D 是基类,所有可变换物体都继承自它。

Q11: 什么是 Clock 对象?为什么要用它?

  • 用于跟踪时间。
  • getDelta():获取上一帧到当前帧的时间间隔 (秒)。
  • 用途:保证动画速度与帧率无关(Frame Rate Independence),移动距离 = 速度 * delta。

Q12: Three.js 坐标系是左手还是右手?

  • 右手坐标系
  • X轴向右,Y轴向上,Z轴指向屏幕外(观察者)。

Q13: 如何获取物体的世界坐标?

  • object.getWorldPosition(targetVector3)
  • 或者手动更新矩阵后从 matrixWorld 中提取。

Q14: 什么是 Layers (图层)?

  • 用于控制相机只渲染特定层的物体,或者光照只影响特定层的物体。
  • object.layers.set(1)camera.layers.enable(1)

Q15: 什么是 Fog (雾)?有哪些类型?

  • Fog:线性雾,随距离线性变浓。参数:color, near, far。
  • FogExp2:指数雾,随距离指数变浓,更真实。参数:color, density。

Q16: 如何克隆 (Clone) 和 复制 (Copy) 一个物体?

  • clone():创建新对象(浅拷贝,几何体和材质通常共享引用)。
  • copy(source):将 source 的属性值复制给当前对象。

Q17: 什么是 UserData?

  • object.userData:一个空对象 {},专门留给开发者存储自定义数据,不会被 Three.js 内部逻辑修改。

Q18: 解释 dispose() 的作用。

  • Three.js 创建的 WebGL 资源(Buffer, Texture, Shader)不会自动释放。
  • 必须手动调用 geometry.dispose(), material.dispose(), texture.dispose() 来释放 GPU 内存,防止内存泄漏。

Q19: 什么是 Color Space (色彩空间)?Linear vs sRGB?

  • Linear:线性空间,适合物理计算(光照混合)。
  • sRGB:显示器显示的非线性空间。
  • 流程:纹理通常是 sRGB -> 解码为 Linear 进行渲染计算 -> 编码回 sRGB 输出到屏幕。
  • Three.js 默认使用 THREE.SRGBColorSpace

Q20: 什么是 ShadowMaterial?

  • 一种透明材质,只接收阴影。
  • 用途:AR 应用中,将虚拟物体的阴影投射到真实世界的视频流背景上。

第二部分:数学与算法 (Q21-Q30)

Q21: Vector3 的 dot() 和 cross() 分别代表什么?

  • dot (点乘):结果是标量。a·b = |a||b|cosθ。用于计算夹角、投影、判断前后方向。
  • cross (叉乘):结果是向量。垂直于 a 和 b 构成的平面。用于计算法线、左右方向。

Q22: 矩阵 (Matrix4) 在 3D 中的作用?

  • 存储变换信息(平移、旋转、缩放)。
  • ModelMatrix:局部 -> 世界。
  • ViewMatrix:世界 -> 相机空间。
  • ProjectionMatrix:相机空间 -> 裁剪空间 (NDC)。

Q23: 欧拉角 (Euler) 的万向节死锁 (Gimbal Lock) 是什么?

  • 使用 X/Y/Z 轴旋转时,当中间轴旋转 90 度,会导致另外两个轴重合,失去一个旋转自由度。
  • 解决:使用四元数 (Quaternion)。

Q24: 四元数 (Quaternion) 的优势?

  • 避免万向节死锁。
  • 插值 (Slerp) 平滑。
  • 计算效率比矩阵高。

Q25: 什么是 Raycaster (射线检测)?原理是什么?

  • 原理:从相机位置穿过屏幕点击点发射一条射线。
  • 检测:计算射线与场景中包围球/包围盒/三角形的交点。
  • 用途:鼠标拾取、点击事件。

Q26: 什么是 Frustum Culling (视锥体剔除)?

  • 判断物体的包围体(Bounding Sphere/Box)是否在相机视锥体内。
  • 如果在外面,则完全不提交给 GPU 绘制。

Q27: 包围盒 Box3 和 Sphere 的区别?

  • Sphere (包围球):计算快,检测快,但不够紧凑。
  • Box3 (AABB 轴对齐包围盒):比球更紧凑,但旋转后需要重新计算(因为它始终轴对齐)。
  • OBB (有向包围盒):随物体旋转,最紧凑,但计算最复杂。

Q28: 什么是 NDC (标准化设备坐标)?

  • 坐标范围 [-1, 1] 的立方体空间。
  • Vertex Shader 的最终输出 gl_Position 就是 NDC 坐标。

Q29: lookAt() 方法是如何工作的?

  • 构建一个旋转矩阵,使物体的 Z 轴(负方向)指向目标点,Y 轴指向上方。
  • 常用于相机跟随、炮塔瞄准。

Q30: 什么是 MVP 矩阵?

  • M (Model) * V (View) * P (Projection)
  • 顶点坐标变换流程:Local -> World -> Camera -> Clip Space。

第三部分:纹理与材质进阶 (Q31-Q45)

Q31: 纹理过滤 Nearest vs Linear?

  • Nearest:最近邻,像素化风格(Minecraft)。
  • Linear:线性插值,平滑模糊。

Q32: 什么是 Mipmap?

  • 生成一系列分辨率递减的纹理图。
  • 作用:解决远处物体纹理闪烁(摩尔纹)问题,提高缓存命中率。
  • 要求:纹理尺寸最好是 2 的幂次方 (POT)。

Q33: 各向异性过滤 (Anisotropy) 是什么?

  • 解决以倾斜角度观察纹理时变模糊的问题(如地面延伸到远处)。
  • texture.anisotropy = renderer.capabilities.getMaxAnisotropy()

Q34: UV 映射是什么?

  • 将 2D 纹理坐标 (0~1) 映射到 3D 模型的顶点上。
  • geometry.attributes.uv

Q35: 法线贴图 (Normal Map) vs 凹凸贴图 (Bump Map)?

  • Bump Map:灰度图,只模拟高度扰动,效果较假。
  • Normal Map:RGB 图,直接改变表面法线方向,光影细节更真实,性能消耗极低。

Q36: 置换贴图 (Displacement Map) 的特点?

  • 真正改变顶点的几何位置。
  • 需要网格有足够多的顶点(细分),否则效果呈锯齿状。

Q37: 环境贴图 (Environment Map) 有哪几种形式?

  • CubeTexture:6 张图组成的立方体盒子。
  • Equirectangular:一张全景图 (HDR/EXR),球形展开。

Q38: 什么是 Alpha Map?

  • 灰度图,控制材质的透明度(白色不透,黑色全透)。
  • 需要开启 transparent = true

Q39: 纹理压缩格式 KTX2 / DDS 的优势?

  • 显存优化:GPU 可以直接读取压缩格式,无需解压,大幅降低显存占用。
  • 带宽优化:文件体积小,加载快。

Q40: 什么是 DataTexture?

  • 直接用 JavaScript 的 TypedArray 数据创建纹理。
  • 用途:GPGPU 计算(存位置/速度)、程序化生成纹理。

Q41: 什么是 DepthMaterial (深度材质)?

  • 将片元到相机的距离渲染为灰度颜色。
  • 用途:阴影贴图生成、景深效果、SSAO。

Q42: 什么是 Side 属性 (FrontSide, BackSide, DoubleSide)?

  • 决定渲染三角形的哪一面。
  • BackSide:常用于天空盒(从内部看球体)。
  • DoubleSide:性能开销加倍,尽量少用。

Q43: 什么是 Blending (混合模式)?

  • 控制新像素颜色如何与背景颜色混合。
  • NormalBlending:正常遮挡/透明。
  • AdditiveBlending:加法混合(发光效果,粒子)。
  • SubtractiveBlending:减法混合。

Q44: 什么是 Premultiplied Alpha?

  • 颜色值预先乘上了 Alpha 值。
  • 可以避免混合时的黑边问题。

Q45: HDR (高动态范围) 纹理的优势?

  • 存储超过 0~1 范围的亮度值。
  • 提供真实的光照信息,用于 IBL (基于图像的照明)。

第四部分:光照与阴影 (Q46-Q55)

Q46: AmbientLight, HemisphereLight, DirectionalLight 的区别?

  • Ambient:均匀照亮所有物体,无方向,无阴影。
  • Hemisphere:模拟天空和地面反光,有上下两个颜色渐变。
  • Directional:平行光(太阳),有方向,产生平行阴影。

Q47: PointLight, SpotLight, RectAreaLight 的区别?

  • Point:点光源(灯泡),向四周发散。
  • Spot:聚光灯(手电筒),有锥形范围。
  • RectArea:面光源(窗户、灯管),光照更柔和真实,消耗高。

Q48: Three.js 阴影的实现原理?

  • Shadow Map:从光源视角渲染深度图,再在主渲染流程中对比深度。

Q49: 常见的阴影类型 (Basic, PCF, PCFSoft, VSM)?

  • Basic:像素化严重,性能最高。
  • PCF:边缘模糊处理,默认选择。
  • PCFSoft:更柔和。
  • VSM:方差阴影,解决漏光,适合模糊阴影。

Q50: 什么是 Shadow Acne (阴影痤疮) 和 Peter Panning (悬浮)?

  • Acne:由于精度问题,物体表面产生了自我遮挡的条纹。解决:增加 bias
  • Peter Panningbias 太大导致阴影与物体分离。解决:调整 biasnormalBias

Q51: 什么是 Light Probe (光照探针)?

  • 预计算空间中某一点的光照信息(球谐函数 SH)。
  • 用于替代昂贵的实时光照,照亮动态物体。

Q52: 什么是 Physically Correct Lights (物理正确光照)?

  • 使用物理单位(如流明、坎德拉)计算光照强度。
  • 光照随距离平方衰减。
  • renderer.useLegacyLights = false (新版默认)。

Q53: 什么是 IBL (Image Based Lighting)?

  • 使用环境贴图(HDR)作为光源照亮物体。
  • PBR 渲染的核心,提供真实的反射和漫反射环境光。

Q54: 阴影贴图的分辨率 (mapSize) 影响什么?

  • 分辨率越高,阴影越清晰,但显存占用和计算开销越大。
  • 通常设为 1024, 2048, 4096。

Q55: 如何优化阴影性能?

  • 减少投射阴影的光源数量。
  • 使用 Bake (烘焙) 将静态阴影烧录到纹理中。
  • 限制 Shadow Camera 的视锥体范围 (camera.left/right/top/bottom),使其紧贴场景。

第五部分:PBR 与渲染原理 (Q56-Q65)

Q56: PBR 的两个核心工作流?

  • Metalness/Roughness (Three.js 标准):金属度、粗糙度。
  • Specular/Glossiness:高光颜色、光泽度。

Q57: 什么是 Tone Mapping (色调映射)?

  • 将 HDR (高动态范围) 的颜色压缩到显示器能显示的 LDR (0~1) 范围。
  • Reinhard:简单压制。
  • ACESFilmic:电影级效果,对比度高,色彩鲜艳(推荐)。

Q58: 什么是 Gamma Correction (伽马校正)?

  • 人眼对亮度的感知是非线性的。
  • 渲染计算在线性空间,输出时需要进行 Gamma 校正 (Power 1/2.2) 以匹配显示器。

Q59: 什么是 Z-Fighting (深度冲突)?

  • 两个面距离太近,深度值精度不足,导致闪烁。
  • 解决:增加距离、使用 logarithmicDepthBuffer、调整相机 near/far 范围。

Q60: 什么是 Stencil Buffer (模板缓冲区)?

  • 用于遮罩、剖切、轮廓描边。
  • 控制像素是否被写入颜色缓冲区。

Q61: 什么是 Draw Call?为什么它影响性能?

  • CPU 通知 GPU 绘制一次网格的命令。
  • 每次 Draw Call 都有 CPU 准备数据的开销。Draw Call 太多会导致 CPU 瓶颈。

Q62: 什么是 Render Order (渲染顺序)?

  • 不透明物体:从近到远渲染(利用深度测试减少像素重绘)。
  • 透明物体:从远到近渲染(保证混合正确)。
  • 可手动设置 renderOrder

Q63: 什么是 MSAA, FXAA, SMAA?

  • MSAA:硬件多重采样,质量好,显存高(WebGLRenderer 默认)。
  • FXAA:后处理快速近似抗锯齿,有些模糊。
  • SMAA:后处理子像素形态抗锯齿,质量高,开销中等。

Q64: 什么是 AO (Ambient Occlusion)?

  • 环境光遮蔽。模拟角落、缝隙处光线难以到达产生的阴影,增加立体感。

Q65: 什么是 Deferred Rendering (延迟渲染)?Three.js 支持吗?

  • 先渲染几何信息(位置、法线、颜色)到 G-Buffer,再统一计算光照。支持大量光源。
  • Three.js 默认是 Forward Rendering (前向渲染)。延迟渲染需要通过 WebGPURenderer 或自定义 Shader 实现。

第六部分:Shader 与 WebGL (Q66-Q80)

Q66: Vertex Shader 和 Fragment Shader 的区别?

  • Vertex:处理顶点,计算位置 (gl_Position)。
  • Fragment:处理像素(片元),计算颜色 (gl_FragColor)。

Q67: Uniform, Attribute, Varying 的区别?

  • Uniform:全局变量,对所有顶点/片元相同(如时间、光照位置)。
  • Attribute:顶点独有数据(如位置、UV、法线),只在 Vertex Shader 可用。
  • Varying:从 Vertex 传递给 Fragment 的插值数据。

Q68: ShaderMaterial 和 RawShaderMaterial 的区别?

  • ShaderMaterial:包含 Three.js 内置的 uniforms 和 attributes,支持 #include
  • RawShaderMaterial:纯净环境,不包含任何内置变量,需手动声明。

Q69: 什么是 onBeforeCompile?

  • 在 Three.js 编译内置材质 Shader 之前,拦截并修改 Shader 代码。
  • 用于在标准材质上微调效果(如添加噪点、顶点波浪),而无需重写整个 Shader。

Q70: 常用 GLSL 函数有哪些?

  • mix(): 线性插值。
  • step(): 阶梯函数(0 或 1)。
  • smoothstep(): 平滑阶梯(S形曲线)。
  • fract(): 取小数部分。

Q71: 什么是 Post-processing (后处理)?

  • 在场景渲染完成后,对最终图像进行滤镜处理。
  • 如:Bloom (辉光), DOF (景深), Vignette (暗角)。

Q72: EffectComposer 的工作流?

  • RenderPass (场景 -> FBO) -> ShaderPass (FBO -> 处理 -> FBO) -> ... -> OutputPass (屏幕)。
  • 使用 Ping-Pong 双缓冲技术。

Q73: 什么是 GPGPU?

  • General-Purpose computing on GPU。
  • 利用纹理存储非图像数据(如粒子位置),在 Fragment Shader 中进行物理计算。

Q74: 什么是 FBO (Frame Buffer Object)?

  • 帧缓冲区对象。允许渲染到纹理,而不是屏幕。

Q75: gl_Position 的 w 分量有什么用?

  • 用于透视除法。(x/w, y/w, z/w) 将坐标转换到 NDC 空间。

Q76: 什么是 Highp, Mediump, Lowp?

  • GLSL 中的精度限定符。
  • highp (高精度,位置计算), mediump (中精度,UV/颜色), lowp (低精度)。

Q77: 如何在 Shader 中实现纹理滚动?

  • vUv + time * speed

Q78: 什么是 Signed Distance Field (SDF)?

  • 符号距离场。表示空间中一点到最近物体表面的距离。
  • 用于字体渲染、Raymarching (光线步进) 渲染体积云/分形。

Q79: WebGL 1.0 和 2.0 的主要区别?

  • WebGL 2.0 基于 OpenGL ES 3.0。
  • 支持 3D Texture, MSAA Renderbuffer, Uniform Buffer Objects, 更多的纹理格式。
  • Three.js 默认尝试使用 WebGL 2.0。

Q80: 什么是 Uniform Buffer Object (UBO)?

  • 允许在多个 Shader 间共享同一块 Uniform 数据块(如全局光照信息),减少 CPU-GPU 传输开销。

第七部分:性能优化 (Q81-Q90)

Q81: InstancedMesh (实例化网格) 的原理?

  • 一次 Draw Call 绘制多个相同的几何体。
  • 通过 Attribute 存储每个实例的变换矩阵 (Matrix) 和颜色。

Q82: 什么是 Geometry Merging (合并几何体)?

  • 将多个静态物体的几何体数据合并成一个大的 BufferGeometry。
  • 减少 Draw Call,但失去了单独控制每个物体的能力。

Q83: 什么是 LOD (Level of Detail)?

  • 根据物体距离相机的远近,动态切换高/中/低面数的模型。
  • 平衡画质与性能。

Q84: 如何检测性能瓶颈?

  • Stats.js:查看 FPS, MS (帧时)。
  • renderer.info:查看 Draw Calls, Triangles, Textures 数量。
  • Spector.js:截帧分析 WebGL 命令。

Q85: 纹理优化的策略?

  • 尺寸:POT (2的幂),不要过大。
  • 格式:使用 .jpg (不透明), .png (透明), .ktx2 (GPU压缩)。
  • Mipmap:开启以优化显存带宽。

Q86: 什么是 OffscreenCanvas?

  • 在 WebWorker 中进行渲染,避免主线程(UI线程)卡顿。

Q87: Draco 压缩是什么?

  • Google 开源的几何体压缩库。
  • 大幅减小 .gltf 文件体积,但需要在客户端进行解码(有少许 CPU 开销)。

Q88: 什么是 Object Pooling (对象池)?

  • 预先创建一组对象(如子弹),使用时取出,用完后重置并放回,而不是销毁。
  • 避免频繁 GC (垃圾回收) 造成的卡顿。

Q89: 什么时候使用 frustumCulled = false?

  • 当物体在 Vertex Shader 中发生了大幅度偏移(如飘动的旗帜),导致原本的包围盒失效,被错误剔除时。

Q90: 什么是 PowerPreference?

  • new WebGLRenderer({ powerPreference: 'high-performance' })
  • 提示浏览器使用独立显卡(双显卡电脑)。

第八部分:WebGPU、动画与工程化 (Q91-Q100)

Q91: WebGPU 的 Compute Shader 是什么?

  • 一种不经过光栅化管线,直接利用 GPU 并行计算能力的着色器。
  • 适合粒子系统、物理模拟、图像处理。

Q92: 什么是 Storage Buffer?

  • WebGPU 中的一种缓冲区,允许 Compute Shader 读写大量结构化数据。
  • 比 WebGL 的 Texture Hack 更灵活、容量更大。

Q93: Three.js 的 TSL (Three Shading Language) 是什么?

  • 一种基于节点的 Shader 编写方式(JS 语法)。
  • 自动编译为 WGSL (WebGPU) 或 GLSL (WebGL),实现跨后端兼容。

Q94: 骨骼动画 (SkinnedMesh) 的原理?

  • 顶点绑定到骨骼 (Bone)。
  • 每个顶点有 skinIndices (骨骼索引) 和 skinWeights (权重)。
  • 顶点位置 = 骨骼变换矩阵 * 初始位置 * 权重。

Q95: Morph Targets (变形动画) 的原理?

  • 存储顶点的多个目标位置(如表情:笑、哭)。
  • 在 Shader 中对基础位置和目标位置进行线性插值。

Q96: 常见的物理引擎有哪些?

  • Cannon.js:纯 JS,轻量,停止维护但常用。
  • Ammo.js:Bullet 引擎的 WASM 版,功能全但重。
  • Rapier:Rust 编写的 WASM 引擎,性能极高,新贵。
  • PhysX:NVIDIA 引擎,Web 版较少见。

Q97: 如何实现第一人称漫游 (Collision Detection)?

  • 简单:Raycaster 检测脚下高度。
  • 复杂:使用物理引擎的胶囊体 (Capsule) 碰撞检测。
  • Octree (八叉树):优化静态场景的碰撞检测性能。

Q98: LoadingManager 的作用?

  • 统一管理多个加载器 (Texture, Model)。
  • 提供 onProgress, onLoad 回调,用于制作全局进度条。

Q99: 如何在 Blender 中优化导出给 Three.js 的模型?

  • 应用变换 (Apply Transforms)。
  • 合并网格 (Join Meshes)。
  • 烘焙光照贴图 (Bake Texture)。
  • 使用 GLTF 格式导出,勾选压缩。

Q100: Web 3D 的未来趋势?

  • WebGPU 普及,带来桌面级渲染能力。
  • AI 生成 3D (TripoSR, CSM)。
  • 混合渲染 (光线追踪 + 光栅化)。
  • 轻量化与云渲染 结合。