`THREE.InterleavedBufferAttribute` 是 `three.js` 中的一种属性类型,用于在单个缓冲区中存储多个属性的数据。它允许高效地传输顶点数据到 GPU

demo案例

在这里插入图片描述

THREE.InterleavedBufferAttributethree.js 中的一种属性类型,用于在单个缓冲区中存储多个属性的数据。它允许高效地传输顶点数据到 GPU,并减少 WebGL 调用的数量。以下是关于 new THREE.InterleavedBufferAttribute( vertexBuffer, 3, 0 ) 的详细讲解,包括其入参、出参、方法和属性。

构造函数

const interleavedBufferAttribute = new THREE.InterleavedBufferAttribute(vertexBuffer, 3, 0);
入参
  1. vertexBufferTHREE.InterleavedBuffer):要绑定的 InterleavedBuffer 对象,包含了所有顶点属性的数据。
  2. itemSizenumber):每个顶点属性的大小。例如,3 表示一个三维向量(如位置)。
  3. offsetnumber):在 InterleavedBuffer 中此属性的起始位置的偏移量(以元素为单位,不是字节)。
出参

构造函数返回一个 THREE.InterleavedBufferAttribute 对象,用于在单个缓冲区中定义顶点属性的数据布局。

属性

  • array: 存储数据的 TypedArray,由 InterleavedBuffer 提供。
  • count: 此属性中的条目数量。
  • itemSize: 每个顶点属性的大小(例如,3 表示一个三维向量)。
  • offset: 在 InterleavedBuffer 中此属性的起始位置的偏移量。
  • data: 关联的 THREE.InterleavedBuffer 对象。
  • normalized: 如果为 true,表示属性的数据应该被归一化。
  • isInterleavedBufferAttribute: 标志此对象是一个 InterleavedBufferAttribute

方法

THREE.InterleavedBufferAttribute 继承自 THREE.BufferAttribute,因此它具有 BufferAttribute 的所有方法。以下是一些常用的方法:

  • getX(index): 获取给定索引处的 X 值。
  • getY(index): 获取给定索引处的 Y 值。
  • getZ(index): 获取给定索引处的 Z 值。
  • getW(index): 获取给定索引处的 W 值。
  • setX(index, x): 设置给定索引处的 X 值。
  • setY(index, y): 设置给定索引处的 Y 值。
  • setZ(index, z): 设置给定索引处的 Z 值。
  • setW(index, w): 设置给定索引处的 W 值。
  • setXYZ(index, x, y, z): 设置给定索引处的 X, Y 和 Z 值。
  • setXYZW(index, x, y, z, w): 设置给定索引处的 X, Y, Z 和 W 值。

使用示例

下面是一个简单的示例,展示如何创建 InterleavedBufferAttribute 并将其用于定义几何体的属性:

import * as THREE from 'three';

// 创建一个 InterleavedBuffer
const vertexData = new Float32Array([
  // 顶点数据 (x, y, z, u, v)
  -1,  1, 0, 0, 1,
   1,  1, 0, 1, 1,
  -1, -1, 0, 0, 0,
   1, -1, 0, 1, 0
]);
const vertexBuffer = new THREE.InterleavedBuffer(vertexData, 5); // 5 是每个顶点的数据元素数量 (x, y, z, u, v)

// 创建 InterleavedBufferAttribute 用于位置 (x, y, z)
const positions = new THREE.InterleavedBufferAttribute(vertexBuffer, 3, 0);

// 创建 InterleavedBufferAttribute 用于 UV 坐标 (u, v)
const uvs = new THREE.InterleavedBufferAttribute(vertexBuffer, 2, 3);

// 创建几何体并设置属性
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', positions);
geometry.setAttribute('uv', uvs);

// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide });

// 创建网格
const mesh = new THREE.Mesh(geometry, material);

// 创建场景并添加网格
const scene = new THREE.Scene();
scene.add(mesh);

// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;

// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 渲染循环
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}

animate();

