threejs 詳解(一)

1. 開始

1.1 開使用threejs寫一個(gè)最簡潔的demo

function init() {  // **寫任何3d界面初始化必備的東西**

    **// renderer 渲染器**
    var renderer = new THREE.WebGLRenderer({
        canvas: document.getElementById(“test-overview”)
    });

    renderer.setClearColor(0x000000);  // 設(shè)置場景舞臺背景色

    **// scene  場景舞臺,可以看做是最大的group,特殊的group**
    var scene = new THREE.Scene();

    **// camera 相機(jī)**
    var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000);

    camera.position.set(0, 0, 5);

    scene.add(camera);

   ** // 創(chuàng)建一個(gè)模型, 模型(Mesh)=幾何體(XXGeometry)+材質(zhì)(XXMaterial),材質(zhì)可以理解為游戲中的英雄皮膚,你可以切換皮膚,但模型的幾何體不變**
    var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
            new THREE.MeshBasicMaterial({ // 基礎(chǔ)材質(zhì),不受光照影響
                color: 0xff000 // 材質(zhì)顏色
            }));

    scene.add(cube);  // 最后將模型添加到場景當(dāng)中
    render(scene, camera)

}

funciton render(scene, camera){
    var myReq = requestAnimationFrame(render);   // 相當(dāng)于setInterval 不停地render渲染, 渲染消耗性能,在不必要的情況下可以暫停渲染,暫停之后它就相當(dāng)于靜態(tài)圖片
    renderer.render(scene, camera);
}

1.2 threejs常用對象或?qū)ο髮傩?/strong>

1.2.1 Scene,Group

scene 是場景,group是組,他們都是裝Mesh模型的箱子,容器, 不同的是:

scene一般只會有一個(gè),他就好比樹狀結(jié)構(gòu)的根節(jié)點(diǎn),group可以有好幾個(gè),group還可以裝group;
group 一般只會裝模型, 而sence一般用來裝group,mesh模型,camera相機(jī), light燈光,就好比拍電影會用的道具,模型

var scene = new THREE.Scene()
var group = new THREE.Group()
  • 他倆常用api屬性:

增加.add(xx), 刪除.remove(xx), 清空所有clear(),

是否可見.visible = 布爾值

名稱.name = ”xx” // 這個(gè)一般是group用,給組命個(gè)名,用到時(shí)好找,比如要給哪個(gè)組加特效


1.2.2 Light

沒有光,就算模型加到場景當(dāng)中也看不見,一片黑,但是呢,模型的亮度不一定是光越強(qiáng)越亮,這跟模型材質(zhì)的反光度也有關(guān)

  • 常用的光的類型:
  1. 環(huán)境光
var light = new THREE.AmbientLight(光的顏色);   // 環(huán)境光一個(gè)就夠了
light.intensity = 1  // 光的強(qiáng)度
sence.add(light)  // 記得加入場景
  1. 點(diǎn)光源
var light = new THREE.PointLight(顏色, 強(qiáng)度, 距離);   // 點(diǎn)光源可以多弄幾個(gè)照射

環(huán)境光不需要調(diào)整位置他沒有XXXHelper,點(diǎn)光源可以,將光添加到PointLightHelper就看到一個(gè)菱形物體了,你可以像調(diào)整模型一樣調(diào)整光的位置

var pointLightHelper = new THREE.PointLightHelper(light, 10);
scene.add(pointLightHelper)
QQ圖片20220129095114.png

1.2.3 Camera

就算有光,沒有眼睛照樣一片黑

  • 常用camera類型:
  1. 透視相機(jī),就跟人眼看到的世界一樣效果,遠(yuǎn)小近大
var width = document.getElementById("overview-container").clientWidth   // canvas的寬
var height= document.getElementById("overview-container").clientHeight  // canvas 的高
var camera = new THREE.PerspectiveCamera(45, width/height, 0.5, 6000)   // 參數(shù)基本不用調(diào),詳情看文檔
20190918231348619.png
  1. 正交相機(jī), 沒有遠(yuǎn)小近大,看到都就是物體本身大小,使用場景比如選礦的俯視圖
