news 2026/9/10 22:31:07

如何用 three.js 的 GLTFLoader 加载 .glb/.gltf 模型并渲染?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何用 three.js 的 GLTFLoader 加载 .glb/.gltf 模型并渲染?

如何用 three.js 的 GLTFLoader 加载 .glb/.gltf 模型并渲染?

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

任务是:在网页应用里把 glTF 2.0 格式的模型(.glb.gltf)加载进 three.js 场景并渲染出来。three.js 官方手册明确推荐 glTF 作为运行时模型格式,.GLB.GLTF两种版本都得到了良好的支持(见 Loading 3D Models)。本文按「搭好本地环境 → 导入加载器 → 加载并渲染 → 验证结果」的路径展开,代码来自官方 API 文档与 manual 教程。

准备条件:项目结构与本地服务器

three.js 项目至少需要一个 HTML 文件和一个 JavaScript 文件,纹理、音频和 3D 模型通常放在public/(或叫 static)目录下,这些文件会原样推送到网站(见 Installation 手册)。

官方手册给出两种搭建方式,任选其一即可:

方式一:npm + Vite(手册推荐的默认方式)

  1. 安装 Node.js,然后在项目目录执行:
npm install --save three npm install --save-dev vite
  1. 运行本地开发服务器:
npx vite
  1. 终端出现类似http://localhost:5173的地址,在浏览器打开它。此时页面是空白的,说明环境就绪。

方式二:CDN + import map(可选分支)

不装构建工具时,需要在index.html<head>中加入 import map(<version>需替换为真实版本号,手册示例用的是"v0.149.0",最新版本号以 npm 列表为准):

