Three.js 快速上手以及在 React 中運用[轉]


https://juejin.im/post/5ca22692f265da30a53d6656

github 的地址 歡迎 star!

之前項目中用到了 3D 模型演示的問題,整理了一下之前學習總結以及遇到的坑。3D 框架有老牌引擎 Three.js 和微軟的 Babylon.js

image 對比一下還是使用更為普遍的 Three.js

 

Three.js基礎概念

主要來自於《Three.js 開發指南》也可以參考在線網站 threejs 教程

3個基礎概念:場景(scene)、相機(camera)和渲染器(renderer)。

  • Sence 場景:場景是一個載體,容器,所有的一切都運行在這個容器里面(存放着所有渲染的物體和使用的光源)

  • 相機 camera 的作用是定義可視域,相當於我們的雙眼,生產一個個快照,最為常用的是 PerspectiveCamera 透視攝像機,其他還有 ArrayCamera 陣列攝像機(包含多個子攝像機,通過這一組子攝像機渲染出實際效果,適用於 VR 場景),CubeCamera 立方攝像機(創建六個 PerspectiveCamera(透視攝像機),適用於鏡面場景),StereoCamera 立體相機(雙透視攝像機適用於 3D 影片、視差效果)。相機主要分為兩類正投影相機和透視相機,正投影相機的話, 所有方塊渲染出來的尺寸都一樣; 對象和相機之間的距離不會影響渲染結果,而透視相機接近真實世界,看物體會產生遠近高低各不同

  • PerspectiveCamera 透視攝像機--模擬人眼的視覺,根據物體距離攝像機的距離,近大遠小

 

image

 

  • 渲染器 renderer 則負責用如何渲染出圖像,是使用 WegGL 還是 Canvas,類似於 react 中 render,產生實際的頁面效果

其他一些概念

  • Mesh 網格:有了場景和攝像頭就可以看到 3D 場景中的物體,場景中的我們最為常用的物體稱為網格。網格由兩部分組成:幾何體和材質
  • 材料(Materials),紋理( Textures):物體的表面屬性可以是單純的顏色,也可以是很復雜的情況,比如反射/透射/折射的情況,還可以有紋理圖案。比如包裝盒外面的貼圖。
  • Geometry 幾何形狀:threejs 使用 Geometry 定義物體的幾何形狀,其實 Geometry 的核心就是點集,之所以有這么多的 Geometry,是為了更方便的創建各種形狀的點集
  • 光照(Lights):組成部分。 如果 沒有 光源, 我們 就不 可能 看到 任何 渲染 結果,具體介紹可以查看光照效果和Phong光照模型。一些常用的光源:
    1. AmbientLight 環境光源,屬於基礎光源,為場景中的所有物體提供一個基礎亮度。
    2. DirectionalLight 平行光源:類似太陽光,發出的光源都是平行的
    3. HemisphereLight 半球光源:只有圓球的半邊會發出光源。
    4. PointLight 點光源:一個點向四周發出光源,一般用於燈泡。
    5. SpotLight 聚光燈光源:一個圓錐體的燈光
  • 注意:並不是每一種光源都能產生陰影(Shadow): DirectionalLight, PointLight, SpotLight 三種能產生陰影,另外如要開啟模型的陰影的話,模型是由多個 Mesh 組成的,只開啟父的 Mesh 的陰影是不行的,還需要遍歷父 Mesh 下所有的子 Mesh 為其開啟投射陰影 castShadow 和接收投射陰影 receiveShadow。
  • 加載器(Loaders):用來解析的導入的模型文件,常見的有 OBJLoader(加載 .obj 文件)、JSONLoader,MTLLoader
// 場景是所有物體的容器
var scene = new THREE.Scene();
// 相機,相機決定了場景中那個角度的景色會顯示出來。相機就像人的眼睛一樣,人站在不同位置,抬頭或者低頭都能夠看到不同的景色。
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
// 渲染器renderer的domElement元素,表示渲染器中的畫布,所有的渲染都是畫在domElement上的
var renderer = new THREE.WebGLRenderer();	// 渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 設置渲染器的大小為窗口的內寬度,也就是內容區的寬度
document.body.appendChild(renderer.domElement);