var k = width / height; //窗口寬高比
var s = 450; //三維場景顯示范圍控制系數(shù),系數(shù)越大河胎,顯示的范圍越大
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
20190918231213655.png
  • 常用屬性:
    camera.lookAt(xx.poation) // 相機(jī)聚焦于那個(gè)點(diǎn),即屏幕中心對準(zhǔn)的三維空間中的哪個(gè)點(diǎn)

1.2.4 WebGLRender

有上述幾個(gè)對象加到場景當(dāng)中,我們就可以將模型渲染出來了,他就好比是一支筆開始畫畫了, 光告訴他顏色該怎么畫,攝像機(jī)告訴他從哪個(gè)角度畫

var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) // 創(chuàng)建render
renderer.setSize(document.getElementById("overview-container").clientWidth, document.getElementById("overview-container").clientHeight)
renderer.setClearColor(0xeeeeee, 0.0)  

實(shí)例化后renderer會有一個(gè)domElement屬性,值是一個(gè)canvas的dom元素,因?yàn)閠hreejs就是靠canvas畫出來的場景,把這個(gè)canvas塞到vue的div中就行了

let webglOutput = document.getElementById(“xx-container”);
webglOutput.appendChild(renderer.domElement)

然后定時(shí)器不停地調(diào)用,寫一個(gè)render方法, 如果你只調(diào)用一次,那就是繪制了一張圖片相當(dāng)于,不停的用render方法,才會不停地繪制

function render() {
        requestAnimationFrame(render)
        camera.updateProjectionMatrix();  // 攝像機(jī)也要不停地更新
        renderer.render(場景, 攝像機(jī))
}

1.2.5 Controls

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' 引文件

想要鼠標(biāo)控制三維場景,那么就需要用到控制器, 控制器是我們一般只會用到OrbitControls,就是鼠標(biāo)左鍵旋轉(zhuǎn)場景,右鍵平移場景,滾輪放大縮小.