<script type="importmap"> { "imports": { "three": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js", "three/addons/": "https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/" } } </script>

然后装 Node.js 并运行npx serve .启动本地服务器,打开终端给出的http://localhost:3000

两种方式都有一个硬性前提:不能直接双击 HTML 文件打开。手册说明,出于安全原因,直接以本地文件方式打开页面时,后面要用到的功能无法正常工作;很多加载 3D 模型时的常见错误都是因为没有正确托管文件。另外,如果用 CDN,所有依赖必须来自同一版本的 three.js 和同一个 CDN,混用不同来源可能导致代码重复加载甚至应用损坏。

导入 GLTFLoader

three.js 默认只内置了少数几个加载器(如ObjectLoader),其余组件——包括GLTFLoader——属于 addons,不需要单独安装,但必须显式导入(见 GLTFLoader API 文档):

import * as THREE from 'three'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

在 Vite 项目中three/addons/由构建工具解析;在 CDN 方式中,它由上面的 import map 映射到examples/jsm/目录。

加载模型并加入场景

GLTFLoader.load( url, onLoad, onProgress, onError )从给定 URL 开始加载,完成后把结果传给onLoad回调。官方 Loading 3D Models 给出的基本用法:

const loader = new GLTFLoader(); loader.load( 'path/to/model.glb', function ( gltf ) { scene.add( gltf.scene ); }, undefined, function ( error ) { console.error( error ); } );

其中'path/to/model.glb'是文档中的占位路径,必须替换成你的模型的实际路径或 URL——url参数也支持 data URI。想拿现成模型做验证时,仓库里的 duck.glb 可以直接复制到你的public/目录作为测试模型。

onLoad收到的gltf对象(API 文档中的LoadObject)包含这些字段:

  • gltf.sceneGroup类型,默认场景,加入场景的就是它
  • gltf.scenesGroup数组,glTF 资产可能定义多个场景;
  • gltf.animationsAnimationClip数组,模型里的动画剪辑;
  • gltf.asset:资产元数据;
  • gltf.userData:附加数据。

加载完成后还需要渲染循环。下面是与手册示例 load-gltf 一致的最小可运行结构(main.js):

import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; const renderer = new THREE.WebGLRenderer( { antialias: true } ); document.body.appendChild( renderer.domElement ); const camera = new THREE.PerspectiveCamera( 45, 2, 0.1, 100 ); camera.position.set( 0, 10, 20 ); const controls = new OrbitControls( camera, renderer.domElement ); controls.target.set( 0, 5, 0 ); controls.update(); const scene = new THREE.Scene(); scene.background = new THREE.Color( 'black' ); // 场景需要光源,否则模型可能不可见 const hemiLight = new THREE.HemisphereLight( 0xB1E1FF, 0xB97A20, 2 ); scene.add( hemiLight ); const dirLight = new THREE.DirectionalLight( 0xFFFFFF, 2.5 ); dirLight.position.set( 5, 10, 2 ); scene.add( dirLight ); scene.add( dirLight.target ); // 模型 URL:替换成你的实际路径,例如放在 public/ 下的 duck.glb const MODEL_URL = 'path/to/model.glb'; { const gltfLoader = new GLTFLoader(); gltfLoader.load( MODEL_URL, ( gltf ) => { const root = gltf.scene; scene.add( root ); // 计算包围盒并自动把相机框住模型,见下文「验证结果」 const box = new THREE.Box3().setFromObject( root ); const boxSize = box.getSize( new THREE.Vector3() ).length(); const boxCenter = box.getCenter( new THREE.Vector3() ); frameArea( boxSize * 0.5, boxSize, boxCenter, camera ); controls.maxDistance = boxSize * 10; controls.target.copy( boxCenter ); controls.update(); }, undefined, ( error ) => { console.error( error ); } ); } function frameArea( sizeToFitOnScreen, boxSize, boxCenter, camera ) { const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5; const halfFovY = THREE.MathUtils.degToRad( camera.fov * .5 ); const distance = halfSizeToFitOnScreen / Math.tan( halfFovY ); const direction = ( new THREE.Vector3() ) .subVectors( camera.position, boxCenter ) .multiply( new THREE.Vector3( 1, 0, 1 ) ) .normalize(); camera.position.copy( direction.multiplyScalar( distance ).add( boxCenter ) ); camera.near = boxSize / 100; camera.far = boxSize * 100; camera.updateProjectionMatrix(); camera.lookAt( boxCenter.x, boxCenter.y, boxCenter.z ); } function resizeRendererToDisplaySize( renderer ) { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; const needResize = canvas.width !== width || canvas.height !== height; if ( needResize ) { renderer.setSize( width, height, false ); } return needResize; } function render() { if ( resizeRendererToDisplaySize( renderer ) ) { camera.aspect = renderer.domElement.clientWidth / renderer.domElement.clientHeight; camera.updateProjectionMatrix(); } renderer.render( scene, camera ); requestAnimationFrame( render ); } requestAnimationFrame( render );

这段代码里,相机自动取景(frameArea与包围盒计算)和渲染循环都取自 manual 的 GLTF 示例页面。它的作用是在模型加载后根据包围盒调整相机距离与 near/far,避免模型太小或相机位于模型内部而看不到内容——这正是官方排查清单里提到的一个常见现象。OrbitControls也是 addon,导入方式与GLTFLoader相同。

验证结果

  1. 浏览器里能看到模型:打开 Vite(或 serve)给出的本地地址,模型连同纹理一起渲染出来。manual 教程 Loading a .GLTF File 演示的就是这条流程——替换掉 OBJ 加载代码、导入GLTFLoaderscene.add( gltf.scene )后,"textures and all" 直接出画面。
  2. 检查加载出的场景结构:manual 教程在onLoad回调里调用打印函数输出场景图(scenegraph),确认节点是否按预期组织,例如定位某个命名节点:
gltfLoader.load( MODEL_URL, ( gltf ) => { const root = gltf.scene; scene.add( root ); console.log( dumpObject( root ).join( '\n' ) ); } );

dumpObject是 manual 教程给出的完整工具函数(递归打印每个节点的名称、类型与层级,形如RootNode [Object3D]/SomeMesh [Mesh]),可原样取自 manual/pages/load-gltf.html。拿到场景图后,就可以用root.getObjectByName( '节点名' )找出后续要操作的子对象(教程用它找到了名为Cars的节点)。

模型不显示、变形或颜色不对时怎么排查

官方 Loading 3D Models 给出了五条排查步骤,按顺序执行:

  1. 查控制台报错:检查 JavaScript 控制台,并确保调用.load()时传了onError回调来记录错误(上文代码已包含)。
  2. 在别的应用里打开模型:先用其他支持 glTF 的查看器打开模型。如果模型在别的软件里显示正常,向 three.js 提交 bug;如果在任何软件里都显示不出来,向制作模型的工具提交 bug。
  3. 把模型放大或缩小 1000 倍试试:很多模型的缩放各不相同,相机位于大模型内部时模型不会显示。
  4. 添加并摆放一个光源:模型可能只是"藏在黑暗里"。
  5. 在网络面板里找失败的纹理请求:比如出现"C:\Path\To\Model\texture.jpg"这种绝对路径。应改用相对于模型的路径,如images/texture.jpg——这可能需要用文本编辑器修改模型文件。.gltf本质是 JSON 文件,可以直接打开查看内容;模型引用了绝对路径纹理时,纹理请求就会失败。

可选分支:加载 Draco 压缩的模型

如果模型用KHR_draco_mesh_compression扩展压缩过网格数据,必须给GLTFLoader设置DRACOLoadersetDRACOLoader方法即为此用途)。GLTFLoader API 文档 的示例:

const loader = new GLTFLoader(); // Optional: Provide a DRACOLoader instance to decode compressed mesh data const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath( '/examples/jsm/libs/draco/' ); loader.setDRACOLoader( dracoLoader ); const gltf = await loader.loadAsync( 'models/gltf/duck/duck.gltf' ); scene.add( gltf.scene );

注意示例中的setDecoderPath路径与模型 URL 对应的是 three.js 仓库自身的目录结构:解码器文件(draco_decoder.jsdraco_decoder.wasm等)位于仓库的examples/jsm/libs/draco/目录,模型 URL 需替换为你自己的资产。在自己的项目中,把解码器文件放到可访问的目录后,将setDecoderPath指向该目录即可。另外两点来自 DRACOLoader 文档:建议创建并复用同一个DRACOLoader实例,避免加载多个解码器;DRACOLoader会根据浏览器能力自动选择 JS 或 WASM 解码库。

同理,KTX2 压缩纹理需要setKTX2LoaderEXT_meshopt_compression压缩的资产需要setMeshoptDecoder,均见 GLTFLoader API 文档 的方法说明。

限制与注意事项

  • GLTFLoader支持一长串 glTF 2.0 扩展(KHR_draco_mesh_compressionKHR_materials_transmissionKHR_mesh_quantizationEXT_texture_webp等),完整列表以 API 文档为准;部分扩展(如KHR_gaussian_splattingMSFT_texture_dds)需要另行注册插件才能支持。
  • glTF 中的位图纹理不会在失去引用后被浏览器自动垃圾回收,需要在销毁(disposal)流程中做特殊处理——模型会频繁切换的应用要留意这一点。
  • 手动教程在实践里还提醒了两点边界:glTF 无法覆盖所有情况(例如.blend里的灯光加载进来后可能不再是灯光);用于实时 3D 的资产,其节点的原点位置和缩放最好在导出前就整理好,运行时再修会很麻烦。这些经验来自 Loading a .GLTF File 的完整过程。

模型加载成功且gltf.animations非空时,数组里的AnimationClip可以直接接入 three.js 的动画系统(AnimationMixer/AnimationAction,见 docs/pages 对应文档),这就是加载完成后自然的下一步。

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 22:30:27

微信小程序开发智能停车系统实战

1. 项目概述&#xff1a;地下停车场智能化的破局点每次开车进商场地下车库都要兜圈子找车位&#xff0c;这种体验实在太糟糕了。去年帮本地商业综合体做智慧化改造时&#xff0c;我们团队用微信小程序开发了一套车位预约系统&#xff0c;上线后车位周转率直接提升了40%。这套系…

作者头像 李华
网站建设 2026/9/10 22:27:57

留个神!不是每款 AI 都能用来写学术论文,2026 高校认可工具精选

每年毕业季&#xff0c;无数同学深陷论文难题&#xff1a;开题毫无思路、搭建框架耗费数日、初稿逻辑松散、查重标红泛滥、AI检测超标、格式反复被导师驳回。面对繁重的写作任务&#xff0c;许多学生开始依赖通用型AI工具&#xff0c;但市面上大多数AI平台存在严重短板。它们往…

作者头像 李华
网站建设 2026/9/10 22:27:36

SpringBoot打印店预约系统:技术实现与优化

1. 项目背景与核心价值打印店作为高校和办公区的高频服务场所&#xff0c;传统的人工登记模式存在三大痛点&#xff1a;高峰期排队耗时、订单状态不透明、文件安全管理薄弱。这套基于SpringBoot的预约取件系统&#xff0c;正是为解决这些实际问题而设计的轻量级解决方案。我在实…

作者头像 李华
网站建设 2026/9/10 22:27:22

Python与Milvus构建高效向量搜索系统指南

1. 为什么选择PythonMilvus这个技术组合&#xff1f;Milvus作为一款开源的向量数据库&#xff0c;在处理非结构化数据时展现出独特优势。而Python凭借其简洁语法和丰富生态&#xff0c;成为AI领域事实上的标准语言。这两者的结合&#xff0c;为开发者提供了从数据预处理到向量存…

作者头像 李华