ThreeJS總結(jié)

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ī)纫事。


image-20210908161550605.png
//透視投影相機(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 })
);

BoxBufferGeometrySphereBufferGeometry可以分別用來(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):

image-20210117192758975
  • 標(biāo)準(zhǔn)設(shè)備坐標(biāo)系

    標(biāo)準(zhǔn)設(shè)備坐標(biāo)系是三維的,其原點(diǎn)默認(rèn)在屏幕中心荣回,且 x y z 的范圍是 [-1,1]遭贸,因此其 xy 軸在屏幕坐標(biāo)系中的表示就是:

image-20210117193755299

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

image-20210908174945218.png

十蛔屹、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借助插件可以加載的文件類型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痴颊,一起剝皮案震驚了整個(gè)濱河市赏迟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蠢棱,老刑警劉巖锌杀,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異泻仙,居然都是意外死亡糕再,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門饰豺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)亿鲜,“玉大人,你說(shuō)我怎么就攤上這事冤吨≥锪” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵漩蟆,是天一觀的道長(zhǎng)垒探。 經(jīng)常有香客問(wèn)我,道長(zhǎng)怠李,這世上最難降的妖魔是什么圾叼? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮捺癞,結(jié)果婚禮上夷蚊,老公的妹妹穿的比我還像新娘。我一直安慰自己髓介,他們只是感情好惕鼓,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唐础,像睡著了一般箱歧。 火紅的嫁衣襯著肌膚如雪矾飞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天呀邢,我揣著相機(jī)與錄音洒沦,去河邊找鬼。 笑死价淌,一個(gè)胖子當(dāng)著我的面吹牛申眼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝉衣,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼豺型,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了买乃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤钓辆,失蹤者是張志新(化名)和其女友劉穎剪验,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體前联,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡功戚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了似嗤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啸臀。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烁落,靈堂內(nèi)的尸體忽然破棺而出乘粒,到底是詐尸還是另有隱情,我是刑警寧澤伤塌,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布灯萍,位于F島的核電站,受9級(jí)特大地震影響每聪,放射性物質(zhì)發(fā)生泄漏旦棉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一药薯、第九天 我趴在偏房一處隱蔽的房頂上張望绑洛。 院中可真熱鬧,春花似錦童本、人聲如沸真屯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)讨跟。三九已至纪他,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晾匠,已是汗流浹背茶袒。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凉馆,地道東北人薪寓。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像澜共,于是被迫代替她去往敵國(guó)和親向叉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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

  • 一嗦董、前言 ThreeJs 封裝了 WebGL 進(jìn)行渲染時(shí)所涉及到的相關(guān)概念母谎,如光照,材質(zhì)京革,紋理以及相機(jī)等奇唤。除此之外...
    仰簡(jiǎn)閱讀 7,262評(píng)論 1 10
  • 近年來(lái)web得到了快速的發(fā)展。隨著HTML5的普及匹摇,網(wǎng)頁(yè)的表現(xiàn)能力越來(lái)越強(qiáng)大咬扇。網(wǎng)頁(yè)上已經(jīng)可以做出很多復(fù)雜的動(dòng)畫,精...
    ayusong870閱讀 34,584評(píng)論 1 9
  • 馬上就要畢業(yè)了廊勃,實(shí)習(xí)進(jìn)入了一個(gè)GIS公司懈贺,任務(wù)要求使用Threejs寫個(gè)漸變的發(fā)光半球小特效,我完成后決定寫出這篇...
    gardenlike2閱讀 15,888評(píng)論 1 3
  • Three.js模型標(biāo)簽 在很多的實(shí)際的項(xiàng)目中坡垫,你可能需要給一個(gè)Three.js的模型添加標(biāo)簽梭灿,標(biāo)簽可以通過(guò)一個(gè)包...
    郭隆邦技術(shù)博客閱讀 3,403評(píng)論 0 1
  • 歡迎關(guān)注微信公號(hào)【三維網(wǎng)格3D】,第一時(shí)間獲取最新文章 CesiumJS是一個(gè)開源冰悠、免費(fèi)的三維地圖開發(fā)框架胎源,Thr...
    三維網(wǎng)格閱讀 8,527評(píng)論 2 9