threejs好学吗(ThreeJS:几何体相关BufferGeometry)

使用 BufferGeometry 可以有效减少向 GPU 传输几何体相关数据所需的开销

可以自定义顶点位置, 面片索引, 法向量, 颜色值

threejs好学吗(ThreeJS:几何体相关BufferGeometry)(1)

作者:随遇丿而安

出处:https://www.cnblogs.com/xiaxiangx/p/13849204.html

1. BufferGeometry使用初体验

在之前的学习中, 我是已经了解到建立一个3d场景, 不知道屏幕前的你是否有了解到, threejs需要做的有, 第一: 渲染器renderer; 第二: 场景Scene; 第三, 光源Light; 第四, 物质有点线面三个部分.

在实际的开发过程中, 自己创建几何体这种情况很少见, 大部分情况是载入已有的模型, 对模型进行操作, 导入的模型可能很大, 这个时候就需要优化, 优化可以从几何体入手, 也可以从材质入手, 但是优化主要针对的就是几何体, 占内存的也是几何体而不是材质, 因此了解几何体是非常有必要的, 几何体英文( geometry ).

对于Threejs, 官方说明, 使用buffergeometry能够有效减少向GPU传输几何体相关数据所需要的开销, 同时, 用户可以自定义集合体的顶点位置, 名片索引, 法向量, 颜色值

下面创建一个简单的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 );

效果如下图所示

threejs好学吗(ThreeJS:几何体相关BufferGeometry)(2)

下面对代码进行简单的分析, 并进行汇总

代码主要分为三步

  1. 创建所有点的位置数组, 每三个值形成x, y, z确定三维世界点的坐标对应positions = [],positions.push()
  2. 创建所有点的颜色数组, 每三个值形成r, g, b确定三维世界点的颜色colors = []colors.push()
  3. 将位置数组和颜色数组导入到集合体中geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );

根据代码, 将建好的点云加入场景中, 就有效果了, 完整代码附在文章末尾处

2. 简单压缩几何体的方法

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' );

3. 创建由点到线的几何体

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 );

效果图如下

threejs好学吗(ThreeJS:几何体相关BufferGeometry)(3)

是不是觉得这个代码与第一章节的代码十分类似呢, 实际上就是完全一样的代码

不同点在于

  1. 线几何体的 material 是THREE.LineBasicMaterial
  2. 创建线几何体mesh使用的是 THREE.Line, 而点云使用的是THREE.Points

有了创建点几何体的知识, 就能创建线几何体

4. 创建由线到面的几何体

// 点数 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 );

效果图如下

threejs好学吗(ThreeJS:几何体相关BufferGeometry)(4)

面几何体与前两种几何体很大的不同在于, 面几何体需要法向量信息

在代码中我也是添加了很多注释便于理解, 这里我再大致解释一下

已知三个点, 求出两条边的方向向量, 这两个方向向量做叉乘, 结果变为由三个点构成的三角形的法向量

5. 创建点云的源码

由点到线, 由线到面, 希望读者自己可以模仿写出来

<!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>

作者:随遇丿而安

出处:https://www.cnblogs.com/xiaxiangx/p/13849204.html

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页