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)
- 常用的光的類型:
- 環(huán)境光
var light = new THREE.AmbientLight(光的顏色); // 環(huán)境光一個(gè)就夠了
light.intensity = 1 // 光的強(qiáng)度
sence.add(light) // 記得加入場景
- 點(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)
1.2.3 Camera
就算有光,沒有眼睛照樣一片黑
- 常用camera類型:
- 透視相機(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),詳情看文檔
- 正交相機(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);
- 常用屬性:
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
cnpm i three --save 安裝依賴
import * as THREE from 'three' // 引入threejs對象,你也可以按需引入,這里方便就全引入了
在template中定義一個(gè)dom, <div id="test-overview"></div>
在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).加了更好
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文件通信
- 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)目的特效:
鏡頭切換效果
天空盒背景
模型陰影效果
鼠標(biāo)判斷是否選中取場景中的模型;
精靈圖做模型標(biāo)簽
Echart圖表映射到3d場景中,并可以實(shí)時(shí)更新變化
模型外發(fā)光
模型沿自定義路徑移動(dòng)
粒子成像
物理引擎模擬碎石掉落
Shader高級渲染
模型拆散自動(dòng)組裝