13.three官方示例+编辑器+AI快速学习webgl_buffergeometry_instancing_billboards

本实例主要讲解内容

这个Three.js示例展示了如何使用**GPU实例化(GPU Instancing)**技术创建高性能的粒子系统。通过实例化渲染,我们可以在单次绘制调用中渲染大量相同基元的不同实例,显著提高渲染效率。

核心技术包括:

  • GPU实例化渲染
  • 自定义着色器编程
  • 顶点属性与实例属性
  • 基于时间的动态效果
  • HSL颜色空间转换
    在这里插入图片描述

完整代码注释

<!DOCTYPE html>
<html lang="en">
<head>
	<title>three.js webgl - instanced particles - billboards - colors</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="info">
		<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - instanced circle billboards - colors
		<div id="notSupported" style="display:none">Sorry, your graphics card + browser does not support hardware instancing</div>
	</div>

	<!-- 顶点着色器 -->
	<script id="vshader" type="x-shader/x-vertex">
		precision highp float;
		uniform mat4 modelViewMatrix;  // 模型视图矩阵
		uniform mat4 projectionMatrix; // 投影矩阵
		uniform float time;            // 时间变量,用于动画

		attribute vec3 position;       // 顶点位置(基元几何体)
		attribute vec2 uv;             // 纹理坐标
		attribute vec3 translate;      // 实例平移向量

		varying vec2 vUv;              // 传递给片段着色器的纹理坐标
		varying float vScale;          // 传递给片段着色器的缩放因子

		void main() {

			// 计算实例的模型视图位置
			vec4 mvPosition = modelViewMatrix * vec4( translate, 1.0 );
			
			// 基于时间和位置计算缩放因子,创建波浪效果
			vec3 trTime = vec3(translate.x + time,translate.y + time,translate.z + time);
			float scale =  sin( trTime.x * 2.1 ) + sin( trTime.y * 3.2 ) + sin( trTime.z * 4.3 );
			vScale = scale;
			scale = scale * 10.0 + 10.0;
			
			// 应用缩放并计算最终裁剪空间位置
			mvPosition.xyz += position * scale;
			vUv = uv;
			gl_Position = projectionMatrix * mvPosition;

		}
	</script>
	
	<!-- 片段着色器 -->
	<script id="fshader" type="x-shader/x-fragment">
		precision highp float;

		uniform sampler2D map;         // 粒子纹理

		varying vec2 vUv;              // 从顶点着色器接收的纹理坐标
		varying float vScale;          // 从顶点着色器接收的缩放因子

		// HSL到RGB的颜色转换函数
		vec3 HUEtoRGB(float H){
			H = mod(H,1.0);
			float R = abs(H * 6.0 - 3.0) - 1.0;
			float G = 2.0 - abs(H * 6.0 - 2.0);
			float B = 2.0 - abs(H * 6.0 - 4.0);
			return clamp(vec3(R,G,B),0.0,1.0);
		}

		vec3 HSLtoRGB(vec3 HSL){
			vec3 RGB = HUEtoRGB(HSL.x);
			float C = (1.0 - abs(2.0 * HSL.z - 1.0)) * HSL.y;
			return (RGB - 0.5) * C + HSL.z;
		}

		void main() {
			// 采样纹理颜色
			vec4 diffuseColor = texture2D( map, vUv );
			
			// 基于缩放因子计算HSL颜色并转换为RGB
			gl_FragColor = vec4( diffuseColor.xyz * HSLtoRGB(vec3(vScale/5.0, 1.0, 0.5)), diffuseColor.w );

			// 丢弃透明度低于0.5的片段,实现纹理的透明效果
			if ( diffuseColor.w < 0.5 ) discard;
		}
	</script>

	<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 geometry, material, mesh;

		init();

		function init() {

			container = document.createElement( 'div' );
			document.body.appendChild( container );

			// 初始化相机
			camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5000 );
			camera.position.z = 1400;

			// 初始化场景
			scene = new THREE.Scene();

			// 创建基础几何体(圆形)
			const circleGeometry = new THREE.CircleGeometry( 1, 6 );

			// 创建实例化几何体
			geometry = new THREE.InstancedBufferGeometry();
			geometry.index = circleGeometry.index;
			geometry.attributes = circleGeometry.attributes;

			// 设置粒子数量
			const particleCount = 75000;

			// 创建并填充实例位置数组
			const translateArray = new Float32Array( particleCount * 3 );

			for ( let i = 0, i3 = 0, l = particleCount; i < l; i ++, i3 += 3 ) {

				// 随机分布在单位球内
				translateArray[ i3 + 0 ] = Math.random() * 2 - 1;
				translateArray[ i3 + 1 ] = Math.random() * 2 - 1;
				translateArray[ i3 + 2 ] = Math.random() * 2 - 1;

			}

			// 将位置数组设置为实例属性
			geometry.setAttribute( 'translate', new THREE.InstancedBufferAttribute( translateArray, 3 ) );

			// 创建自定义着色器材质
			material = new THREE.RawShaderMaterial( {
				uniforms: {
					'map': { value: new THREE.TextureLoader().load( 'textures/sprites/circle.png' ) },
					'time': { value: 0.0 }
				},
				vertexShader: document.getElementById( 'vshader' ).textContent,
				fragmentShader: document.getElementById( 'fshader' ).textContent,
				depthTest: true,
				depthWrite: true
			} );

			// 创建实例化网格并添加到场景
			mesh = new THREE.Mesh( geometry, material );
			mesh.scale.set( 500, 500, 500 ); // 放大整体场景
			scene.add( mesh );

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

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

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

			return true;

		}

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

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

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

		}

		// 动画循环
		function animate() {

			// 更新时间统一变量
			const time = performance.now() * 0.0005;
			material.uniforms[ 'time' ].value = time;

			// 旋转整个粒子系统
			mesh.rotation.x = time * 0.2;
			mesh.rotation.y = time * 0.4;

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

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

		}

	</script>

