12.three官方示例+编辑器+AI快速学习webgl_buffergeometry_indexed

本实例主要讲解内容

这个Three.js示例展示了如何使用**索引几何体(Indexed Geometry)**创建高效的网格模型。通过复用顶点数据并使用索引缓冲区指定三角形面,我们可以在不增加内存开销的情况下创建复杂的几何体。

核心技术包括:

  • 索引缓冲区的使用
  • 顶点属性的组织
  • 颜色插值与平滑着色
  • 高效的网格几何体创建

完整代码注释

html 复制代码
<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - buffergeometry - indexed</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> webgl - buffergeometry - indexed</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';
			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

			let camera, scene, renderer, stats;

			let mesh;

			init();

			function init() {

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

				// 初始化场景
				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0x050505 );

				// 添加半球光,提供基本照明
				const light = new THREE.HemisphereLight();
				light.intensity = 3;
				scene.add( light );

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

				// 存储索引、顶点、法线和颜色的数组
				const indices = [];
				const vertices = [];
				const normals = [];
				const colors = [];

				// 网格参数
				const size = 20; // 网格大小
				const segments = 10; // 分段数

				const halfSize = size / 2;
				const segmentSize = size / segments;

				const _color = new THREE.Color();

				// 生成顶点、法线和颜色数据

				// 遍历网格的每一行和每一列
				for ( let i = 0; i <= segments; i ++ ) {

					const y = ( i * segmentSize ) - halfSize;

					for ( let j = 0; j <= segments; j ++ ) {

						const x = ( j * segmentSize ) - halfSize;

						// 添加顶点位置(z坐标为0,创建一个平面)
						vertices.push( x, - y, 0 );
						
						// 添加顶点法线(平面朝上,法线方向为z轴正方向)
						normals.push( 0, 0, 1 );

						// 基于位置计算颜色(红色和绿色分量基于x和y坐标)
						const r = ( x / size ) + 0.5;
						const g = ( y / size ) + 0.5;

						_color.setRGB( r, g, 1, THREE.SRGBColorSpace );

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

					}

				}

				// 生成索引数据(定义三角形面)

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

					for ( let j = 0; j < segments; j ++ ) {

						// 计算四个顶点的索引
						const a = i * ( segments + 1 ) + ( j + 1 );
						const b = i * ( segments + 1 ) + j;
						const c = ( i + 1 ) * ( segments + 1 ) + j;
						const d = ( i + 1 ) * ( segments + 1 ) + ( j + 1 );

						// 每个四边形由两个三角形组成
						indices.push( a, b, d ); // 第一个三角形
						indices.push( b, c, d ); // 第二个三角形

					}

				}

				// 设置几何体的索引和属性
				geometry.setIndex( indices );
				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
				geometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
				geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );

				// 创建材质(双面渲染,使用顶点颜色)
				const material = new THREE.MeshPhongMaterial( {
					side: THREE.DoubleSide,
					vertexColors: true
				} );

				// 创建网格并添加到场景
				mesh = new THREE.Mesh( geometry, material );
				scene.add( mesh );

				// 初始化渲染器
				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				renderer.setAnimationLoop( animate );
				document.body.appendChild( renderer.domElement );

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

				// 添加GUI控制面板
				const gui = new GUI();
				gui.add( material, 'wireframe' ); // 控制是否显示线框

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

			}

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

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

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

			}

			// 动画循环
			function animate() {

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

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

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

			}

		</script>

	</body>
</html>

索引几何体技术解析

什么是索引几何体

在Three.js中,几何体可以通过两种方式定义:

  1. 非索引几何体:为每个三角形的每个顶点单独定义位置、法线、颜色等属性
  2. 索引几何体:定义一组顶点,然后通过索引指定哪些顶点组成一个三角形

索引几何体的优势:

  • 内存效率高:共享顶点数据,减少内存占用
  • 渲染效率高:减少需要传输到GPU的数据量
  • 便于修改:修改一个顶点会影响所有使用该顶点的三角形
索引数据的生成与应用

在本示例中,我们通过以下步骤创建索引几何体:

  1. 生成顶点数据:创建规则网格的顶点位置、法线和颜色
  2. 生成索引数据:为每个四边形创建两个三角形,每个三角形由三个顶点索引组成
  3. 设置几何体属性 :使用setIndex方法设置索引数据,使用setAttribute设置顶点属性

索引生成的关键代码:

javascript 复制代码
// 对于每个四边形
for ( let i = 0; i < segments; i ++ ) {
  for ( let j = 0; j < segments; j ++ ) {
    // 计算四个顶点的索引
    const a = i * ( segments + 1 ) + ( j + 1 );
    const b = i * ( segments + 1 ) + j;
    const c = ( i + 1 ) * ( segments + 1 ) + j;
    const d = ( i + 1 ) * ( segments + 1 ) + ( j + 1 );

    // 定义两个三角形(每个三角形由三个顶点索引组成)
    indices.push( a, b, d ); // 第一个三角形
    indices.push( b, c, d ); // 第二个三角形
  }
}
顶点属性与着色

本示例中,我们为每个顶点定义了三种属性:

  1. 位置(position):决定顶点在空间中的位置
  2. 法线(normal):决定顶点的朝向,用于光照计算
  3. 颜色(color):决定顶点的基础颜色

当使用Phong着色模型时,Three.js会自动在三角形面之间插值这些属性,创建平滑的过渡效果。这就是为什么我们只在顶点处定义了颜色,但整个表面都有平滑的颜色渐变。

性能考虑

索引几何体在处理复杂模型时尤为重要:

  1. 减少内存占用:对于有大量共享顶点的模型,索引方式可以显著减少内存使用
  2. 提高渲染效率:更少的数据需要传输到GPU
  3. 简化数据管理:修改顶点属性时,只需更新一次,所有使用该顶点的三角形都会受到影响

然而,索引几何体也有一些限制:

  • 每个顶点的所有属性必须相同(如果需要不同的法线或颜色,需要创建不同的顶点)
  • 稍微复杂的数据结构,需要更多的前期计算

在实际应用中,是否使用索引几何体取决于具体场景。对于规则网格、共享大量顶点的模型,索引方式是首选;对于简单的、顶点不共享的模型,非索引方式可能更简单。

相关推荐
yangyanping2010818 小时前
Go语言学习之对象关系映射GORM
jvm·学习·golang
网络工程小王18 小时前
【Transformer架构详解】(学习笔记)
笔记·学习
【ql君】qlexcel19 小时前
Visual Studio Code的使用,VS code常用扩展
ide·vscode·编辑器·visual studio·扩展
倒酒小生20 小时前
今日算法学习小结
学习
醇氧20 小时前
【学习】【说人话版】子网划分
学习
不灭锦鲤21 小时前
网络安全学习(面试)
学习·安全·web安全
世人万千丶1 天前
Flutter 框架跨平台鸿蒙开发 - 鸿蒙版本五子棋游戏应用
学习·flutter·游戏·华为·harmonyos·鸿蒙
Aktx20FNz1 天前
一文学习 Spring AOP 源码全过程
java·学习·spring
Jay Kay1 天前
生成式推荐模型学习记录part1
学习
正经教主1 天前
【docker基础】0、系统学习docker之总计划
学习·docker·容器