使用 BufferGeometry 能夠有效減小向 GPU 傳輸幾何體相關數據所需的開銷html
能夠自定義頂點位置, 面片索引, 法向量, 顏色值數組
在以前的學習中, 我是已經瞭解到創建一個3d場景, 不知道屏幕前的你是否有了解到, threejs須要作的有, 第一: 渲染器renderer; 第二: 場景Scene; 第三, 光源Light; 第四, 物質有點線面三個部分.app
在實際的開發過程當中, 本身建立幾何體這種狀況不多見, 大部分狀況是載入已有的模型, 對模型進行操做, 導入的模型可能很大, 這個時候就須要優化, 優化能夠從幾何體入手, 也能夠從材質入手, 可是優化主要針對的就是幾何體, 佔內存的也是幾何體而不是材質, 所以瞭解幾何體是很是有必要的, 幾何體英文( geometry ).dom
對於Threejs, 官方說明, 使用buffergeometry可以有效減小向GPU傳輸幾何體相關數據所須要的開銷, 同時, 用戶能夠自定義集合體的頂點位置, 名片索引, 法向量, 顏色值ide
下面建立一個簡單的buffergeometry吧學習
// 頂點個數 var particles = 500000; var geometry = new THREE.BufferGeometry(); // 每一個頂點位置 let positions = []; // 顏色值 var colors = []; // 臨時顏色類型 var color = new THREE.Color(); var n = 1000, n2 = n / 2; for ( var i = 0; i < particles; i ++ ) { // positions, 造成一個長方體, x, y, z的範圍都是從-500到500, 造成的長方體的長寬高都爲500 var x = Math.random() * n - n2; var y = Math.random() * n - n2; var z = Math.random() * n - n2; positions.push( x, y, z ); // colors, 設置顏色, 同理, 從0到1 var vx = ( x / n ) + 0.5; var vy = ( y / n ) + 0.5; var vz = ( z / n ) + 0.5; color.setRGB( vx, vy, vz ); colors.push( color.r, color.g, color.b ); } // 設置位置信息 geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) ); // 設置顏色信息 geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) ); // 計算邊界球體 geometry.computeBoundingSphere(); // 建立物資 var material = new THREE.PointsMaterial( { size: 15, vertexColors: true } ); // 建立點雲 points = new THREE.Points( geometry, material ); scene.add( points );
效果以下圖所示優化
下面對代碼進行簡單的分析, 並進行彙總ui
代碼主要分爲三步3d
根據代碼, 將建好的點雲加入場景中, 就有效果了, 完整代碼附在文章末尾處code
threejs給咱們提供了一些能夠直接引用的方法下降GPU渲染幾何體的開銷, 這裏展現官方給的3種類型的代碼
裏面第一行代碼是用於計算mesh在GPU中所佔內存
// 計算這個mesh在gpu中所佔內存 BufferGeometryUtils.estimateBytesUsed( mesh.geometry ) + " bytes" // 使用DefaultUVEncoding下降內存數 GeometryCompressionUtils.compressUvs( mesh ); // 使用QuantizePosEncoding下降內存數 GeometryCompressionUtils.compressPositions( mesh ); // 使用NormEncodingMethods下降內存數 // [ "None", "DEFAULT", "OCT1Byte", "OCT2Byte", "ANGLES" ] GeometryCompressionUtils.compressNormals( mesh, 'None' );
var geometry = new THREE.BufferGeometry(); var material = new THREE.LineBasicMaterial( { vertexColors: true, morphTargets: true } ); var positions = []; var colors = []; for ( var i = 0; i < segments; i ++ ) { var x = Math.random() * r - r / 2; var y = Math.random() * r - r / 2; var z = Math.random() * r - r / 2; // positions positions.push( x, y, z ); // colors colors.push( ( x / r ) + 0.5 ); colors.push( ( y / r ) + 0.5 ); colors.push( ( z / r ) + 0.5 ); } geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) ); geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) ); geometry.computeBoundingSphere(); line = new THREE.Line( geometry, material ); scene.add( line );
效果圖以下
是否是以爲這個代碼與第一章節的代碼十分相似呢, 實際上就是徹底同樣的代碼
不一樣點在於
有了建立點幾何體的知識, 就能建立線幾何體
// 點數 var triangles = 160000; var geometry = new THREE.BufferGeometry(); var positions = []; // 點的法向量 var normals = []; var colors = []; var color = new THREE.Color(); // 正方體, 長寬高都爲800 var n = 800, n2 = n / 2; // 三角形三個點點在正方體內, 這個正方體長寬高都爲12 var d = 12, d2 = d / 2; // abc, 三個頂點位置 var pA = new THREE.Vector3(); var pB = new THREE.Vector3(); var pC = new THREE.Vector3(); // c點到b點的方向向量 var cb = new THREE.Vector3(); // a點到b點的方向向量 var ab = new THREE.Vector3(); for ( var i = 0; i < triangles; i ++ ) { // positions var x = Math.random() * n - n2; var y = Math.random() * n - n2; var z = Math.random() * n - n2; var ax = x + Math.random() * d - d2; var ay = y + Math.random() * d - d2; var az = z + Math.random() * d - d2; var bx = x + Math.random() * d - d2; var by = y + Math.random() * d - d2; var bz = z + Math.random() * d - d2; var cx = x + Math.random() * d - d2; var cy = y + Math.random() * d - d2; var cz = z + Math.random() * d - d2; // 添加一個三角形的3個頂點, 每一個頂點有xyz三個數據 positions.push( ax, ay, az ); positions.push( bx, by, bz ); positions.push( cx, cy, cz ); // 求法向量, 首先設置三角形的三個頂點 pA.set( ax, ay, az ); pB.set( bx, by, bz ); pC.set( cx, cy, cz ); // 求出兩個方向向量 cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); // 叉積, 求法向量 cb.cross( ab ); // 單位化這個法向量 cb.normalize(); var nx = cb.x; var ny = cb.y; var nz = cb.z; // 添加法向量到法向量數組中 // 三角形的三個頂點的法向量相同, 所以複製三份 normals.push( nx, ny, nz ); normals.push( nx, ny, nz ); normals.push( nx, ny, nz ); // colors var vx = ( x / n ) + 0.5; var vy = ( y / n ) + 0.5; var vz = ( z / n ) + 0.5; color.setRGB( vx, vy, vz ); colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); } // 加入位置信息 geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ).onUpload( disposeArray ) ); // 加入法向量信息 geometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ).onUpload( disposeArray ) ); // 加入顏色信息 geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ).onUpload( disposeArray ) ); geometry.computeBoundingSphere(); var material = new THREE.MeshPhongMaterial( { color: 0xaaaaaa, specular: 0xffffff, shininess: 250, side: THREE.DoubleSide, vertexColors: true } ); mesh = new THREE.Mesh( geometry, material ); scene.add( mesh );
效果圖以下
面幾何體與前兩種幾何體很大的不一樣在於, 面幾何體須要法向量信息
在代碼中我也是添加了不少註釋便於理解, 這裏我再大體解釋一下
已知三個點, 求出兩條邊的方向向量, 這兩個方向向量作叉乘, 結果變爲由三個點構成的三角形的法向量
由點到線, 由線到面, 但願讀者本身能夠模仿寫出來
<!DOCTYPE html> <html lang="ch"> <head> <meta charset="UTF-8"> <title>Title</title> <style> body{ margin: 0; overflow: hidden; } </style> </head> <body> <div id="container"> </div> <script type="module"> import * as THREE from '../build/three.module.js'; import {OrbitControls} from "./jsm/controls/OrbitControls.js"; import {GUI} from "./jsm/libs/dat.gui.module.js"; let container, camera, scene, renderer, stats; let points; init(); animation(); function init() { scene = new THREE.Scene(); scene.background = new THREE.Color(0x050505); scene.fog = new THREE.Fog(0x050505, 2000, 3000); scene.add(new THREE.AmbientLight(0x8FBCD4, 0.4)); container = document.getElementById('container'); camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); camera.position.z = 2750; scene.add(camera); let particles = 500000; let geometry = new THREE.BufferGeometry(); let positions = []; let colors = []; let color = new THREE.Color(); let n = 1000, n2 = n / 2; for (let i = 0; i < particles; i++) { let x = Math.random() * n - n2; let y = Math.random() * n - n2; let z = Math.random() * n - n2; positions.push(x, y, z); let vx = (x / n) + 0.5; let vy = (y / n) + 0.5; let vz = (z / n) + 0.5; color.setRGB(vx, vy, vz); colors.push(color.r, color.g, color.b); } geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); geometry.computeBoundingSphere(); let material = new THREE.PointsMaterial({size:15, vertexColors: true}); points = new THREE.Points(geometry, material); scene.add(points); // let pointLight = new THREE.PointLight(0xffffff, 1); // // 燈跟着相機走, 效果不錯 // camera.add(pointLight); scene.add(new THREE.AxesHelper(5)); renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement); let controls = new OrbitControls(camera, renderer.domElement); controls.enabledZoom = false; window.addEventListener('resize', onWindowResize, false); } function animation(){ render(); requestAnimationFrame(animation); } function render() { let time = Date.now() * 0.001; points.rotation.x = time * 0.25; points.rotation.y = time * 0.5; renderer.render(scene, camera); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } </script> </body> </html>