</body>
</html>

GPU实例化技术解析

什么是GPU实例化

GPU实例化是一种渲染技术,允许在单次绘制调用中渲染同一基元的多个实例,每个实例可以有不同的属性(如位置、颜色、缩放)。与传统的逐个渲染方式相比,实例化渲染的优势在于:

  1. 减少CPU-GPU通信:只需一次绘制调用,而不是为每个实例单独调用
  2. 降低内存占用:共享相同的几何体数据
  3. 提高渲染效率:特别适合大量相似对象的场景,如粒子系统、植被、城市建筑等

在Three.js中,我们可以通过InstancedBufferGeometryInstancedBufferAttribute来实现GPU实例化。

实例化几何体的创建

本示例中,我们创建实例化几何体的步骤如下:

  1. 创建基础几何体:使用CircleGeometry创建一个简单的圆形
  2. 创建实例化几何体new THREE.InstancedBufferGeometry()
  3. 复制基础几何体的属性:将圆形的索引和属性复制到实例化几何体
  4. 添加实例属性:创建并设置每个实例的位置属性translate

关键代码:

// 创建基础圆形几何体
const circleGeometry = new THREE.CircleGeometry( 1, 6 );

// 创建实例化几何体并复制基础几何体的属性
const geometry = new THREE.InstancedBufferGeometry();
geometry.index = circleGeometry.index;
geometry.attributes = circleGeometry.attributes;

// 创建并设置实例位置属性
const translateArray = new Float32Array( particleCount * 3 );
// 填充位置数据...
geometry.setAttribute( 'translate', new THREE.InstancedBufferAttribute( translateArray, 3 ) );
自定义着色器编程

本示例使用了自定义着色器来实现粒子的动态效果:

  1. 顶点着色器:计算每个粒子的最终位置和缩放,并传递给片段着色器
  2. 片段着色器:采样纹理并根据顶点着色器传递的缩放因子计算颜色

特别值得注意的是HSL到RGB的颜色转换算法,它允许我们基于单一变量(缩放因子)生成丰富的颜色变化。

性能优化与应用场景

GPU实例化技术特别适合以下场景:

  1. 粒子系统:如本例所示,高效渲染大量粒子
  2. 植被模拟:渲染森林、草地等
  3. 城市建筑:渲染大量相似的建筑或建筑部件
  4. 大规模数据可视化:如点云数据、星空模拟等

使用实例化渲染时的性能优化建议:

  1. 批量更新数据:尽量批量更新实例属性,减少渲染状态切换
  2. 合理使用uniforms和attributes:将频繁变化的数据放在uniforms中,静态数据放在attributes中
  3. 优化着色器计算:避免在着色器中进行复杂计算,特别是在处理大量实例时
  4. 考虑视锥体剔除:对于大规模场景,考虑实现视锥体剔除以避免渲染不可见的实例

这种技术虽然强大,但需要注意并非所有硬件都支持,特别是较旧的移动设备。在实际应用中,建议提供回退方案或降级策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值