還有其他類型的控制器比如射擊類游戲,他鏡頭的中心點(diǎn)始終就是你鼠標(biāo)的位置;

  var controls = new OrbitControls(camera, renderer.domElement)
  • 常用api屬性:
    controls.addEventListener("start", () => { // 監(jiān)聽鼠標(biāo)觸發(fā)的事件})
    controls.addEventListener("end", () => { // 監(jiān)聽鼠標(biāo)觸發(fā)結(jié)束的事件})
    controls.minDistance = 100 // 最近視距 // 滾輪放大的數(shù)值
    controls.maxDistance = 1500 //最遠(yuǎn)視距 // 滾輪縮小的數(shù)值
    controls.rotateSpeed = 0.7 // 左鍵旋轉(zhuǎn)的速度
    controls.enableDamping = true // 是否開啟阻尼效果, 展示單個(gè)模型可以用到,慣性漂移

1.2.6 XXGeometry

創(chuàng)建各種類型的幾何體,雖然大部分模型都是ui給的,但是我們要加些效果的話,還是要手動(dòng)寫,這樣性能會更好!

var geometry= new THREE.PlaneGeometry(1, 1) // 平面形狀, 就是地板,只有一個(gè)面,要么是正方形,要么長方形, 常見就是作為場景的舞臺地板,或者作為文字logo的面板

var geometry = new THREE.BoxGeometry(1,1,1) // 幾何體,長方體,正方體

var geometry = new THREE.CylinderGeometry(1,1,1,1, 布爾值) // 圓柱體, 布爾值表示是否渲染上下兩個(gè)面, 比如選礦中箭頭旋轉(zhuǎn)動(dòng)畫,表示機(jī)器在運(yùn)轉(zhuǎn)

var geometry = new THREE.EdgesGeometry(geometry) // 邊框模型, 只渲染邊, 比如鼠標(biāo)移到某個(gè)物體上給他加個(gè)邊框

var shape = new THREE.Shape();
var geometry = new THREE.ShapeGeometry() // 配合THREE.Shape繪制任意幾邊形,類似于鋼筆工具, 比如選礦中每個(gè)區(qū)域地面標(biāo)記的顏色

var geometry = new THREE.TubeGeometry() // 管道幾何體,就是類似下水管道,比如煉鋼鋼水從管道中倒出;但是管道比較多的情況下,自己畫管道太麻煩了,直接修改ui給的管道中的材質(zhì),也可以做流動(dòng)動(dòng)畫,他會將管道分為幾個(gè)部分,讓他告訴你每個(gè)管道的.name就行了


1.2.7 XXMaterial

雖然threejs自帶了很多類型的材質(zhì),但我們就用最基礎(chǔ)的就好了,因?yàn)槟P鸵话闶莡i給的,材質(zhì)也在里面,我們自己也不會經(jīng)常去畫模型.

  var material= new THREE.MeshBasicMaterial()
  • 材質(zhì)的屬性:

我們做的更多的是修改材質(zhì)的屬性, 一下列舉常用的一些屬性:

new THREE.MeshBasicMaterial({
  color: “#fff”,  //材質(zhì)的顏色羡滑,好比衣服的顏色
  transparent: true,  //配合opcity使用
  opacity: 1 // 0~1之間,設(shè)置材質(zhì)透明度,即模型的透明度
  side: THREE.DoubleSide  // 決定渲染那個(gè)面,比如PlaneGeometry他有上下兩個(gè)面,默認(rèn)全部渲染粥航,你也可以之渲染上面,或者只渲染下面蠢甲,沒有渲染的面從那個(gè)方向看曼追,就是透明的
  metalness: // 金屬度晶伦,0~1之間調(diào)整, 解決ui給的模型較暗問題
  map: texture  //紋理
})

1.2.8 TextureLoader

紋理是材質(zhì)的很重要組成部分,如果沒有紋理就好像你的衣服沒有裝飾,就是純白色,純黑色,很單調(diào)

加載紋理,紋理可是是圖片,也可以是canavas或者視頻都可以,這里以圖片舉例

  var textureLoader = new THREE.TextureLoader();
  var texture = textureLoader.load(path);  // 圖片路徑

texture 常用api屬性:

// 設(shè)置陣列模式 RepeatWrapping, 紋理一般是一張圖片,你需要鋪滿整個(gè)面,那么你就需要設(shè)置他以何種方式鋪滿,大部分只要按下面這兩句復(fù)制就行了

texture.wrapS = THREE.RepeatWr<u>a</u>pping
texture.wrapT = THREE.RepeatWrapping
texture.repeat.x = 2 // 紋理在X軸方向重復(fù)多少份,相同的還有y軸和z軸
texture.offset.x = 2 // 紋理在X軸方向挪動(dòng)多少單位,你想讓他對齊的位置是哪里,相同的還有y軸和z軸

texture.needsUpdate = true; 如果你的紋理是動(dòng)態(tài)的,比如canvas動(dòng)畫,vedio視頻,你需要在render里面持續(xù)設(shè)置, 因?yàn)閞ender是在定時(shí)器中執(zhí)行的,只設(shè)置一次是無效的!


1.2.9 Mesh

var mesh = new THREE.Mesh(幾何體,材質(zhì)) // 最常用的模型,你用loader加載ui給的模型就是mesh對象

  • 常用api屬性:

縮放mesh.scale.set(x,y,z) ,旋轉(zhuǎn)mesh.rotation.set(x,y,z),位置mesh.position.set(x,y,z) ,
mesh.clone() // 克隆模型,復(fù)制同一個(gè)引用,節(jié)約內(nèi)存

遍歷mesh,因?yàn)閡i給的mesh可能是好幾個(gè)mesh組成的整體,比如你要給這個(gè)模型透明,你需要遍歷整個(gè)mesh的材質(zhì)才能實(shí)現(xiàn)

mesh.traverse(function (child) {
  if (child.isMesh) {  // 如果是mesh類型,怎么怎么樣
    ....
  }
})

1.2.10 GLTFLoader

加載ui給的模型文件,還有其他XXloader,我們基本不會用,就gltfLoader夠了

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' //gltfloader需要單獨(dú)引文件, 我上面寫的THREE.xxx都是在three包里面,這個(gè)是單獨(dú)分開的

gltfloader 用來加載.gltf格式的文件,這個(gè)文件就是ui給你的模型文件,如果不是這個(gè)格式讓他轉(zhuǎn)一下,或者你自己轉(zhuǎn)一下

      var gltfLoader = new GLTFLoader()   // 這個(gè)一般情況下只要在new一次就夠了
      gltfLoader.load(
        “模型文件路徑”,
        (gltf) => {
              // 一般是gltf.sence,這個(gè)就是一個(gè)Mesh對象,加載完之后通過group.add(gltf.sence)添加到場景中
        },
      )

1.2.11 AnimationMixer

除了你自己可以做一些簡單的縮放平移旋轉(zhuǎn)動(dòng)畫, 但是比較復(fù)雜的動(dòng)畫是ui給你畫的,需要你手動(dòng)播放還是停止

定義AnimationMixer對象

var animate = new THREE.AnimationMixer(mesh模型) // 看業(yè)務(wù),如果你復(fù)制的多個(gè)模型共用一個(gè)動(dòng)畫,那你就new一個(gè),如果比如6個(gè)磁選機(jī),可能某一個(gè)磁選機(jī)暫停了動(dòng)畫,但其他不暫停,那就要針對每個(gè)磁選機(jī)模型都要new一個(gè)AnimationMixer了

因?yàn)橐粋€(gè)模型可能有多個(gè)動(dòng)畫片段,好比打游戲按Q是出拳,按W是踢腿,目前我遇到的都是一個(gè)模型就一個(gè)動(dòng)畫片段;所以直接獲取第一個(gè)片段;如果多個(gè)片段就遍歷

