ThreeJS 里元素如下:
1.場(chǎng)景(Scene):是物體、光源等元素的容器凌蔬, 2.相機(jī)(Camera):控制視角的位置、范圍以及視覺(jué)焦點(diǎn)的位置,一個(gè)3D環(huán)境中只能存在一個(gè)相機(jī) 3.物體對(duì)象(Mesh):包括二維物體(點(diǎn)、線锌蓄、面)稠项、三維物體涯雅、粒子 4.光源(Light):包括全局光、平行光展运、點(diǎn)光源 5.渲染器(Renderer):指定渲染方式活逆,如webGL\canvas2D\Css2D\Css3D等精刷。 6.控制器(Control): 相機(jī)控件,可通過(guò)鍵盤蔗候、鼠標(biāo)控制相機(jī)的移動(dòng)
一怒允、camera
1.三維里的相機(jī)分為兩種,一種是透視相機(jī)锈遥,一種是正交相機(jī)纫事。
//透視投影相機(jī):
var camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
//正交投影相機(jī):
THREE.OrthographicCamera(left, right, top, bottom, near, far)</pre>
2.屬性
fov
— 攝像機(jī)視錐體垂直視野角度-aspect
— 攝像機(jī)視錐體長(zhǎng)寬比near
— 攝像機(jī)視錐體近端面far
— 攝像機(jī)視錐體遠(yuǎn)端面zoom
—設(shè)置攝像機(jī)的縮放倍數(shù)
3.兩個(gè)重要參數(shù)
camera.position:控制相機(jī)在整個(gè)3D環(huán)境中的位置(取值為3維坐標(biāo)對(duì)象-THREE.Vector3(x,y,z)) camera.lookAt:控制相機(jī)的焦點(diǎn)位置,決定相機(jī)的朝向(取值為3維坐標(biāo)對(duì)象-THREE.Vector3(x,y,z))
camera.position.set(0, 0, 1000);
4.方法
.updateProjectionMatrix () : null
更新攝像機(jī)投影矩陣所灸。在任何camer屬性參數(shù)被改變以后必須被調(diào)用丽惶。
.toJSON () : JSON
使用JSON格式來(lái)返回?cái)z像機(jī)數(shù)據(jù)。
二爬立、model
(一)Mesh
mesh包括形狀Geometry
和材質(zhì)Material
1.屬性:
name:可以通過(guò)scene.getObjectByName(name)獲取該物體對(duì)象;
userData:可以存放用戶自定義的信息钾唬;
id:是mesh實(shí)例唯一標(biāo)識(shí)
2.方法
clone() / copy()
.copy()
方法簡(jiǎn)單的說(shuō)就是復(fù)制一個(gè)對(duì)象的屬性值賦值給給另一個(gè)對(duì)象對(duì)應(yīng)的屬性
.clone()
是相當(dāng)于新建一個(gè)對(duì)象,然后復(fù)制原對(duì)象的屬性值賦值給新的對(duì)象對(duì)應(yīng)屬性侠驯,創(chuàng)建一個(gè)和原來(lái)對(duì)象完全一樣的對(duì)象
網(wǎng)格模型對(duì)象
Mesh
調(diào)用clone(),也會(huì)返回一個(gè)新對(duì)象抡秆,但是兩mesh共享模型幾何體和材質(zhì)對(duì)象,修改其中一個(gè)mesh的幾何體屬性陵霉,另一個(gè)也會(huì)跟著變化琅轧。幾何體Geometry克隆或復(fù)制,
Geometry.vertices
不是獲得對(duì)象的索引值踊挠,而是深拷貝屬性的值乍桂,你修改其中一個(gè)Geometry.vertices
的值,另一個(gè)不會(huì)發(fā)生變化效床。
getObjectById / getObjectByName
根據(jù)唯一ID或name獲取mesh實(shí)例
注意:mesh 縮放使用 mesh.scale.copy(10, 10, 10);
不可以直接使用 mesh.scale=***
cubeGeo = new THREE.CircleGeometry(1, 64);
cubeMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
});
this.circleMesh = new THREE.Mesh(cubeGeo, cubeMaterial);</pre>
(二)Geometry
包括平面Plane睹酌、圓形Circle、立方體Cube剩檀、球體Sphere憋沿、圓柱Cylinder、多面體Polyhedron等
除去這些已經(jīng)定義好的幾何體模型外沪猴,還可以利用ShapeGeometry實(shí)現(xiàn)自定義辐啄,例如:
//triangle
this.triangleShape = new THREE.Shape()
.moveTo(0, 0)//Move the .currentPoint to x, y
.lineTo(1, 1)//Connects a LineCurve from .currentPoint to x, y onto the path.
.lineTo(2, 0)
.lineTo(0, 0); // close path
this.triangleGeometry = new THREE.ShapeGeometry(this.triangleShape);
this.triangleMesh = new THREE.Mesh(
this.triangleGeometry,
new THREE.MeshPhongMaterial({ color: 0x0000ff })
);
BoxBufferGeometry
、SphereBufferGeometry
可以分別用來(lái)創(chuàng)建長(zhǎng)方體运嗜、球體
BoxGeometry
壶辜、SphereGeometry
也可以用來(lái)分別創(chuàng)建長(zhǎng)方體、球體
上文提到的幾何體模型的基類為BufferGeometry(緩沖區(qū)幾何對(duì)象)和Geometry(普通幾何對(duì)象)担租,他倆的區(qū)別是使用BufferGeometry比Geometry性能更好點(diǎn)砸民。
// create a simple square shape. We duplicate the top left and bottom right
// vertices because each vertex needs to appear once per triangle.
const vertices = new Float32Array( [
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0
] );
// itemSize = 3 because there are 3 values (components) per vertex
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
//或者
//geometry.setFromPoints([...pointPositions])//pointPositions為[vector3,vector3...]
1.將幾何轉(zhuǎn)換為BufferGeometry
var bufferGeometry = new THREE.BufferGeometry().fromGeometry( geometry );
2.幾何體位置轉(zhuǎn)換
(1)父子關(guān)系,父對(duì)象的位置發(fā)生改變子對(duì)象也會(huì)跟著改變,但子對(duì)象的位置是相對(duì)父對(duì)象的;子對(duì)象的位置發(fā)生改變岭参,父對(duì)象的位置不會(huì)改變反惕。
父對(duì)象位置改變,若想得到子對(duì)象mesh的世界坐標(biāo)演侯,則需要執(zhí)行以下方法:
for (let i = 0; i < children.length; i++) {
let vec = children[i].position;
vec.applyMatrix4(group.matrix); // 應(yīng)用網(wǎng)格的矩陣變換
children[i].position.copy(vec);
}
//或者
(2)網(wǎng)格模型位置發(fā)生改變姿染,但是幾何體Geometry的vertices不會(huì)立即發(fā)生變化,具體情況如下:
this.lineMesh = new THREE.LineLoop(
this.lineGeometry.clone().setFromPoints(this.pointPositions),
this.lineMaterial
);
this.lineMesh位置發(fā)生改變蚌本,但是this.lineGeometry的vertices不會(huì)立即發(fā)生改變盔粹,需要執(zhí)行以下方法
for (let i = 0; i < length; i++) {
vec = new THREE.Vector3();
attribute = obj.geometry.attributes.position; // 我們想要位置數(shù)據(jù)
vec.fromBufferAttribute(attribute, i); // 提取x,y程癌,z坐標(biāo)
vec.applyMatrix4(obj.matrix); // 應(yīng)用網(wǎng)格的矩陣變換
children[i].position.copy(vec);//vec即為位置改變后vertices里點(diǎn)的值
}
(三)Material
Mesh基本材質(zhì)MeshBasicMaterial
//純色材質(zhì)
material = new THREE.MeshBasicMaterial({
color: 0xff0000
}),
//貼圖
let texture = await new THREE.TextureLoader().load(currentPlatformImgInfo.img);
let texture = await new THREE.TextureLoader().load(currentPlatformImgInfo.img);
let geometry = new THREE.PlaneGeometry(currentPlatformImgInfo.width, currentPlatformImgInfo.height);
//transparent: true
let material = new THREE.MeshBasicMaterial({ map: texture, name: 'material-background',transparent: true });
//或者在loader的回調(diào)函數(shù)里完成貼圖
const loader = new THREE.TextureLoader();
loader.load(
url,
// 加載完貼圖后的回調(diào)函數(shù)
function (texture) {}
)
注意:圖片是異步加載的舷嗡,必須在加載完成之后在進(jìn)行貼圖
PointsMaterial 點(diǎn)的材質(zhì)
LineBasicMaterial 線的基礎(chǔ)材質(zhì) LineDashedMaterial 虛線的基礎(chǔ)材質(zhì)
MeshBasicMaterial 網(wǎng)格基礎(chǔ)材質(zhì)
MeshDepthMaterial 網(wǎng)格深度材質(zhì) 根據(jù)網(wǎng)格到相機(jī)距離 染色
材質(zhì)修改
mesh.material = newMaterial;
三、Light
全局光:
THREE.AmbientLight嵌莉,影響整個(gè)scene的光源进萄,一般是為了弱化陰影或調(diào)整整體色調(diào),可設(shè)置光照顏色锐峭,以顏色的明度確定光源亮度 平行光:
THREE.DirectionalLight中鼠,模擬類似太陽(yáng)的光源,所有被照射的區(qū)域亮度是一致的沿癞,可設(shè)置光照顏色援雇、光照方向(通過(guò)向量確定方向),以顏色的明度確定光源亮度 點(diǎn)光源:
THREE.PointLight:?jiǎn)吸c(diǎn)發(fā)光椎扬,照射所有方向惫搏,可設(shè)置光照強(qiáng)度,光照半徑和光顏色
四蚕涤、渲染器 renderer
渲染器決定了渲染的結(jié)果應(yīng)該畫在元素的什么元素上面筐赔,并且以怎樣的方式來(lái)繪制。
Threejs中提供了很多的渲染方式揖铜,主要介紹 CanvasRenderer 茴丰、WebGLRenderer兩種。
注:CanvasRenderer 和 WebGLRenderer 都是使用HTML5的 <canvas> 直接內(nèi)嵌在網(wǎng)頁(yè)中天吓。Cavas渲染器中的“Canvas”表示使用Canvas 2d而不是WebGL贿肩。
(一)WebGLRenderer
WebGL渲染器使用WebGL來(lái)繪制場(chǎng)景(如果設(shè)備支持),使用WebGL能夠利用GPU硬件加速?gòu)亩岣咪秩拘阅?/p>
//開啟反鋸齒
var renderer = new THREE.WebGLRenderer({antialias: true});
由于在3D圖像中,受分辨的制約,物體邊緣總會(huì)或多或少的呈現(xiàn)三角形的鋸齒,而抗鋸齒就是指對(duì)圖像邊緣進(jìn)行柔化處理,使圖像邊緣看起來(lái)更平滑,更接近實(shí)物的物體龄寞。
(二)CanvasRenderer
Canvas渲染器不使用WebGL來(lái)繪制場(chǎng)景尸曼,使用相對(duì)較慢的Canvas 2D Context API。
var renderer = new THREE.CanvasRenderer();
在不確定瀏覽器是否支持WebGL渲染器的時(shí)候萄焦,可以通過(guò)以下代碼來(lái)實(shí)現(xiàn)渲染器的選擇:
function webglAvailable() {
try {
var canvas = document.createElement( 'canvas' );
return !!( window.WebGLRenderingContext && (canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ));
} catch ( e ) {
return false;
}
}
if ( webglAvailable() ) {
renderer = new THREE.WebGLRenderer();
} else {
renderer = new THREE.CanvasRenderer();
}
五、控制器
1.TransformControls
用來(lái)轉(zhuǎn)換 3D 空間中的對(duì)象。與其他控件不同拂封,它不用于變換場(chǎng)景的相機(jī)茬射。
//實(shí)例化
transformControl = new TransformControls(
camera,
renderer.domElement
);
//屬性更改可以添加事件偵聽器的單獨(dú)事件。事件類型是“propertyname-changed”冒签。
transformControl.addEventListener(
'dragging-changed',
function (event) {
controls.enabled = !event.value;
}
);
//受控 3D 對(duì)象發(fā)生更改在抛,則觸發(fā)。
transformControl.addEventListener('objectChange', () => {
this.isMove = true;
this.updateSplineOutline();
});
//何為受控
transformControl.attach(object);
transformControl.getRaycaster ()萧恕; //射線投射器 返回用于用戶交互的Raycaster對(duì)象
transformControl.setSize();//設(shè)置助手 UI 的大小刚梭。
propertyname有axis 、camera票唆、domElement朴读、enabled(是否啟用控件,在于其他操作沖突時(shí)可用) 走趋、object 衅金、showX 、size 簿煌、translationSnap
怎么讓 transformControl 助手 UI 的大小跟隨畫布縮放氮唯,這個(gè)問(wèn)題我還沒(méi)解決
2.OrbitControls
軌道控制允許相機(jī)圍繞目標(biāo)運(yùn)行,一般用于全景
//實(shí)例化
const controls = new OrbitControls(camera, renderer.domElement);
controls.damping = 0.2;
//當(dāng)相機(jī)被控件轉(zhuǎn)換時(shí)觸發(fā)姨伟。
controls.addEventListener('change', this.render);
3.TrackballControls
TrackballControls 類似于OrbitControls惩琉。但是,它不保持恒定的相機(jī)向上矢量夺荒。這意味著如果相機(jī)圍繞“北極”和“南極”運(yùn)行瞒渠,它不會(huì)翻轉(zhuǎn)以保持“正面朝上”。
TrackballControls可設(shè)置參數(shù)比較多般堆,意味著功能會(huì)比OrbitControls更強(qiáng)大
4.DragControls
dragControsl可以用來(lái)拖動(dòng)Group需要設(shè)置 dragControls.transformGroup = true;
let draggableObjects = dragControls.getObjects();
//draggableObjects為只讀屬性在孝,所以只能通過(guò)設(shè)置length = 0來(lái)刪除之前操作對(duì)象
draggableObjects.length = 0;
draggableObjects.push(group);
當(dāng)對(duì)象被移除scene后,也需要從draggableObjects里移除該對(duì)象
dragControls = new DragControls(
curbeObj.children,
this.camera,
this.renderer.domElement
);
//移入模型
dragControls.addEventListener('hoveron', () => {
//選中模型
this.orbitControls.enabled = false; // 關(guān)閉orbitControls 控制器
});
//移出模型
dragControls.addEventListener('hoveroff', () => {
//選中模型
this.orbitControls.enabled = true; // 關(guān)閉orbitControls 控制器
});
//拖拽啟動(dòng)
dragControls.addEventListener('dragstart', (event) => {
this.transformControl.attach(event.object);//將選中對(duì)象綁定transformControl
console.log(event.object);
});
//拖過(guò)程
dragControls.addEventListener('drag', () => {
this.render();//
});
//拖完
dragControls.addEventListener('dragend', () => {
this.transformControl.detach();//解除transformControl綁定
// event.object.material.emissive.set(0x000000);
});
5.MapControls
地圖控件淮摔,如果查看類似地圖模型或者不希望用戶對(duì)模型進(jìn)行反轉(zhuǎn)的時(shí)候可以使用這個(gè)控件私沮。
6.CameraControls
優(yōu)勢(shì):放大縮小,鼠標(biāo)點(diǎn)擊位置不變
export default class CameraControl {
private readonly cameraControls: CameraControls;
constructor(renderer: THREE.WebGL1Renderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera, dom: HTMLElement) {
this.cameraControls = new CameraControls(camera, dom);
this.init(renderer, scene, camera);
}
enable() {
this.cameraControls.enabled = true;
}
disable() {
this.cameraControls.enabled = false;
}
private init(renderer: THREE.WebGL1Renderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera): void {
this.cameraControls.dollyToCursor = true;
this.cameraControls.mouseButtons.left = CameraControls.ACTION.TRUCK;
this.cameraControls.mouseButtons.right = CameraControls.ACTION.TRUCK;
// default disable this control
this.cameraControls.enabled = false;
const clock = new THREE.Clock();
let anim = () => {
const delta = clock.getDelta();
const hasControlsUpdated = this.cameraControls.update(delta);
requestAnimationFrame(anim);
if (hasControlsUpdated) {
renderer.render(scene, camera);
}
};
anim();
}
}
以上控件不再需要時(shí)需要調(diào)用dispose () 方法處理掉
六和橙、鼠標(biāo)選取物體
1.使用GPU選取物體
創(chuàng)建選取材質(zhì)仔燕,將場(chǎng)景中的每個(gè)模型的材質(zhì)替換成不同的顏色。
讀取鼠標(biāo)位置像素顏色魔招,根據(jù)顏色判斷鼠標(biāo)位置的物體晰搀。
pick() {
//render the picking scene off-screen
// set the view offset to represent just a single pixel under the mouse
//window.devicePixelRatio是設(shè)備上物理像素和設(shè)備獨(dú)立像素(device-independent pixels (dips))的比例。
this.camera.setViewOffset(
this.renderer.domElement.width,
this.renderer.domElement.height,
(hoverPosition.x * window.devicePixelRatio) | 0,
(hoverPosition.y * window.devicePixelRatio) | 0,
1,
1
);
// console.log(camera);
// render the scene
//指定下面渲染所在位置為 pickingTexture办斑,不填默認(rèn)為cavans畫布
this.renderer.setRenderTarget(this.pickingTexture);
this.renderer.render(this.pickingScene, this.camera);
// clear the view offset so rendering returns to normal
// console.log(renderer);
this.camera.clearViewOffset();
//create buffer for reading single pixel
const pixelBuffer = new Uint8Array(4);
// console.log(pickingTexture);
//read the pixel
//從 renderTarget 讀取像素?cái)?shù)據(jù)到你傳入的緩沖區(qū)中外恕。這是一個(gè)圍繞WebGLRenderingContext.readPixels ()的包裝器杆逗。
this.renderer.readRenderTargetPixels(
this.pickingTexture,
0,
0,
1,
1,
pixelBuffer
);
// if (pixelBuffer[0] !== 0)
console.log(pixelBuffer);
//interpret the pixel as an ID
const id =
(pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | pixelBuffer[2];
const data = pickingData[id];
if (data) {
//move our highlightBox so that it surrounds the picked object
if (data.position) {
// console.log(data.position);
highlightBox.position.copy(data.position);
// highlightBox.rotation.copy(data.rotation);
highlightBox.visible = true;
// console.log(highlightBox);
}
} else {
highlightBox.visible = false;
}
}
2.光線投射法
將鼠標(biāo)在屏幕上的位置轉(zhuǎn)化為標(biāo)準(zhǔn)設(shè)備坐標(biāo),與camera連接形成一條射線鳞疲,射線穿過(guò)Mesh即為選中的目標(biāo)罪郊。
此法缺點(diǎn):當(dāng)模型非常大,比如說(shuō)有40萬(wàn)個(gè)面尚洽,通過(guò)遍歷的方法選取物體和計(jì)算碰撞點(diǎn)位置將非常慢悔橄,用戶體驗(yàn)不好。
let { clientWidth, clientHeight } = this.container;
pointer.x = (event.offsetX / clientWidth) * 2 - 1;
pointer.y = -(event.offsetY / clientHeight) * 2 + 1;
raycaster.setFromCamera(pointer, this.camera);
const curbIntersects = raycaster.intersectObjects(
curbeObj.children
);
return curbIntersects.length == 0 ? null : curbIntersects[0].object;
參考用three.js開發(fā)三維地圖實(shí)例腺毫、Three.js 拾取之GPU Picking的理解和思考
七癣疟、性能優(yōu)化
刪除模型對(duì)象(.remove()
和·dispose()
方法)
remove / removeFromParent
一個(gè)網(wǎng)格模型Mesh是包含幾何體geometry和材質(zhì)對(duì)象Material的,幾何體geometry本質(zhì)上就是頂點(diǎn)數(shù)據(jù)潮酒,Three.js通過(guò)WebGL渲染器解析幾何體的時(shí)候會(huì)調(diào)用WebGL API創(chuàng)建頂點(diǎn)緩沖區(qū)來(lái)存儲(chǔ)頂點(diǎn)數(shù)據(jù)睛挚。
如果僅僅執(zhí)行scene.remove(Mesh)
只是把網(wǎng)格模型從場(chǎng)景對(duì)象的.children
屬性中刪除,解析網(wǎng)格模型Mesh幾何體的頂點(diǎn)數(shù)據(jù)通過(guò)WebGL API創(chuàng)建的頂點(diǎn)緩沖區(qū)占用的內(nèi)存并不會(huì)釋放澈灼。
處理材質(zhì)對(duì)象. 材質(zhì)的紋理不能得到處理. 材質(zhì)的紋理需要通過(guò)紋理對(duì)象Texture的dispose方法實(shí)現(xiàn)
Material·dispose()
刪除場(chǎng)景對(duì)象中Scene
一個(gè)子對(duì)象Group
竞川,并釋放組對(duì)象Group
中所有網(wǎng)格模型幾何體的頂點(diǎn)緩沖區(qū)占用內(nèi)存
// 遞歸遍歷組對(duì)象group釋放所有后代網(wǎng)格模型綁定幾何體占用內(nèi)存
group.traverse(function(obj) {
if (obj.type === 'Mesh') {
obj.geometry.dispose();
obj.material.dispose();
}
})
// 刪除場(chǎng)景對(duì)象scene的子對(duì)象group
scene.remove(group);
八、ThreeJS 三種坐標(biāo)系
1.分類
-
世界坐標(biāo)(右手坐標(biāo))
世界坐標(biāo)系默認(rèn)就是對(duì)Threejs整個(gè)場(chǎng)景Scene建立一個(gè)坐標(biāo)系
- 屏幕坐標(biāo)系
ThreeJS 是使用了 canvas
畫布繪制圖形的叁熔,因此屏幕坐標(biāo)系就是 canvas
中的坐標(biāo)系委乌,也就是左上角是坐標(biāo)原點(diǎn):
-
標(biāo)準(zhǔn)設(shè)備坐標(biāo)系
標(biāo)準(zhǔn)設(shè)備坐標(biāo)系是三維的,其原點(diǎn)默認(rèn)在屏幕中心荣回,且
x y z
的范圍是[-1,1]
遭贸,因此其x
、y
軸在屏幕坐標(biāo)系中的表示就是:
2.坐標(biāo)系轉(zhuǎn)換
(1)屏幕坐標(biāo)轉(zhuǎn)世界坐標(biāo)
屏幕坐標(biāo)轉(zhuǎn)空間坐標(biāo)需要經(jīng)過(guò)兩個(gè)步驟:屏幕坐標(biāo) -> 標(biāo)準(zhǔn)設(shè)備坐標(biāo) -> 世界坐標(biāo)
設(shè)屏幕一點(diǎn)A(x,y)心软,A點(diǎn)對(duì)應(yīng)標(biāo)準(zhǔn)設(shè)備中點(diǎn)O點(diǎn)A^的坐標(biāo)為(x1,y1)壕吹,可得兩坐標(biāo)之間關(guān)系為 x1 = x - width / 2, y1 = - (y - height / 2)
再標(biāo)準(zhǔn)化到[-1,1]之間删铃,即得A對(duì)應(yīng)標(biāo)準(zhǔn)設(shè)備坐標(biāo)系中的點(diǎn)坐標(biāo)( x1 / (width / 2) 耳贬, - (y - height / 2) / height / 2 )
然后,再通過(guò) Vector3.unproject(camera)
方法將標(biāo)準(zhǔn)設(shè)備坐標(biāo)轉(zhuǎn)為世界坐標(biāo):
代碼如下:
const x = event.clientX;//鼠標(biāo)單擊坐標(biāo)X
const y = event.clientY;//鼠標(biāo)單擊坐標(biāo)Y
// 屏幕坐標(biāo)轉(zhuǎn)標(biāo)準(zhǔn)設(shè)備坐標(biāo)
const x1 = ( x / window.innerWidth ) * 2 - 1;
const y1 = -( y / window.innerHeight ) * 2 + 1;
//標(biāo)準(zhǔn)設(shè)備坐標(biāo)(z=0.5這個(gè)值并沒(méi)有一個(gè)具體的說(shuō)法)
const stdVector = new Vector3(x, y, 0.5);
const worldVector = stdVector.unproject(camera);
(2)世界坐標(biāo)轉(zhuǎn)屏幕坐標(biāo)
屏幕坐標(biāo)轉(zhuǎn)空間坐標(biāo)需要經(jīng)過(guò)兩個(gè)步驟:世界> 標(biāo)準(zhǔn)設(shè)備坐標(biāo) -> 屏幕坐標(biāo)
先將世界坐標(biāo)系使用 project
方法轉(zhuǎn)換到標(biāo)準(zhǔn)設(shè)備坐標(biāo)系猎唁,再轉(zhuǎn)換到屏幕坐標(biāo)系中:
const standardVec = worldVector.project(camera);
//世界坐標(biāo)轉(zhuǎn)換屏幕坐標(biāo)偏差問(wèn)題
function wordPosToScreen(object,camera) {
var vector = new THREE.Vector3();
var widthHalf = 0.5 * window.innerWidth;
var heightHalf = 0.5 * window.innerHeight;
object.updateMatrixWorld(); /*這段代碼是重要的在獲取前先更新下對(duì)象的世界坐標(biāo)/世界矩陣*/
vector.setFromMatrixPosition(object.matrixWorld);
vector.project(camera);
vector.x = (vector.x * widthHalf) + widthHalf;
vector.y = -(vector.y * heightHalf) + heightHalf;
return {
x: vector.x,
y: vector.y
};
}
九咒劲、邊緣庫(kù) stats.js
this.stats = new Stats();
this.stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
document.body.appendChild(this.stats.dom);
......
animate() {
// this.stats.begin();
this.stats.update();
..............
// this.stats.end();
requestAnimationFrame( animate );
}
最后一秒渲染的FPS幀數(shù)。數(shù)字越高越好诫隅。
MS毫秒需要渲染的幀腐魂。數(shù)字越小越好。
MB已分配內(nèi)存的MB逐纬。(用 運(yùn)行 Chrome
--enable-precise-memory-info
)
十蛔屹、ThreeJS里的一些方法
計(jì)算a向量到所傳入的v間的距離a.distanceTo ( v : Vector3 ) : Float
利用ShapeGeometry形成的mesh設(shè)置mesh的position屬性不是中心點(diǎn),mesh旋轉(zhuǎn)中心也不是自己的中心點(diǎn),怎么解決這個(gè)問(wèn)題豁生?
如何將mesh的position對(duì)準(zhǔn)mesh中心點(diǎn)
//必須先調(diào)用computeBoundingBox方法計(jì)算
scene.children[0].geometry.computeBoundingBox();
// 把對(duì)象放到坐標(biāo)原點(diǎn)
scene.children[0].geometry.center();
如何解決旋轉(zhuǎn)中心不是中心點(diǎn)問(wèn)題
let center = new THREE.Vector3();
scene.children[0].geometry.computeBoundingBox();
scene.children[0].geometry.boundingBox.getCenter(center);
let x = center.x;
let y = center.y;
let z = center.z;
// 把對(duì)象放到坐標(biāo)原點(diǎn)
scene.children[0].geometry.center();
// 繞軸旋轉(zhuǎn)
scene.children[0].rotation.z = Date.now() * 0.001;
// 再把對(duì)象放回原來(lái)的地方
scene.children[0].geometry.translate(x, y, z);
//最后調(diào)用render
查看某對(duì)象是否在group里
group.children.includes( object ) === true
ThreeJS bufferGeometry位置屬性在應(yīng)用轉(zhuǎn)換時(shí)不更新
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(10, 10, 10);
mesh.rotation.set(- Math.PI/2, 0, 0);
mesh.scale.set(1, 1, 1);
scene.add(mesh);
mesh.updateMatrix(); // make sure the mesh's matrix is updated
var vec = new THREE.Vector3();
var attribute = mesh.geometry.attributes.position; // we want the position data
var index = 1; // index is zero-based, so this the the 2nd vertex
vec.fromAttribute(attribute, index); // extract the x,y,z coordinates
vec.applyMatrix4(mesh.matrix); // apply the mesh's matrix transform
由點(diǎn)連成線兔毒,當(dāng)point作為在line的children時(shí)漫贞,使用dragControl平移了線,但是點(diǎn)的坐標(biāo)不變時(shí)育叁,使用下面方法
for (let i = 0; i < obj.userData.positions.length; i++) {
vec = new THREE.Vector3();
attribute = obj.geometry.attributes.position; // 我們想要位置數(shù)據(jù)
vec.fromBufferAttribute(attribute, i); // 提取x绕辖,y,z坐標(biāo)
vec.applyMatrix4(obj.matrix); // 應(yīng)用網(wǎng)格的矩陣變換
children[i].position.copy(vec);
pointLists.push(vec);
}
十一擂红、Three.js的渲染機(jī)制
renderer = new THREE.WebGLRenderer({ antialias: true }),
antialias - 是否執(zhí)行抗鋸齒。默認(rèn)為false围小。
three的渲染器是基于webGL的昵骤。它的渲染機(jī)制是根據(jù)物體離照相機(jī)的距離來(lái)控制和進(jìn)行渲染的。對(duì)于透明的物體肯适,則是按照從最遠(yuǎn)到最近的順序進(jìn)行渲染变秦。也就是說(shuō),它根據(jù)物體的空間位置進(jìn)行排序框舔,然后根據(jù)這個(gè)順序來(lái)渲染物體蹦玫。
1.renderer.sortObjects = false;
物體的渲染順序?qū)?huì)由他們添加到場(chǎng)景中的順序所決定。適合大部分場(chǎng)景刘绣。
2.renderer.sortObjects = true;
并且給特定的物體設(shè)置object.renderOrder 指定它的渲染順序樱溉。
3.material1.depthWrite = false;
對(duì)于透明物體
如果發(fā)現(xiàn)場(chǎng)景中的透明物體顯示有問(wèn)題,例如旋轉(zhuǎn)攝像機(jī)的時(shí)候會(huì)出現(xiàn)閃爍等問(wèn)題纬凤「U辏可以嘗試以下幾種方法:
1.設(shè)置
material.transparent = false;1
注意:不透明物體將會(huì)優(yōu)先渲染。所以這也可以作為控制渲染順序的一個(gè)方法停士。
2.設(shè)置
material.alphaTest = 0.1;1
自己嘗試改變不同的alpha測(cè)試值挖帘,以適合你自己的場(chǎng)景。 3.嘗試改變sortObject和depthWrite的值等等恋技。
十二拇舀、WebGLRenderTarge
WebGLRenderTarget,它是一個(gè)緩沖蜻底,就是在這個(gè)緩沖中骄崩,視頻卡為正在后臺(tái)渲染的場(chǎng)景繪制像素。 它用于不同的效果朱躺,例如把它做為貼圖使用或者圖像后期處理刁赖。
WebGLRenderTarget的構(gòu)造器有三個(gè)參數(shù),分別是width长搀,height和options宇弛。寬高就是RenderTarget的高,設(shè)置的同時(shí)也會(huì)把它們賦值給texture.image的width和height屬性源请。
WebGLRenderTarget的屬性有
width | 渲染目標(biāo)寬度 |
---|---|
height | 渲染目標(biāo)高度 |
scissor | 渲染目標(biāo)視口內(nèi)的一個(gè)矩形區(qū)域枪芒,區(qū)域之外的片元將會(huì)被丟棄 |
scissorTest | 表明是否激活了剪裁測(cè)試 |
viewport | 渲染目標(biāo)的視口 |
texture | 紋理實(shí)例保存這渲染的像素彻况,用作進(jìn)一步處理的輸入值 |
depthBuffer | 渲染到深度緩沖區(qū)。默認(rèn)true |
stencilBuffer | 渲染到模具緩沖區(qū)舅踪。默認(rèn)false |
depthTexture | 如果設(shè)置纽甘,那么場(chǎng)景的深度將會(huì)被渲染到此紋理上。默認(rèn)是null |
WebGLRenderTarget的方法
方法 | 描述 |
---|---|
setSize | 設(shè)置渲染目標(biāo)的大小 |
clone | 創(chuàng)建一個(gè)渲染目標(biāo)副本 |
copy | 采用傳入的渲染目標(biāo)的設(shè)置 |
dispose | 發(fā)出一個(gè)處理事件 |
十三抽碌、Three.js使用dat.GUI簡(jiǎn)化試驗(yàn)流程
使用這個(gè)插件的最省事的地方在于悍赢,調(diào)試很方便的調(diào)節(jié)相關(guān)的值,從而影響最后繪制的結(jié)果货徙。而dat.GUI實(shí)現(xiàn)的東西也很簡(jiǎn)單左权,理解起來(lái)也很好理解。
最后附上之前的參考案例:
通過(guò)鼠標(biāo)點(diǎn)擊平面實(shí)現(xiàn)任意畫線功能
ThreeJS借助插件可以加載的文件類型