11.three官方示例+编辑器+AI快速学习webgl_buffergeometry_glbufferattribute

本实例主要讲解内容

这个Three.js示例展示了如何使用**自定义WebGL缓冲区对象(VBO)**创建高性能的粒子系统。通过直接操作底层WebGL API创建和管理缓冲区,实现了粒子位置数据的动态切换,并结合drawRange技术优化渲染性能。

核心技术包括:

  • 直接使用WebGL API创建和管理VBO
  • 动态切换几何体属性数据
  • 基于时间的动画效果
  • 随机渲染粒子数量
    11.three官方示例+编辑器+AI快速学习webgl_buffergeometry_glbufferattribute_第1张图片

完整代码注释

DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - buffergeometry - custom VBOstitle>
		<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.jsa> webgl - buffergeometry - custom VBOsdiv>

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

			let points;

			const particles = 300000; // 粒子总数
			let drawCount = 10000; // 当前渲染的粒子数

			init();
			animate();

			function init() {

				container = document.getElementById( 'container' );

				// 初始化渲染器
				renderer = new THREE.WebGLRenderer();
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				renderer.setAnimationLoop( animate );

				container.appendChild( renderer.domElement );

				// 初始化相机
				camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 5, 3500 );
				camera.position.z = 2750;

				// 初始化场景
				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0x050505 );
				scene.fog = new THREE.Fog( 0x050505, 2000, 3500 );

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

				// 存储粒子位置和颜色的数组
				const positions = [];
				const positions2 = [];
				const colors = [];

				const color = new THREE.Color();

				// 粒子分布范围
				const n = 1000, n2 = n / 2;

				// 初始化所有粒子数据
				for ( let i = 0; i < particles; i ++ ) {

					// 随机位置
					const x = Math.random() * n - n2;
					const y = Math.random() * n - n2;
					const z = Math.random() * n - n2;

					positions.push( x, y, z );
					// 创建第二个位置数组,坐标值进行了交换和缩放
					positions2.push( z * 0.3, x * 0.3, y * 0.3 );

					// 根据位置计算颜色
					const vx = ( x / n ) + 0.5;
					const vy = ( y / n ) + 0.5;
					const vz = ( z / n ) + 0.5;

					color.setRGB( vx, vy, vz, THREE.SRGBColorSpace );

					colors.push( color.r, color.g, color.b );

				}

				// 获取底层WebGL上下文
				const gl = renderer.getContext();

				// 直接使用WebGL API创建和填充缓冲区对象

				// 第一个位置缓冲区
				const pos = gl.createBuffer();
				gl.bindBuffer( gl.ARRAY_BUFFER, pos );
				gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( positions ), gl.STATIC_DRAW );

				// 第二个位置缓冲区
				const pos2 = gl.createBuffer();
				gl.bindBuffer( gl.ARRAY_BUFFER, pos2 );
				gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( positions2 ), gl.STATIC_DRAW );

				// 颜色缓冲区
				const rgb = gl.createBuffer();
				gl.bindBuffer( gl.ARRAY_BUFFER, rgb );
				gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( colors ), gl.STATIC_DRAW );

				// 创建THREE.GLBufferAttribute包装WebGL缓冲区
				const posAttr1 = new THREE.GLBufferAttribute( pos, gl.FLOAT, 3, 4, particles );
				const posAttr2 = new THREE.GLBufferAttribute( pos2, gl.FLOAT, 3, 4, particles );
				
				// 初始使用第一个位置缓冲区
				geometry.setAttribute( 'position', posAttr1 );

				// 每2秒切换一次位置缓冲区
				setInterval( function () {
					const attr = geometry.getAttribute( 'position' );
					geometry.setAttribute( 'position', ( attr === posAttr1 ) ? posAttr2 : posAttr1 );
				}, 2000 );

				// 设置颜色属性
				geometry.setAttribute( 'color', new THREE.GLBufferAttribute( rgb, gl.FLOAT, 3, 4, particles ) );

				// 创建粒子材质
				const material = new THREE.PointsMaterial( { size: 15, vertexColors: true } );

				// 创建粒子系统
				points = new THREE.Points( geometry, material );

				// 设置边界球体,帮助渲染器进行视锥体剔除
				geometry.boundingSphere = new THREE.Sphere().set( new THREE.Vector3(), 500 );

				// 添加到场景
				scene.add( points );

				// 添加性能统计
				stats = new Stats();
				container.appendChild( stats.dom );

				// 窗口大小变化事件监听
				window.addEventListener( 'resize', onWindowResize );

			}

			// 窗口大小变化处理函数
			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

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

			}

			// 动画循环
			function animate() {

				// 随机改变渲染的粒子数量
				drawCount = ( Math.max( 5000, drawCount ) + Math.floor( 500 * Math.random() ) ) % particles;
				points.geometry.setDrawRange( 0, drawCount );

				// 基于时间的旋转动画
				const time = Date.now() * 0.001;
				points.rotation.x = time * 0.1;
				points.rotation.y = time * 0.2;

				// 渲染场景
				renderer.render( scene, camera );

				// 更新性能统计
				stats.update();

			}

		script>

	body>
html>

自定义VBO技术解析

直接使用WebGL API创建VBO

Three.js通常隐藏了底层WebGL API的细节,但在某些高性能需求场景下,我们可以直接操作WebGL缓冲区对象:

  1. 获取WebGL上下文const gl = renderer.getContext()
  2. 创建缓冲区gl.createBuffer()
  3. 绑定缓冲区gl.bindBuffer()
  4. 填充数据gl.bufferData()

这种方法比使用Three.js的BufferAttribute更底层,性能更高,特别适合处理超大规模数据集。

动态切换几何体属性

本示例的核心技术是每2秒切换一次粒子的位置数据:

  1. 创建两个位置缓冲区pospos2,分别存储不同的位置数据
  2. 定时切换:通过setInterval定期切换几何体使用的位置属性
  3. 无缝过渡:由于两个缓冲区大小相同,切换时不需要重新设置几何体的其他属性

这种技术可以用于实现粒子系统的形态变化、数据可视化中的数据切换等场景。

drawRange技术优化渲染

通过动态调整drawRange,我们可以:

  1. 控制渲染的粒子数量:每一帧随机改变渲染的粒子数
  2. 优化性能:避免渲染不可见或不必要的粒子
  3. 创建动态效果:通过改变渲染数量,实现粒子系统的呼吸效果或流动感

这种技术在处理大规模数据集时尤为重要,可以显著提高渲染性能。

性能考虑与最佳实践

直接操作WebGL缓冲区时的性能优化建议:

  1. 批量更新数据:尽量批量更新缓冲区数据,减少GPU和CPU之间的通信
  2. 使用适当的缓冲区类型:根据数据更新频率选择STATIC_DRAWDYNAMIC_DRAWSTREAM_DRAW
  3. 预分配足够空间:预先分配足够大的缓冲区,避免频繁重新分配
  4. 使用边界信息:设置正确的boundingSphere,帮助渲染器进行视锥体剔除
  5. 平衡底层操作:只有在必要时才直接操作WebGL API,大多数情况下Three.js的高级API已经足够高效

这种自定义VBO技术适合需要极致性能的应用场景,如大规模粒子系统、科学可视化、实时数据渲染等。

你可能感兴趣的:(编辑器,学习,webgl)