var clip = gltf.animations[0];
var animationAction = animate.clipAction( clip )

clipAction的常用的一些屬性:

animationAction.clampWhenFinished = true; // 是否最后一幀暫停
animationAction.loop = THREE.LoopOnce; // 不循環(huán)播放
animationAction.time = 0; // 動(dòng)畫開始播放時(shí)間
clip.duration = 0; // 動(dòng)畫過程時(shí)間
animationAction.play(); // 動(dòng)畫開始執(zhí)行,你可以把a(bǔ)nimationAction存起來,在合適的時(shí)機(jī)去play(),比如設(shè)備故障了你就不執(zhí)行.stop()


1.3 在vue中使用threejs

  1. cnpm i three --save 安裝依賴

  2. import * as THREE from 'three' // 引入threejs對象,你也可以按需引入,這里方便就全引入了

  3. 在template中定義一個(gè)dom, <div id="test-overview"></div>

  4. 在mounted中初始化, 這里可以看出created和mounted區(qū)別, 你需要等待vue的dom加載完成才能去畫3d

mounted(){

執(zhí)行1.1中的init方法

}

最簡單的大致是這四個(gè)步驟.實(shí)際以這個(gè)為拓展;

下面看實(shí)際項(xiàng)目,雖然3d在項(xiàng)目之內(nèi),但寫好一個(gè)3d文件代碼量多,完全可以看做一個(gè)單獨(dú)的項(xiàng)目;

我的項(xiàng)目結(jié)構(gòu)一般這樣:

├── overview                    
│   ├── components                 // 大屏的組件,比如兩邊側(cè)邊欄信息,3dcanvas
│         └── 3d                // 3d相關(guān)組件
│            └──XX3D.vue  // 承載threejs的容器,threejs生成的canvas插入到其中
│   └── images               //圖片資源,比如紋理會用到的圖片
│   └── utils               //我一般分為三個(gè)文件
│         └──model-animate.js   // 專門寫動(dòng)畫
│         └──model-common.js  // 專門寫初始化的一些操作,比如場景,相機(jī),render渲染
│         └──model-loader.js  // 專門加載模型的,大部分是ui給的gltf或glb的模型,也有自己寫的Mesh,比如文字
│         └──MoveUtil.js // 調(diào)試模型位置角度的工具logo,紋理等等
│   ├── Overview.vue    // 最大的父組件,路由引用的這個(gè)文件        

1.4 Vue中優(yōu)化threejs加載

如果用最簡單的方式寫threejs有一堆問題,你需要考慮模型加載速度問題

1.4.1 使用keep-alive緩存

大部分情況下,我們最好將初始化后的threejs 場景緩存下來,不要每次都初始化一下,如果每次都初始化,如果內(nèi)存清理不干凈,vue單頁面路由反復(fù)跳轉(zhuǎn)會導(dǎo)致內(nèi)存一直增長.

這里先使用keep-alive 將組件緩存,我是直接將Overview 緩存了,緩存了父組件子組件也是會被緩存的;

activated(){

執(zhí)行1.1中的init方法

} // 使用activated鉤子代替mounted鉤子;

