Three.js加載gltf模型文件

項目中直接使用的是three.js直接加載gltf模型文件员凝。

gtlf文件被譽為3d界的“jpeg”,很形象

gltf

個人接觸的gltf文件不是很多知牌,所以這里舉一個項目中用的列子油狂,圖片奉上

image

scene.bin文件為3d模型導(dǎo)出的二進制文件,最最主要的文件之一(哈哈)
我在寫模型加載的時候企锌,是引用的外部配置文件榆浓,這樣的話,就能使組件能夠復(fù)用霎俩,寫一個組件哀军,更改不同的配置文件沉眶,加載不同的模型

后面調(diào)用組件時傳遞給組件的參數(shù)
{
      // 傳入給3d模型渲染組件的服務(wù)器路徑
    "model": "/web_app/three3d/models/019/scene.gltf",
    "textures": "",
    "envMap": "",
    "texturesOn": true,
    "enableKeys": false,
    "autoRotate": false
}

組件編寫

render函數(shù)長這樣,ref的作用是獲取到標(biāo)簽的真實dom(哈哈杉适,個人理解)

render() {
    let style = { width: '100%', height: '100%' };
    return (
      <div 
        style={style}
        ref={(mount) => { this.mount = mount; }}
      />
    );
  }

定義一些需要用到的全局變量

    let container,
        mixer,
        controls;
    let camera,
        scene,
        renderer,
        light;
    // 定義three中的動畫時間
    let clock = new THREE.Clock();

    let threeConf = this.state.threeConf;
    container = document.createElement('div');
    this.mount.appendChild(container);
    container = this.mount;
    let width = this.mount.clientWidth;
    let height = this.mount.clientHeight;
    camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
    container.addEventListener('resize', () => {
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
      renderer.setSize(width, height);
    }, false);

定義textures環(huán)境圖片谎倔,有這個的話模型就會有反射環(huán)境,圖片是從服務(wù)器拿回來的猿推。

// 環(huán)境設(shè)置片习,設(shè)置之后可以反射周圍環(huán)境
    let envMap = new THREE.CubeTextureLoader().load([
      '/images/sys/three3d/envmappic/posx.jpg',
      '/images/sys/three3d/envmappic/negx.jpg',
      '/images/sys/three3d/envmappic/posy.jpg',
      '/images/sys/three3d/envmappic/negy.jpg',
      '/images/sys/three3d/envmappic/posz.jpg',
      '/images/sys/three3d/envmappic/negz.jpg'
    ]);
    scene = new THREE.Scene();

    // 添加光線
    light = new THREE.HemisphereLight(0xbbbbff, 0x444422);
    light.position.set(0, 1, 0);
    scene.add(light);

關(guān)于gltf的模型加載器不用自己寫loader加載。scene.gltf文件就是我們需要loader的文件

// 新建loader,加載gltf文件
    let loader = new THREE.GLTFLoader();
    loader.load(threeConf.model, (gltf) => {
      const gltfScene = gltf.scene || gltf.scenes[0];
      const clips = gltf.animations || [];
      gltfScene.updateMatrixWorld();
      const box = new THREE.Box3().setFromObject(gltfScene);
      const size = box.getSize(new THREE.Vector3()).length();
      const center = box.getCenter(new THREE.Vector3());
      // controls.reset();

      gltfScene.position.x += gltfScene.position.x - center.x;
      gltfScene.position.y += gltfScene.position.y - center.y;
      gltfScene.position.z += gltfScene.position.z - center.z;

      // 重新設(shè)置相機參數(shù)
      controls.maxDistance = size * 10;
      camera.near = size / 100;
      camera.far = size * 100;
      camera.updateProjectionMatrix();

      camera.position.copy(center);
      camera.position.x += size / 2.0;
      camera.position.y += size / 2.0;
      camera.position.z += size / 2.0;
      camera.lookAt(center);

      gltf.scene.traverse((child) => {
        if (child.isMesh) {
          child.material.envMap = envMap;
        }
      });
      scene.add(gltf.scene);
      // 判斷當(dāng)前的gltf問價中是否有動畫
      if (clips.length !== 0) {
        mixer = new THREE.AnimationMixer(gltfScene);
        mixer.clipAction(gltf.animations[0]).play();
      } else {
      }
    });
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(container.devicePixelRatio);
    renderer.setSize(width, height);
    renderer.gammaOutput = true;

控制器和動畫的加載

    controls = new THREE.OrbitControls(camera, container);
    //按鍵控制
    controls.enableKeys = false;
    // 自動旋轉(zhuǎn)(默認(rèn)flase)
    controls.autoRotate = false;
    container.appendChild(renderer.domElement);
    container.addEventListener('resize', this.onWindowResize, false);

    requestAnimationFrame(function fn() {
      if (mixer) {
        let delta = clock.getDelta();
        mixer.update(delta);
      }
      requestAnimationFrame(fn);
      renderer.render(scene, camera);
      controls.update();
    });

控制器中用到的監(jiān)聽方法

onWindowResize = () => {
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    renderer.setSize(width, height);
  }

最后附上完整代碼

import React from 'react';

const THREE = window.THREE = require('three');

require('three/examples/js/loaders/GLTFLoader');
require('three/examples/js/loaders/DRACOLoader');
require('three/examples/js/loaders/DDSLoader');
require('three/examples/js/controls/OrbitControls');
require('three/examples/js/controls/TrackballControls');
require('three/examples/js/loaders/RGBELoader');
require('three/examples/js/loaders/HDRCubeTextureLoader');
require('three/examples/js/pmrem/PMREMGenerator');
require('three/examples/js/pmrem/PMREMCubeUVPacker');