说明

  • 在这个示例中,我们首先创建了一个 InterleavedBuffer,其中包含顶点数据,包括位置(x, y, z)和 UV 坐标(u, v)。
  • 然后,我们创建了两个 InterleavedBufferAttribute,一个用于位置,一个用于 UV 坐标,分别从 InterleavedBuffer 中提取数据。
  • 接着,我们创建一个 BufferGeometry 并将属性设置到几何体中。
  • 最后,我们创建一个 Mesh 并将其添加到场景中进行渲染。

通过这种方式,可以高效地将多个顶点属性存储在一个缓冲区中,从而减少 WebGL 调用的次数并提高渲染性能。

THREE.InstancedBufferGeometrythree.js 中的一个类,用于处理几何体的实例化渲染。实例化渲染是一种优化技术,可以高效地渲染大量相同几何体的副本。以下是关于 new THREE.InstancedBufferGeometry() 的详细讲解,包括其入参、出参、方法和属性。

构造函数

const instancedBufferGeometry = new THREE.InstancedBufferGeometry();
入参

THREE.InstancedBufferGeometry 构造函数不需要参数。它创建了一个新的实例化缓冲几何体对象。

出参

构造函数返回一个 THREE.InstancedBufferGeometry 对象,用于存储顶点和实例化属性的数据。

属性

  • attributes: 存储几何体的属性(如位置、法线、UV 坐标等)。
  • index: 存储几何体的索引数据(如果存在)。
  • groups: 用于定义几何体的不同部分,以便应用不同的材质。
  • boundingBox: 几何体的包围盒。
  • boundingSphere: 几何体的包围球。
  • drawRange: 控制几何体渲染的起始和结束顶点。
  • instanceCount: 实例化渲染的实例数量。默认值是 Infinity,通常需要根据具体情况设置。

方法

THREE.InstancedBufferGeometry 继承自 THREE.BufferGeometry,因此它具有 BufferGeometry 的所有方法。以下是一些常用的方法:

  • addAttribute(name, attribute): 添加一个属性到几何体中(已废弃,使用 setAttribute 替代)。
  • setAttribute(name, attribute): 设置几何体的属性。例如位置、法线、UV 坐标等。
  • getAttribute(name): 获取几何体的属性。
  • setIndex(index): 设置几何体的索引。
  • getIndex(): 获取几何体的索引。
  • addGroup(start, count, materialIndex): 为几何体添加一个组。
  • clearGroups(): 清除所有组。
  • setDrawRange(start, count): 设置几何体的绘制范围。
  • computeBoundingBox(): 计算几何体的包围盒。
  • computeBoundingSphere(): 计算几何体的包围球。
  • fromGeometry(geometry): 从 Geometry 对象生成一个 BufferGeometry 对象。
  • dispose(): 释放几何体的内存。

此外,InstancedBufferGeometry 还提供了一些特定的方法:

  • addAttribute(name, attribute, meshPerAttribute): 添加一个实例化属性。
  • setAttribute(name, attribute, meshPerAttribute): 设置一个实例化属性。
  • getAttribute(name): 获取一个实例化属性。

使用示例

下面是一个简单的使用示例,展示如何创建一个 InstancedBufferGeometry 并设置其属性来渲染多个实例化的立方体:

import * as THREE from 'three';

// 创建场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;

// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建几何体
const geometry = new THREE.InstancedBufferGeometry();

// 定义一个基本的立方体
const baseGeometry = new THREE.BoxGeometry(1, 1, 1);

// 将基本立方体的属性复制到实例化几何体中
geometry.attributes.position = baseGeometry.attributes.position;
geometry.index = baseGeometry.index;

// 创建一个实例化属性,用于存储每个实例的位置
const instanceCount = 100;
const offsets = new Float32Array(instanceCount * 3);

for (let i = 0; i < instanceCount; i++) {
  offsets[i * 3 + 0] = Math.random() * 10 - 5;
  offsets[i * 3 + 1] = Math.random() * 10 - 5;
  offsets[i * 3 + 2] = Math.random() * 10 - 5;
}

geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(offsets, 3));

// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });

// 创建实例化网格
const mesh = new THREE.InstancedMesh(geometry, material, instanceCount);

// 将网格添加到场景中
scene.add(mesh);

// 渲染循环
function animate() {
  requestAnimationFrame(animate);

  // 旋转网格
  mesh.rotation.x += 0.01;
  mesh.rotation.y += 0.01;

  renderer.render(scene, camera);
}

animate();

说明

  • 在这个示例中,我们首先创建了一个 InstancedBufferGeometry 对象,并将一个基本立方体的属性复制到实例化几何体中。
  • 然后,我们创建了一个实例化属性 offset,用于存储每个实例的位置。
  • 使用 THREE.InstancedMesh 来创建一个实例化网格,并将其添加到场景中进行渲染。

通过这种方式,我们可以高效地渲染大量相同的几何体实例,从而显著提升渲染性能。

以下是 three.js 示例代码,用于展示如何使用 indexed instancing 和 interleaved buffers 来渲染多个实例化的盒子:

<!DOCTYPE html>
<html lang="en">
<head>
	<title>three.js webgl - indexed instancing (single box), interleaved buffers</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>

	<div id="container"></div>
	<div id="info">
		<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - indexed instancing (single box), interleaved buffers
		<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
	</div>

	<script type="importmap">
		{
			"imports": {
				"three": "../build/three.module.js",
				"three/addons/": "./jsm/"
			}
		}
	</script>

	<script type="module">
		import * as THREE from 'three';

		import Stats from 'three/addons/libs/stats.module.js';

		let container, stats;
		let camera, scene, renderer, mesh;

		const instances = 5000;  // 实例数量
		let lastTime = 0;

		const moveQ = new THREE.Quaternion( 0.5, 0.5, 0.5, 0.0 ).normalize();
		const tmpQ = new THREE.Quaternion();
		const tmpM = new THREE.Matrix4();
		const currentM = new THREE.Matrix4();

		init();  // 初始化场景
		animate();  // 开始动画

		function init() {

			container = document.getElementById( 'container' );

			camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );

			scene = new THREE.Scene();
			scene.background = new THREE.Color( 0x101010 );

			// 创建几何体
			const geometry = new THREE.InstancedBufferGeometry();

			// 每个网格的数据 x,y,z,w,u,v,s,t 用于 4 元素对齐
			// 这里只使用 x, y, z 和 u, v; 但 x, y, z, nx, ny, nz, u, v 可能是一个好的布局
			const vertexBuffer = new THREE.InterleavedBuffer( new Float32Array( [
				// 前面
				- 1, 1, 1, 0, 0, 0, 0, 0,
				1, 1, 1, 0, 1, 0, 0, 0,
				- 1, - 1, 1, 0, 0, 1, 0, 0,
				1, - 1, 1, 0, 1, 1, 0, 0,
				// 后面
				1, 1, - 1, 0, 1, 0, 0, 0,
				- 1, 1, - 1, 0, 0, 0, 0, 0,
				1, - 1, - 1, 0, 1, 1, 0, 0,
				- 1, - 1, - 1, 0, 0, 1, 0, 0,
				// 左侧
				- 1, 1, - 1, 0, 1, 1, 0, 0,
				- 1, 1, 1, 0, 1, 0, 0, 0,
				- 1, - 1, - 1, 0, 0, 1, 0, 0,
				- 1, - 1, 1, 0, 0, 0, 0, 0,
				// 右侧
				1, 1, 1, 0, 1, 0, 0, 0,
				1, 1, - 1, 0, 1, 1, 0, 0,
				1, - 1, 1, 0, 0, 0, 0, 0,
				1, - 1, - 1, 0, 0, 1, 0, 0,
				// 顶部
				- 1, 1, 1, 0, 0, 0, 0, 0,
				1, 1, 1, 0, 1, 0, 0, 0,
				- 1, 1, - 1, 0, 0, 1, 0, 0,
				1, 1, - 1, 0, 1, 1, 0, 0,
				// 底部
				1, - 1, 1, 0, 1, 0, 0, 0,
				- 1, - 1, 1, 0, 0, 0, 0, 0,
				1, - 1, - 1, 0, 1, 1, 0, 0,
				- 1, - 1, - 1, 0, 0, 1, 0, 0
			] ), 8 );

			// 使用 vertexBuffer,从偏移 0 开始,位置属性有 3 个元素
			const positions = new THREE.InterleavedBufferAttribute( vertexBuffer, 3, 0 );
			geometry.setAttribute( 'position', positions );
			// 使用 vertexBuffer,从偏移 4 开始,uv 属性有 2 个元素
			const uvs = new THREE.InterleavedBufferAttribute( vertexBuffer, 2, 4 );
			geometry.setAttribute( 'uv', uvs );

			const indices = new Uint16Array( [
				0, 2, 1,
				2, 3, 1,
				4, 6, 5,
				6, 7, 5,
				8, 10, 9,
				10, 11, 9,
				12, 14, 13,
				14, 15, 13,
				16, 17, 18,
				18, 17, 19,
				20, 21, 22,
				22, 21, 23
			] );

			geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );

			// 创建材质
			const material = new THREE.MeshBasicMaterial();
			material.map = new THREE.TextureLoader().load( 'textures/crate.gif' );
			material.map.colorSpace = THREE.SRGBColorSpace;
			material.map.flipY = false;

			// 每个实例的数据
			const matrix = new THREE.Matrix4();
			const offset = new THREE.Vector3();
			const orientation = new THREE.Quaternion();
			const scale = new THREE.Vector3( 1, 1, 1 );
			let x, y, z, w;

			mesh = new THREE.InstancedMesh( geometry, material, instances );

			for ( let i = 0; i < instances; i ++ ) {

				// 设置偏移
				x = Math.random() * 100 - 50;
				y = Math.random() * 100 - 50;
				z = Math.random() * 100 - 50;

				offset.set( x, y, z ).normalize();
				offset.multiplyScalar( 5 ); // 至少 5 个单位从中心移动
				offset.set( x + offset.x, y + offset.y, z + offset.z );

				// 设置方向
				x = Math.random() * 2 - 1;
				y = Math.random() * 2 - 1;
				z = Math.random() * 2 - 1;
				w = Math.random() * 2 - 1;

				orientation.set( x, y, z, w ).normalize();

				matrix.compose( offset, orientation, scale );

				mesh.setMatrixAt( i, matrix );

			}

			scene.add( mesh );

			renderer = new THREE.WebGLRenderer();
			renderer.setPixelRatio( window.devicePixelRatio );
			renderer.setSize( window.innerWidth, window.innerHeight );
			container.appendChild( renderer.domElement );

			// 检查硬件是否支持实例化
			if ( renderer.capabilities.isWebGL2 === false && renderer.extensions.has( 'ANGLE_instanced_arrays' ) === false ) {
				document.getElementById( 'notSupported' ).style.display = '';
				return;
			}

			stats = new Stats();
			container.appendChild( stats.dom );

			window.addEventListener( 'resize', onWindowResize );

		}

		function onWindowResize() {
			camera.aspect = window.innerWidth / window.inner

Height;
			camera.updateProjectionMatrix();

			renderer.setSize( window.innerWidth, window.innerHeight );
		}

		function animate() {
			requestAnimationFrame( animate );

			render();
			stats.update();
		}

		function render() {
			const time = performance.now();

			mesh.rotation.y = time * 0.00005;

			const delta = ( time - lastTime ) / 5000;
			tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
			tmpM.makeRotationFromQuaternion( tmpQ );

			for ( let i = 0, il = instances; i < il; i ++ ) {
				mesh.getMatrixAt( i, currentM );
				currentM.multiply( tmpM );
				mesh.setMatrixAt( i, currentM );
			}

			mesh.instanceMatrix.needsUpdate = true;
			mesh.computeBoundingSphere();

			lastTime = time;

			renderer.render( scene, camera );
		}

	</script>

</body>
</html>

压图地址

  • 30
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值