tip: 如果一個(gè)組件使用了activated鉤子,并且這個(gè)組件在父組件是被異步引用的,那么這個(gè)組件第一次初始化是不會執(zhí)行activeted鉤子,異步引用組件好處父組件加載就是快那么一點(diǎn)點(diǎn),同步相反;

1.4.2 模型預(yù)加載

如果你的頁面一進(jìn)來不是3d概覽頁面,那最好不過了,第一次與后續(xù)加載都會很快; 如果不是的話,并且你模型大小綜合一兩百兆第一次加載就會比較遲鈍, 但是后續(xù)加載還是很快;

比如在main.js中 引入model-loader

import { prevLoadModels } from "@/views/overview/utils/model-loader";
const modelPathArr = [
  'static/three-models/管道.gltf',
  'static/three-models/地形.gltf',
  'static/three-models/碎礦.gltf',
...
]
modelPathArr.forEach(path => {   // 遍歷模型文件路徑,一個(gè)個(gè)后臺偷偷加載,并存到內(nèi)存中,這里我直接掛在到window對象上,因?yàn)樗隙ㄊ且恢贝嬖诘?
  prevLoadModels(path)
})

1.4.3 模型按需加載

如果一個(gè)模型很大,就把ui拆分成好幾個(gè)模型,就跟加載游戲場景一樣,先把主要的場景加載完,一些小模型,細(xì)枝末節(jié)的后加載;

1.4.4 相同模型克隆

為了提高threejs 渲染效率,長得一摸一樣的模型可以使用Mesh.clone的方法, 比如選礦的皮帶

1.4.5 相同材質(zhì)克隆(有缺點(diǎn))

當(dāng)你需要替換一個(gè)模型的材質(zhì)時(shí)候,你不需要每次都new XXMaterial(), 這樣可以優(yōu)化渲染性能;但是不好的是同一個(gè)材質(zhì)對象是引用類型,一個(gè)材質(zhì)得變化會導(dǎo)致其他所有材質(zhì)都變化;有時(shí)候我并不想這樣; 比如選礦分成三個(gè)區(qū)域,選礦磨礦,當(dāng)我的視角切換到選礦時(shí)候,我希望自己的區(qū)域內(nèi)的皮帶不透明另外兩個(gè)區(qū)域透明(透明就是改變材質(zhì)的.opacity屬性), 但是改變了一個(gè)所有的都透明了(因?yàn)槠esh.clone的時(shí)候材質(zhì)都是用的同一份),這時(shí)候我應(yīng)該new三個(gè)材質(zhì),分別替換每個(gè)區(qū)域皮帶的材質(zhì),相對的這樣性能就下降了些


1.5 更好的使用threejs,經(jīng)驗(yàn)總結(jié)

1.5.1 善用Mesh模型的add方法

Mesh的add就跟group的add一樣,只不過group是看不見容器,Mesh是看的見的, 如果A模型.add(B模型),那么這兩個(gè)模型就可以是一個(gè)組,A模型放大,B模型會跟著放大,A模型移動(dòng),B模型會跟著移動(dòng),這樣的好處就是就好比寫vue組件,你將重復(fù)的代碼抽成一個(gè)組件,這里不是為了方便引用而是方便調(diào)試,因?yàn)槟P臀恢?大小經(jīng)常會隨業(yè)務(wù)變化而變化;

1.5.2 善用group和group.name

這比如選礦中有三個(gè)區(qū),選礦,磨礦,尾礦,顯而易見我應(yīng)該給他們分成三個(gè)組,分別再設(shè)置組的name屬性, 然后使用上面1.4.1的邏輯模型嵌套模型

總結(jié)就是 場景(sence) => 組(group) => 父模型(Mesh) => 子模型(Mesh)....

不僅是為了后面好寫代碼,而且你點(diǎn)開sence屬性的時(shí)候邏輯也清晰明了;

1.5.3 修改模型的metalness和加多個(gè)燈光

初遇threejs 有一大堆格式的模型文件.fbx, .glb, .gltf, .... 我們就用gltf模型. 但是導(dǎo)入進(jìn)入明明加了燈光,亮度也調(diào)的挺高的,可模型就是偏暗, 這時(shí)候要么你修改材質(zhì)的metalness (金屬度,我喜歡理解為反光程度) 0~1 之間調(diào)整; 我的loader方法里都是加了initEmissive方法的, 默認(rèn)會設(shè)置為.1, 這時(shí)候大部分模型應(yīng)該是亮的, 如果只有個(gè)別模型很暗,聯(lián)系ui修改