// 渲染循環
function animate() { render(); // 調用 requestAnimationFrame 函數,傳遞一個 callback 參數,則在下一個動畫幀時,會調用 callback 這個函數。 requestAnimationFrame( animate ); } 動畫方案: 一:改變camera function animation() { //renderer.clear(); camera.position.x =camera.position.x +1; renderer.render(scene, camera); requestAnimationFrame(animation); } // camera.position.x =camera.position.x +1; // 將相機不斷的沿着x軸移動1個單位,也就是相機向右移動,那么相機中物體是向左移動的。 // 調用requestAnimationFrame(animation)函數,這個函數又會在下一個動畫幀出發animation()函數,這樣就不斷改變了相機的位置,從而物體看上去在移動了。 // 另外,必須要重視render函數,這個函數是重新繪制渲染結果,如果不調用這個函數,那么即使相機的位置變化了,但是沒有重新繪制,仍然顯示的是上一幀的動畫 renderer.render(scene, camera); 二:改變物體自身位置--mesh mesh就是指的物體,它有一個位置屬性position,這個position是一個THREE.Vector3類型變量,所以你要把它向左移動,只需要將x的值不斷的減少就可以了。這里我們減去的是1個單位。 // [渲染真實性---光源運用](http://www.hewebgl.com/article/getarticle/60) THREE.Light ( hex ) 它有一個參數hex,接受一個16進制的顏色值。例如要定義一種紅色的光源,我們可以這樣來定義: Var redLight = new THREE.Light(0xFF0000); // [文理--3D物體的皮膚:](http://www.hewebgl.com/article/getarticle/68) 紋理類由THREE.Texture表示,其構造函數如下所示: THREE.Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) 復制代碼

一下就是 Three.js 的基本概念

image

 

然后給出一個簡單的例子

// 引入 Three.js 庫
<script src="https://unpkg.com/three"></script> function init () { // 獲取瀏覽器窗口的寬高,后續會用 var width = window.innerWidth var height = window.innerHeight // 創建一個場景 var scene = new THREE.Scene() // 創建一個具有透視效果的攝像機 var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 800) // 設置攝像機位置,並將其朝向場景中心 camera.position.x = 10 camera.position.y = 10 camera.position.z = 30 camera.lookAt(scene.position) // 創建一個 WebGL 渲染器,Three.js 還提供 <canvas>, <svg>, CSS3D 渲染器。 var renderer = new THREE.WebGLRenderer() // 設置渲染器的清除顏色(即背景色)和尺寸。 // 若想用 body 作為背景,則可以不設置 clearColor,然后在創建渲染器時設置 alpha: true,即 new THREE.WebGLRenderer({ alpha: true }) renderer.setClearColor(0xffffff) renderer.setSize(width, height) // 創建一個長寬高均為 4 個單位長度的立方體(幾何體) var cubeGeometry = new THREE.BoxGeometry(4, 4, 4) // 創建材質(該材質不受光源影響) var cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }) // 創建一個立方體網格(mesh):將材質包裹在幾何體上 var cube = new THREE.Mesh(cubeGeometry, cubeMaterial) // 設置網格的位置 cube.position.x = 0 cube.position.y = -2 cube.position.z = 0 // 將立方體網格加入到場景中 scene.add(cube) // 將渲染器的輸出(此處是 canvas 元素)插入到 body 中 document.body.appendChild(renderer.domElement) // 渲染,即攝像機拍下此刻的場景 renderer.render(scene, camera) } init() 復制代碼

在線的例子點擊

實際運用

three 性能監視器 stats

主要是用來顯示性能幀數的

  • FPS:最后一秒的幀數,越大越流暢

  • MS:渲染一幀需要的時間(毫秒),越低越好

  • MB:占用的內存信息

  • CUSTOM:自定義面板

    image

     

var stats = new Stats()
stats.showPanel(1)
document.body.appendChild(stats.dom)
function animate() { requestAnimationFrame(animate) } requestAnimationFrame(animate) 復制代碼

具體一些不用導入的例子

可以在 github.com/mrdoob/thre… 下載文件,查看\three.js-master\examples中例子熟悉相應的代碼

導入 3D 模型例子

導入模型類型介紹

導入模型文件需要用到相應的 loader,常用 3d 軟件導出的格式,項目中主要是用了 OBJ 和 MTL 類型,OBJ 定義了幾何體,MTL 定義了材質

//當mtl中引用了dds類型的圖片時,還需導入DDSLoader文件。
//這里的src路徑視實際開發而定
<script src="js/loaders/DDSLoader.js"></script> <script src="js/loaders/MTLLoader.js"></script> <script src="js/loaders/OBJLoader.js"></script> THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() ); var mtlLoader = new THREE.MTLLoader(); //設置路徑,也可不是設置,在load中加載完整路徑也可 mtlLoader.setPath( 'obj/male02/' ); mtlLoader.load( 'male02_dds.mtl', // 資源加載成功后執行的函數 //@params materials THREE.MTLLoader.MaterialCreator function( materials ) { materials.preload(); var objLoader = new THREE.OBJLoader(); objLoader.setMaterials( materials ); objLoader.setPath( 'obj/male02/' ); objLoader.load( 'male02.obj', function ( object ) { object.position.y = - 95; scene.add( object ); }); }); 復制代碼

具體例子可以查看

導入 OBJ 存在問題,模型導出為 obj 格式后,文件太大(放在服務器就會產生嚴重的性能問題),而且還需要額外對 MTL 導入,不然只會顯示幾何模型
glTF 模型格式

.obj 是靜態模型,不支持動畫數據存儲,無法使用模型的動畫,而且體積大, glTF 是由 Khronos Group 開發的 3D 模型文件格式,該格式的特點是最大程度的減少了 3D 模型文件的大小,提高了傳輸、加載以及解析 3D 模型文件的效率,並且它可擴展,可互操作。

.gltf 包含場景中節點層次結構、攝像機、網格、材質以及動畫等描述信息

Three.js 中使用 glTF 格式需額外引入 GLTFLoader.js 加載器。

var gltfLoader = new THREE.gltfLoader()
gltfLoader.load('./assets/box.gltf', function(sence) { var object = scene.gltf // 模型對象 scene.add(object) // 將模型添加到場景中 }) 復制代碼

glTF 模型中可以使用 Blender 建模軟件制作動畫,導出后使用 GLTFLoader 加載到 Three.js 中,可以拿到一個 animations 數組,animations 里包含了模型的每個動畫 Action 動作。

為了獲取更好的網絡性能,還可以使用 Draco工具進行壓縮,只有在模型文件很多時,才推薦壓縮(因為壓縮后格式改變,需要引入其他的解析工具)

動畫

上面說到了動畫,關於動畫,可以直接三方庫 Tween 動畫,在許同事提供的研究里面有相關的運用。一般在 Three.js 動畫是使用 requestAnimationFrame(),當你需要更新屏幕畫面時就可以調用此方法。在瀏覽器下次重繪前執行回調函數。回調的次數通常是每秒60次。

對模型實現淡入淡出、縮放、位移、旋轉等動畫推薦使用 GSAP 來實現更為簡便。

let tween = new TimelineMax() tween .to(box.scale, 1, { // 從 1 縮放至 2,花費 1 秒 x: 2, y: 2, z: 2, ease: Power0.easeInOut, // 速度曲線 onStart: function() { // 監聽動畫開始 }, onUpdate: function() { // 監聽動畫過程 }, onComplete: function() { // 監聽動畫結束 } }) .to(box.position, 1, { // 縮放結束后,位移 x 至 10,花費 1 秒 x: 10, y: 0, z: 0 }) 復制代碼

控制 orbitcontrols

場景控制器,OrbitControls 是用於調試 Camera 的方法,實例化后可以通過鼠標拖拽來旋轉 Camera 鏡頭的角度,鼠標滾輪可以控制 Camera 鏡頭的遠近距離,旋轉和遠近都會基於場景的中心點,在調試預覽則會輕松許多。

// 引入文件
<script src="js/OrbitControls.js"></script> //場景控制器初始化 function initControls() { controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enabled = true; // 鼠標控制是否可用 // 是否自動旋轉 controls.autoRotate = true; controls.autoRotateSpeed = 0.05; //是否可旋轉,旋轉速度(鼠標左鍵) controls.enableRotate = true; controls.rotateSpeed = 0.3; //controls.target = new THREE.Vector();//攝像機聚焦到某一個點 //最大最小相機移動距離(景深相機) controls.minDistance = 10; controls.maxDistance = 40; //最大仰視角和俯視角 controls.minPolarAngle = Math.PI / 4; // 45度視角 controls.maxPolarAngle = Math.PI / 2.4; // 75度視角 //慣性滑動,滑動大小默認0.25 controls.enableDamping = true; controls.dampingFactor = 0.25; //是否可平移,默認移動速度為7px controls.enablePan = true; controls.panSpeed = 0.5; //controls.screenSpacePanning = true; //滾輪縮放控制 controls.enableZoom = true; controls.zoomSpeed = 1.5; //水平方向視角限制 //controls.minAzimuthAngle = -Math.PI/4; //controls.maxAzimuthAngle = Math.PI/4; } 復制代碼

點擊交互

在3D模型中,鼠標點擊是重要的交互。對於 Three.js,它沒有類似 DOM 的層級關系,並且處於三維環境中,那么我們則需要通過以下方式來判斷某對象是否被選中。

function onDocumentMouseDown(event) { // 點擊位置創建一個 THREE.Vector3 向量 var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5); // vector.unproject 方法將屏幕上的點擊位置轉換成 Three.js 場景中的坐標 vector = vector.unproject(camera); // 使用 THREE.Raycaster 可以向場景中發射光線 var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize()); // 使用 raycaster.intersectObjects 方法來判斷指定的對象中哪些被該光線照射到的, // 從而顯示不同的顏色 var intersects = raycaster.intersectObjects([sphere, cylinder, cube]); if (intersects.length > 0) { console.log(intersects[0]); // 點擊后改變透明度 intersects[0].object.material.transparent = true; intersects[0].object.material.opacity = 0.1; <!--...... 在這里可以實現你所需要的交互--> } } 復制代碼

react中實踐運用

// 引入相關的依賴
npm i -S three


<!--GisThree.js-->
<!--當然 這個代碼還有很大的優化空間啊!-->

import React, { Component, Fragment } from 'react'; import './GisThree.less'; import OBJLoader from './threejsLibs/OBJLoader'; import Orbitcontrols from './threejsLibs/OrbitControls'; import MTLLoader from './threejsLibs/MTLLoader_module'; import { Icon } from 'antd'; import exhibitObj from './modal/exhibit2.obj'; import exhibitMtl from './modal/exhibit2.mtl'; let THREE = require('three'); Orbitcontrols(THREE); OBJLoader(THREE); MTLLoader(THREE); // 排除這些名字的3D模型 const objectArrName = [ "房屋1101", "房屋1150", "房屋600", "房屋70", "房屋45", "房屋362", "房屋363", "房屋364", "房屋500" ]; class GisThree extends Component { constructor( props ) { super(props); this.state = { isModel: false, currentName: '暫無名字', clientX: 0, clientY: 0 }; this.threeRef = React.createRef(); } componentDidMount() { const width = window.innerWidth; const height = window.innerHeight; // todo 初始化場景 const scene = new THREE.Scene(); // todo 加載相機 const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 80); camera.position.set(0, 25, 25); camera.lookAt(new THREE.Vector3(0, 0, 0)); //todo 加載光線 const ambLight = new THREE.AmbientLight(0x404040, 0.5); const pointLight = new THREE.PointLight(0x404040, 0.8); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); pointLight.position.set(100, 10, 0); pointLight.receiveShadow = true; scene.add(ambLight); scene.add(pointLight); scene.add(directionalLight); //todo renderer const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(width, height - 10); //renderer.setClearColor(0xb9d3ff,1); renderer.setClearColor(0x000000, 1.0); //todo 加載模型model let mtlLoader = new THREE.MTLLoader(); mtlLoader.load(exhibitMtl, function ( materials ) { console.log('sdj exhibit.obj', materials) materials.preload(); let objLoader = new THREE.OBJLoader(); objLoader.setMaterials(materials); objLoader.load(exhibitObj, function ( object ) { console.log('sdj exhibit.obj') console.log('sdj exhibit.obj object', object); for ( let i = 0; i < object.children.length; i++ ) { let material = object.children[ i ].material; let meshObj = new THREE.Mesh(object.children[ i ].geometry, material); meshObj.receiveShadow = true; meshObj.castShadow = true; meshObj.scale.set(0.02, 0.02, 0.02); meshObj.name = "房屋" + i; meshObj.position.x = 0; meshObj.position.y = 0; meshObj.position.z = -20; scene.add(meshObj); } }); } ); // todo 場景控制器初始化 const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enabled = true; // 鼠標控制是否可用 // 是否自動旋轉 controls.autoRotate = true; controls.autoRotateSpeed = 0.05; //是否可旋轉,旋轉速度(鼠標左鍵) controls.enableRotate = true; controls.rotateSpeed = 0.3; //controls.target = new THREE.Vector();//攝像機聚焦到某一個點 //最大最小相機移動距離(景深相機) controls.minDistance = 10; controls.maxDistance = 40; //最大仰視角和俯視角 controls.minPolarAngle = Math.PI / 4; // 45度視角 controls.maxPolarAngle = Math.PI / 2.4; // 75度視角 //慣性滑動,滑動大小默認0.25 controls.enableDamping = true; controls.dampingFactor = 0.25; //是否可平移,默認移動速度為7px controls.enablePan = true; controls.panSpeed = 0.5; //controls.screenSpacePanning = true; //滾輪縮放控制 controls.enableZoom = true; controls.zoomSpeed = 1.5; //水平方向視角限制 //controls.minAzimuthAngle = -Math.PI/4; //controls.maxAzimuthAngle = Math.PI/4; //todo 綁定到類上 this.scene = scene; this.camera = camera; this.renderer = renderer; this.controls = controls; //鼠標移入和移出事件高亮顯示選中的模型 this.currentObjectColor = null; //移入模型的顏色 this.currentObject = null; //鼠標移入的模型 // 初始化場景 // 加載到dom元素上 this.threeRef.current.appendChild(this.renderer.domElement) this.start(); window.addEventListener('resize',this.resizeFunc1 ,false); window.addEventListener('resize',this.resizeFunc2 ,false); } componentWillUnmount() { this.stop(); this.threeRef.current.removeChild(this.renderer.domElement); window.removeEventListener('resize',this.resizeFunc1 ,false); window.removeEventListener('resize',this.resizeFunc2 ,false); } // 初始化 start = () => { if(!this.frameId){ this.frameId = requestAnimationFrame(this.animate) } } // 卸載組件的時候去除 stop = () => { cancelAnimationFrame(this.frameId); } // 更新狀態 animate = () => { this.controls.update(); this.renderScene(); this.frameId = requestAnimationFrame(this.animate); } renderScene = () => { this.renderer.render(this.scene, this.camera); } // 是否展示彈窗 changeModel = ( e ) => { e.stopPropagation(); this.setState({ isModel: !this.state.isModel }) } closeModel = ( e ) => { e.stopPropagation(); if (this.controls && !this.controls.autoRotate){ this.controls.autoRotate = true; } this.setState({ isModel: false }) } // 點擊3D模型匹配 mouseClick = (e) => { // 鼠標坐標映射到三維坐標 e.preventDefault(); const that = this; const mouse = new THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; if(!this.camera || !this.scene) return; let vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(this.camera); let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize()); let intersects = raycaster.intersectObjects(this.scene.children, true); //選中的三維模型 console.log('sdj position',intersects) if (intersects.length > 0) { let SELECTED = intersects[0]; let currentName = SELECTED.object.name; console.log('sdj position', e.clientX, e.clientY, e.screenX, e.screenY); if (objectArrName.indexOf(currentName) == -1) { if (this.controls.autoRotate){ this.controls.autoRotate = false; } that.changeModel(e); that.setState({ currentName, clientX: e.clientX, clientY: (e.clientY - 60) }) console.log("你選中的物體的名字是:" + currentName); } } } // 鼠標聚焦 mouseenterObject = (e) => { // 鼠標坐標映射到三維坐標 e.preventDefault(); let mouse = new THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; let vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(this.camera); let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize()); let intersects = raycaster.intersectObjects(this.scene.children, true); //選中的三維模型 if (!intersects.length && this.currentObjectColor && this.currentObject) { //從模型處移到外面 this.currentObject.object.material.color.setHex(this.currentObjectColor); this.currentObjectColor = null; this.currentObject = null; } if (intersects.length > 0) { let SELECTED = intersects[0]; let currentName = SELECTED.object.name; if (objectArrName.indexOf(currentName) == -1) { if (this.currentObject && currentName === this.currentObject.object.name) { return; } if (this.currentObjectColor && this.currentObject && currentName !== this.currentObject.object.name) { //color值是一個對象 this.currentObject.object.material.color.setHex(this.currentObjectColor); } this.currentObject = SELECTED; this.currentObjectColor = SELECTED.object.material.color.getHex(); SELECTED.object.material.color.set(0x74bec1); } else { if (this.currentObjectColor && this.currentObject && currentName !== this.currentObject.object.name) { //color值是一個對象 this.currentObject.object.material.color.setHex(this.currentObjectColor); } this.currentObjectColor = null; this.currentObject = null; } } } resizeFunc1 = () => { this.controls.update(); } resizeFunc2 = (e) => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); } render() { return ( <Fragment> <div className={ this.props.className || 'three-component' } id="d3" ref={ this.threeRef } onClick={this.mouseClick} onMouseMove={this.mouseenterObject} /> { this.state.isModel && ( <div className="three-modal" style={ { top: this.state.clientY, left: this.state.clientX } } > <Icon className="three-modal-close" type="close" theme="outlined" onClick={ this.closeModel } /> <ul> <li> <span className="modal-title">出租屋編碼</span> <span className="modal-data">{ this.state.currentName }</span> </li> <li> <span className="modal-title">地址</span> <span className="modal-data">社區一號</span> </li> <li> <span className="modal-title">每層樓棟數</span> <span className="modal-data">6</span> </li> <li> <span className="modal-title">層數</span> <span className="modal-data">16</span> </li> </ul> </div> ) } </Fragment> ) } } export default GisThree; 復制代碼

在服務器出現的錯誤,而本地服務器沒有問題 參考 stackoverflow.com/questions/4…

objLoader.js:624 Uncaught Error: THREE.OBJLoader: Unexpected line: "<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><title>智慧社區_管理后台</title><link href="/static/css/main.bdb0e864.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="/config.js"></script><script type="text/javascript" src="/static/js/charts.24f90613.js"></script><script type="text/javascript" src="/static/js/vendor.0b9068d0.js"></script><script type="text/javascript" src="/static/js/main.cfa93993.js"></script></body></html>" at OBJLoader.parse (objLoader.js:624) at objLoader.js:385 at XMLHttpRequest.<anonymous> (three1.js:630) objLoader.js:624 Uncaught Error: THREE.OBJLoader: Unexpected line: "<!doctype html>" at OBJLoader.parse (objLoader.js:624) at objLoader.js:385 at XMLHttpRequest.<anonymous> (three1.js:630) 復制代碼

最后發現棄用 mtl-loader 之后(且升級到 webpack4 )正確顯示了材質,以及出現了 git 忽略了 .obj 問題,看博客,全局的 gitignore_global.txt 中忽略了 .obj 問題,好坑!!!

如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝!

參考:

  1. 入門--Three.js 現學現賣
  2. threejs 官方文檔
  3. Three.js 中文教程
  4. 1.orbit controls 插件 ---2.詳解
  5. 3D 模型導入
  6. react three.js
  7. 首個threejs項目-前端填坑指南 主要介紹了 C4D 轉 json 以及一些動畫模型注意點
  8. 【Three.js】OrbitControl 旋轉插件
  9. 讀取 blender 模型並導入動畫
  10. 十分鍾打造 3D 物理世界

鏈接:https://juejin.im/post/5ca22692f265da30a53d6656


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM