Three加载3D模型贴图
Three加载3D模型贴图
准备阶段
- 3D模型
- three 库文件
- 纹理图片
相关资料
- 官方开发文档: https://threejs.org/docs
- 官网编辑3D模型:https://threejs.org/editor/ 可以在这里创建一个3D模型导出
gltf
或obj
都可以 - 纹理图片:https://3dtextures.me/ 、https://ambientcg.com/view?id=Bricks075A
- 官网示例代码:https://github.com/mrdoob/three.js/
- 学习 three.js 代码(第四版):https://github.com/PacktPublishing/Learn-Three.js-Fourth-edition
- web 浮动面板控制器:https://lil-gui.georgealways.com/#
效果图
核心代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>房屋平面室内图</title> </head> <body> </body> <script type="importmap"> { "imports": { "three":"/js/three.module.js", "three/addons/": "/examples/jsm/" } } </script> <script type="module"> import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.18/+esm'; import * as THREE from 'three'; // 加载 gltf 模型 import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; // 加载 obj 模型 import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'; // 轨道控制器 import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // 创建场景 const scene = new THREE.Scene(); // 创建透视相机 const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000) camera.position.set(0, 20, 200) camera.scale.x = 1 camera.scale.y = 1 camera.scale.z = 1 // camera.zoom=0.5 // 更新相机 camera.updateProjectionMatrix() // 创建渲染器,透明背景 const renderer = new THREE.WebGLRenderer({ alpha: true }); // 场景灯光 const directionalLight = new THREE.DirectionalLight() directionalLight.penumbra = 0.4 directionalLight.position.set(10, 14, 5) directionalLight.distance = 0 directionalLight.castShadow = true directionalLight.intensity = 1 directionalLight.shadow.camera.near = 1 directionalLight.shadow.camera.far = 25 directionalLight.shadow.camera.right = 10 directionalLight.shadow.camera.left = -10 directionalLight.shadow.camera.top = 10 directionalLight.shadow.camera.bottom = -10 directionalLight.shadow.mapSize.width = 2048 directionalLight.shadow.mapSize.height = 2048 directionalLight.shadow.bias = -0.01 scene.add(directionalLight); // 纹理可以加载。TextureLoader const textureLoader = new THREE.TextureLoader(); textureLoader.load('/assets/wood/floor-parquet-pattern-172292.jpg', (loaded) => { loaded.encoding = THREE.sRGBEncoding scene.background = loaded scene.environment = null }) renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const gui = new GUI(); const obj = { left: '/model/textures/12.jpg', right: '/model/textures/12.jpg', bottom: '/model/textures/9.jpg', back: '/model/textures/13.jpg', bed: '/model/textures/5.jpg', table: '/model/textures/1.jpg', } const mm = { 'material1': '/model/textures/1.jpg', 'material2': '/model/textures/2.png', 'material3': '/model/textures/3.jpg', 'material4': '/model/textures/4.png', 'material5': '/model/textures/5.jpg', 'material6': '/model/textures/6.jpg', 'material7': '/model/textures/7.jpg', 'material8': '/model/textures/8.jpg', 'material9': '/model/textures/9.jpg', 'material10': '/model/textures/10.jpg', 'material11': '/model/textures/11.jpg', 'material12': '/model/textures/12.jpg', 'material13': '/model/textures/13.jpg', 'material14': '/model/textures/14.jpg', } const folder = gui.addFolder( 'textures' ); folder.add(obj, 'left', mm) folder.add(obj, 'right', mm) folder.add(obj, 'bottom', mm) folder.add(obj, 'back', mm) folder.add(obj, 'bed', mm) folder.add(obj, 'table', mm) folder.onChange(event => { console.log(event) const key = event.property; const value = event.value; const mesh = scene.getObjectByName(key); if (mesh) { const material = new THREE.MeshBasicMaterial({ map: textureLoader.load(value) }); mesh.material = material; mesh.castShadow=true; } }); const backgroundColor = gui.addFolder( 'scene' ); backgroundColor.add({'backGround':'White'},'backGround', ['White', 'Black', 'Null', 'Color', 'Texture','自定义纹理', 'Cubemap']) .onChange((event) => handleBackgroundChange(event, scene)) const handleBackgroundChange = (setting, scene) => { switch (setting) { case 'White': scene.background = new THREE.Color(0xffffff) break case 'Black': scene.background = new THREE.Color(0x000000) break case 'Null': scene.background = null break case 'Color': scene.background = new THREE.Color(0x44ff44) break case 'Texture': textureLoader.load('/assets/wood/abstract-antique-backdrop-164005.jpg', (loaded) => { loaded.encoding = THREE.sRGBEncoding scene.background = loaded scene.environment = null }) break case '自定义纹理': textureLoader.load('/assets/wood/floor-parquet-pattern-172292.jpg', (loaded) => { loaded.encoding = THREE.sRGBEncoding scene.background = loaded scene.environment = null }) break case 'Cubemap': textureLoader.load('/assets/equi.jpeg', (loaded) => { loaded.mapping = THREE.EquirectangularReflectionMapping scene.background = loaded scene.environment = loaded }) break default: break } } const loader = new GLTFLoader(); // 滑轨控制 const controller = new OrbitControls(camera, renderer.domElement) controller.enableDamping = true controller.dampingFactor = 0.05 controller.minDistance = 1 controller.maxDistance = 100 controller.minPolarAngle = 0 controller.maxPolarAngle = Math.PI loader.load( '/model/plan_room.gltf', function(gltf) { gltf.scene.traverse(function(child) { if (child instanceof THREE.Mesh) { // 加载模型时,添加对于的纹理贴图 for (let key in obj) { if (child.name === key) { const material = new THREE.MeshBasicMaterial({ map: textureLoader.load(obj[key]) }); child.material = material; continue; } } } }) gltf.scene.scale.x = 0.5 gltf.scene.scale.y = 0.5 gltf.scene.scale.z = 0.5 scene.add(gltf.scene); }, function(xhr) { console.log((xhr.loaded / xhr.total * 100) + '% loaded'); }, function(error) { console.error(error); } ); function animate() { requestAnimationFrame(animate); controller.update(); renderer.render(scene, camera); } animate(); </script> </html>
源码传送
下载:https://864000.lanzouy.com/ipX7D0woxeqf 密码:3bi0
根目录执行
http-server -p9001
浏览器访问http://127.0.0.1:9001
C:\Users\mac\OneDrive\three-1>http-server -p9001 Starting up http-server, serving ./ http-server version: 14.1.1 http-server settings: CORS: disabled Cache: 3600 seconds Connection Timeout: 120 seconds Directory Listings: visible AutoIndex: visible Serve GZIP Files: false Serve Brotli Files: false Default File Extension: none Available on: http://192.168.31.156:9001 http://127.0.0.1:9001
哇!又赚了一天人民币
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库