mesh.traverse(function (child) {
 if (child.isMesh) {
     child.material.metalness= metalness;
 }
})

燈光的話一般加4個(gè)就夠了,分別是三角形擺放3個(gè)燈, 然后正中心的上方放個(gè)燈,組成金字塔形狀;

最后還有一個(gè)環(huán)境光,他是一個(gè)無處不在的光,他不是很強(qiáng).加了更好

QQ圖片20220129104834.png

1.5.4 理解攝像機(jī)和控制器的關(guān)系

常用的camera 對象就是 PerspectiveCamera (透視攝像機(jī)), 另外一個(gè)只在選礦中用了一下 OrthographicCamera(正投影攝像機(jī),可以用俯視圖扁平化查看,類似于流程圖一樣);

攝像機(jī)你也可以當(dāng)做是一個(gè)模型,每new 一個(gè)Camera 就是創(chuàng)建一個(gè)眼睛, 在threejs中我們不需要兩只眼睛,一個(gè)就夠了; 初始化的時(shí)候你需為攝像機(jī)設(shè)置.postion.set(x,y,z); 同時(shí)你還需要設(shè)置一下.lookAt(xx.postion) 一般是場景的中心,當(dāng)然你也可以看向別處;設(shè)置了眼睛看相的位置,那么屏幕中點(diǎn)就對準(zhǔn)你看的那個(gè)位置

在寫攝像機(jī)角度切換的動(dòng)畫時(shí), 你會發(fā)現(xiàn)就算你設(shè)置了相機(jī)的postion,rotation的起始位置,終點(diǎn)位置,實(shí)際效果還是有偏差,這是因?yàn)槟阍谡{(diào)整攝像機(jī)時(shí),攝像機(jī)的lookAt的點(diǎn)已經(jīng)變了,你還需要調(diào)整control.target的起始位置和中心位置;,因?yàn)槟阍谑髽?biāo)調(diào)整視角時(shí),lookAt也跟著變了,變成了control的target坐標(biāo)

1.5.5 判斷模型有沒有加載完成

雖然模型gltfLoader有加載的進(jìn)度回調(diào)函數(shù),但是沒啥用,因?yàn)槲覀兡P头珠_加載的,那么,你可以定義一個(gè)變量,每加載完成一個(gè)模型變量++,在定義一個(gè)模型總數(shù)的常量,如果到了總數(shù)就發(fā)送事件;

1.5.6 其他文件更好的調(diào)用render

現(xiàn)在的utils文件大致分為三個(gè)model_common, model_animate, model_loader, 只有common里面有定時(shí)器不斷地render, 如果其他文件也要調(diào)用render ,建議各自在文件里寫個(gè)方法,然后export他,然后common里面引入,放到render里面;這樣三個(gè)文件盡量只做跟自己相關(guān)的事

1.5.7 vue文件跟js文件通信

  1. js文件中:
var vm;  // vue對象
export function getVue(that) {
  vm = that;
}

2.在Overview.vue 引入他,并把this,即當(dāng)前對象傳遞過去即可;


1.6 threejs一些效果(具體業(yè)務(wù)具體發(fā)揮)

1.6.1 使用CSS2DObject 將html與3d場景結(jié)合

一般是用來做模型上面的標(biāo)簽,點(diǎn)擊事件也好處理,直接是用vue那一套,如果是3d場景中做點(diǎn)擊事件,點(diǎn)擊某個(gè)模型觸發(fā)xxx代碼賊多!

import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'; // 引入他

比如我創(chuàng)建一個(gè)html的dom元素

方法1: let domContainer = document.createElement(“div”) // 相當(dāng)于html文件中直接寫<div></div>,因?yàn)樵趈s中,就需要函數(shù)式編程;

