three.js快速上手以及在react中運用

sundjly發表於2019-04-01

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 物理世界

相關文章