THREE.DRACOLoader.setDecoderPath('lib/draco/');


class Three3D extends React.Component {
  state = ({ threeConf: this.props.threeConf });

  componentDidMount() {
    let container,
      mixer,
      controls;
    let camera,
      scene,
      renderer,
      light;
    // 定義three中的動畫時間
    let clock = new THREE.Clock();

    let threeConf = this.state.threeConf;
    container = document.createElement('div');
    this.mount.appendChild(container);
    container = this.mount;
    let width = this.mount.clientWidth;
    let height = this.mount.clientHeight;
    camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
    container.addEventListener('resize', () => {
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
      renderer.setSize(width, height);
    }, false);

    // 環(huán)境設(shè)置蹬叭,設(shè)置之后可以反射周圍環(huán)境
    let envMap = new THREE.CubeTextureLoader().load([
      '/images/sys/three3d/envmappic/posx.jpg',
      '/images/sys/three3d/envmappic/negx.jpg',
      '/images/sys/three3d/envmappic/posy.jpg',
      '/images/sys/three3d/envmappic/negy.jpg',
      '/images/sys/three3d/envmappic/posz.jpg',
      '/images/sys/three3d/envmappic/negz.jpg'  
    ]);
    scene = new THREE.Scene();

    // 添加光線
    light = new THREE.HemisphereLight(0xbbbbff, 0x444422);
    light.position.set(0, 1, 0);
    scene.add(light);

    // 新建loader,加載gltf文件
    let loader = new THREE.GLTFLoader();
    loader.load(threeConf.model, (gltf) => {
      const gltfScene = gltf.scene || gltf.scenes[0];
      const clips = gltf.animations || [];
      gltfScene.updateMatrixWorld();
      const box = new THREE.Box3().setFromObject(gltfScene);
      const size = box.getSize(new THREE.Vector3()).length();
      const center = box.getCenter(new THREE.Vector3());
      // controls.reset();

      gltfScene.position.x += gltfScene.position.x - center.x;
      gltfScene.position.y += gltfScene.position.y - center.y;
      gltfScene.position.z += gltfScene.position.z - center.z;

      // 重新設(shè)置相機參數(shù)
      controls.maxDistance = size * 10;
      camera.near = size / 100;
      camera.far = size * 100;
      camera.updateProjectionMatrix();

      camera.position.copy(center);
      camera.position.x += size / 2.0;
      camera.position.y += size / 2.0;
      camera.position.z += size / 2.0;
      camera.lookAt(center);

      gltf.scene.traverse((child) => {
        if (child.isMesh) {
          child.material.envMap = envMap;
        }
      });
      scene.add(gltf.scene);
      // 判斷當(dāng)前的gltf問價中是否有動畫
      if (clips.length !== 0) {
        mixer = new THREE.AnimationMixer(gltfScene);
        mixer.clipAction(gltf.animations[0]).play();
      } else {
      }
    });
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(container.devicePixelRatio);
    renderer.setSize(width, height);
    renderer.gammaOutput = true;

    controls = new THREE.OrbitControls(camera, container);
    //按鍵控制
    controls.enableKeys = false;
    // 自動旋轉(zhuǎn)(默認(rèn)flase)
    controls.autoRotate = false;
    container.appendChild(renderer.domElement);
    container.addEventListener('resize', this.onWindowResize, false);

    requestAnimationFrame(function fn() {
      if (mixer) {
        let delta = clock.getDelta();
        mixer.update(delta);
      }
      requestAnimationFrame(fn);
      renderer.render(scene, camera);
      controls.update();
    });
  }

  onWindowResize = () => {
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    renderer.setSize(width, height);
  }

  render() {
    let style = { width: '100%', height: '100%' };
    return (
      <div 
        style={style}
        ref={(mount) => { this.mount = mount; }}
      />
    );
  }
}
export default Three3D;

調(diào)用

<Three3d threeConf={this.state.threeConf} />
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末藕咏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子秽五,更是在濱河造成了極大的恐慌孽查,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坦喘,死亡現(xiàn)場離奇詭異盲再,居然都是意外死亡,警方通過查閱死者的電腦和手機瓣铣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門答朋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棠笑,你說我怎么就攤上這事梦碗。” “怎么了蓖救?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵洪规,是天一觀的道長。 經(jīng)常有香客問我藻糖,道長淹冰,這世上最難降的妖魔是什么库车? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任巨柒,我火速辦了婚禮,結(jié)果婚禮上柠衍,老公的妹妹穿的比我還像新娘洋满。我一直安慰自己,他們只是感情好珍坊,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布牺勾。 她就那樣靜靜地躺著,像睡著了一般阵漏。 火紅的嫁衣襯著肌膚如雪驻民。 梳的紋絲不亂的頭發(fā)上翻具,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音回还,去河邊找鬼裆泳。 笑死,一個胖子當(dāng)著我的面吹牛柠硕,可吹牛的內(nèi)容都是我干的工禾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蝗柔,長吁一口氣:“原來是場噩夢啊……” “哼闻葵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起癣丧,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤槽畔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胁编,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竟痰,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年掏呼,在試婚紗的時候發(fā)現(xiàn)自己被綠了坏快。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡憎夷,死狀恐怖莽鸿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拾给,我是刑警寧澤祥得,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站蒋得,受9級特大地震影響级及,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜额衙,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一饮焦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窍侧,春花似錦县踢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斧账,卻和暖如春谴返,著一層夾襖步出監(jiān)牢的瞬間煞肾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工嗓袱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扯旷,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓索抓,卻偏偏與公主長得像钧忽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逼肯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容