方法2: let domContainer = document.getElementById(“#xxx”) // 如果你已經(jīng)在vue文件中創(chuàng)建了這個(gè)元素,并給他加個(gè)id=xxx,那么你可以直接獲取到這個(gè)元素

以上簡單的html你可以用方法1,復(fù)雜的推薦2;

let label = new CSS2DObject(domContainer); // label也有postion,rotation等屬性

mesh.add(label) // 最后就跟添加模型一樣添加label;

1.6.2 使用TWEENJS做動(dòng)畫

cnpm i @tweenjs/tween.js --save // 安裝依賴

import TWEEN from"@tweenjs/tween.js" // 引入他

function xxx( mesh ){
        let startY = mesh.position.clone().y;  // 人生建議,先把起始位置通過clone的方式復(fù)制一份,不然某些情況下他會出現(xiàn)疊加態(tài)勢的變化
        var tween = new TWEEN.Tween({   // 動(dòng)畫開始的數(shù)值,可以是postion,也可以是rotation,也可是scale,總之是數(shù)字的值都可以
            oGun_y: startY, // 氧槍y軸位置,
        });
        tween.to({  // 動(dòng)畫結(jié)束數(shù)值
            oGun_y: getRatioPosition(endY), // 氧槍y軸位置,
        }, 1000);
        tween.onUpdate(function (object) {   // 動(dòng)畫更新鉤子,意思就是每次更新tween將下一次的運(yùn)算結(jié)果覆蓋上一個(gè)結(jié)果,實(shí)現(xiàn)一點(diǎn)點(diǎn)的更新,你用定時(shí)器也能實(shí)現(xiàn)類似簡單的效果
            oGun_model.position.y = object.oGun_y;
        })
        tween.onComplete(function () { // 動(dòng)畫執(zhí)行完成時(shí)候的鉤子,你可以在一個(gè)動(dòng)畫結(jié)束后執(zhí)行另一個(gè)動(dòng)畫

        })
        tween.easing(TWEEN.Easing.Quartic.Out);  // 設(shè)置動(dòng)畫執(zhí)行的貝賽爾曲線
        tween.delay(1000)  // 動(dòng)畫延遲1秒執(zhí)行
        tween.start();  // 動(dòng)畫執(zhí)行
}

更多參考鏈接:

中文文檔
http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/zh/renderers/WebGLRenderer

threejs 官方demo,每個(gè)知識點(diǎn)都有
https://threejs.org/examples/#webgl_animation_keyframes // 網(wǎng)絡(luò)有時(shí)候慢,需要翻墻


可應(yīng)用與大多數(shù)項(xiàng)目的特效:

  1. 鏡頭切換效果

  2. 天空盒背景

  3. 模型陰影效果

  4. 鼠標(biāo)判斷是否選中取場景中的模型;

  5. 精靈圖做模型標(biāo)簽

  6. Echart圖表映射到3d場景中,并可以實(shí)時(shí)更新變化

  7. 模型外發(fā)光

  8. 模型沿自定義路徑移動(dòng)

  9. 粒子成像

  10. 物理引擎模擬碎石掉落

  11. Shader高級渲染

  12. 模型拆散自動(dòng)組裝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泰涂,一起剝皮案震驚了整個(gè)濱河市鲫竞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逼蒙,老刑警劉巖从绘,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異是牢,居然都是意外死亡僵井,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門驳棱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來批什,“玉大人,你說我怎么就攤上這事社搅∫缥牵” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我贱枣,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任目胡,我火速辦了婚禮,結(jié)果婚禮上源织,老公的妹妹穿的比我還像新娘翩伪。我一直安慰自己,他們只是感情好谈息,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布缘屹。 她就那樣靜靜地躺著,像睡著了一般侠仇。 火紅的嫁衣襯著肌膚如雪轻姿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天逻炊,我揣著相機(jī)與錄音互亮,去河邊找鬼。 笑死余素,一個(gè)胖子當(dāng)著我的面吹牛豹休,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桨吊,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼威根,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了视乐?” 一聲冷哼從身側(cè)響起洛搀,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎佑淀,沒想到半個(gè)月后留美,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渣聚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年独榴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奕枝。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棺榔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出隘道,到底是詐尸還是另有隱情症歇,我是刑警寧澤郎笆,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站忘晤,受9級特大地震影響宛蚓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜设塔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一凄吏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闰蛔,春花似錦痕钢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至例诀,卻和暖如春随抠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背繁涂。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工拱她, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爆土。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓椭懊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親步势。 傳聞我的和親對象是個(gè)殘疾皇子氧猬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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