当前位置: 首页 > news >正文

第16节:自定义几何体 - 从顶点构建3D世界

第16节:自定义几何体 - 从顶点构建3D世界

深入BufferGeometry底层原理与动态地形生成

在这里插入图片描述

1. 核心概念解析

1.1 BufferGeometry vs Geometry
特性Geometry (传统)BufferGeometry (现代)
数据结构面向对象(顶点对象)类型化数组(Float32Array)
内存效率低(冗余数据)高(紧凑存储)
性能较慢(CPU处理)极快(GPU直接读取)
适用场景简单几何体/学习用途复杂模型/动态几何/性能敏感场景
更新机制易修改但性能差难修改但渲染快

⚠️ Three.js r125+已弃用Geometry,全面转向BufferGeometry

1.2 顶点数据流
顶点坐标
顶点着色器
法线向量
UV坐标
片元着色器
像素输出

2. 构建自定义几何体

2.1 基础三角形创建
// 创建空几何体
const geometry = new THREE.BufferGeometry();// 定义顶点坐标(3个点构成三角形)
const vertices = new Float32Array([// 顶点10, 0, 0, // 顶点21, 0, 0, // 顶点30.5, 1, 0  
]);// 设置顶点属性
geometry.setAttribute('position',new THREE.BufferAttribute(vertices, 3) // 3个数值表示一个点
);// 定义索引(连接顺序)
const indices = new Uint16Array([0, 1, 2]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));// 计算法线(光照必需)
geometry.computeVertexNormals();// 创建材质
const material = new THREE.MeshStandardMaterial({ color: 0xff0000,wireframe: false 
});// 生成网格
const triangle = new THREE.Mesh(geometry, material);
scene.add(triangle);
2.2 添加UV映射
// UV坐标(纹理映射)
const uvs = new Float32Array([0, 0,  // 顶点1对应纹理左下1, 0,  // 顶点2对应纹理右下0.5, 1 // 顶点3对应纹理顶部
]);geometry.setAttribute('uv',new THREE.BufferAttribute(uvs, 2) // 2个数值表示一组UV
);// 应用纹理
const textureLoader = new THREE.TextureLoader();
material.map = textureLoader.load('/textures/rock.jpg');

3. 动态地形生成

3.1 噪声算法对比
算法特点适用场景性能
Perlin自然连续,梯度噪声地形/云层★★★☆☆
Simplex计算高效,高维优势实时生成★★★★☆
Worley细胞状结构石材/皮肤纹理★★☆☆☆
Value块状效果像素艺术★★★★★
3.2 分形地形生成
// 地形参数配置
const WIDTH = 100;    // 地形宽度(顶点数)
const DEPTH = 100;    // 地形深度(顶点数)
const SPACING = 0.2;  // 顶点间距
const HEIGHT_SCALE = 2; // 高度缩放// 生成顶点数据
const vertices = [];
for (let z = 0; z < DEPTH; z++) {for (let x = 0; x < WIDTH; x++) {// 使用Simplex噪声生成高度const y = noise.simplex2(x * 0.1, z * 0.1) * HEIGHT_SCALE;vertices.push(x * SPACING, y, z * SPACING);}
}// 创建几何体
const terrainGeometry = new THREE.BufferGeometry();
terrainGeometry.setAttribute('position',new THREE.Float32BufferAttribute(vertices, 3)
);// 生成索引(三角形面)
const indices = [];
for (let z = 0; z < DEPTH-1; z++) {for (let x = 0; x < WIDTH-1; x++) {const a = z * WIDTH + x;const b = a + 1;const c = a + WIDTH;const d = c + 1;// 两个三角形组成一个面片indices.push(a, b, c); // 三角形1indices.push(b, d, c); // 三角形2}
}terrainGeometry.setIndex(indices);
terrainGeometry.computeVertexNormals(); // 计算法线// 添加材质
const material = new THREE.MeshStandardMaterial({color: 0x3a7c40,wireframe: false,flatShading: false
});const terrain = new THREE.Mesh(terrainGeometry, material);
scene.add(terrain);
3.3 实时地形变形
// 顶点着色器修改
const positionAttribute = terrain.geometry.getAttribute('position');
const originalVertices = positionAttribute.array.slice(); // 备份原始数据function deformTerrain() {const vertices = positionAttribute.array;const time = performance.now() * 0.001;for (let i = 0; i < vertices.length; i += 3) {const x = vertices[i];const z = vertices[i + 2];// 添加波浪效果const waveY = Math.sin(x * 2 + time) * Math.cos(z * 2 + time) * 0.3;// 恢复原始高度并添加波动vertices[i + 1] = originalVertices[i + 1] + waveY;}positionAttribute.needsUpdate = true; // 标记需要更新terrain.geometry.computeVertexNormals(); // 重新计算法线
}// 每帧更新
function animate() {requestAnimationFrame(animate);deformTerrain();renderer.render(scene, camera);
}
animate();

4. 性能优化技巧

4.1 顶点处理优化
操作正确做法错误做法
几何体更新直接修改ArrayBuffer创建新BufferAttribute
法线计算仅变形后调用computeVertexNormals每帧调用
内存管理复用BufferGeometry频繁创建新几何体
4.2 GPU Instancing(实例化渲染)
// 创建基础几何体
const baseGeometry = new THREE.BoxGeometry(1, 1, 1);// 创建实例化几何体
const instanceCount = 1000;
const instancedGeometry = new THREE.InstancedBufferGeometry();
instancedGeometry.copy(baseGeometry);// 生成实例位置
const positions = new Float32Array(instanceCount * 3);
for (let i = 0; i < instanceCount; i++) {positions[i * 3] = Math.random() * 100 - 50; // xpositions[i * 3 + 1] = Math.random() * 20;   // ypositions[i * 3 + 2] = Math.random() * 100 - 50; // z
}instancedGeometry.setAttribute('instancePosition',new THREE.InstancedBufferAttribute(positions, 3)
);// 着色器修改
const material = new THREE.ShaderMaterial({vertexShader: `attribute vec3 instancePosition;void main() {vec3 pos = position + instancePosition;gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);}`,fragmentShader: `...`
});const mesh = new THREE.Mesh(instancedGeometry, material);
scene.add(mesh);### **5. 实战案例:3D分形地形生成器**  
```javascript
// 完整地形生成器类
class FractalTerrain {constructor(width = 100, depth = 100, options = {}) {this.width = width;this.depth = depth;this.options = {spacing: 0.5,heightScale: 2,noiseScale: 0.1,...options};this.geometry = new THREE.BufferGeometry();this.generate();}// 生成地形generate() {const { spacing, heightScale, noiseScale } = this.options;const vertices = [];const uvs = [];// 生成顶点for (let z = 0; z < this.depth; z++) {for (let x = 0; x < this.width; x++) {// 分形噪声(多倍频叠加)let y = 0;let amplitude = 1;let frequency = 1;for (let i = 0; i < 5; i++) {y += noise.simplex2(x * noiseScale * frequency, z * noiseScale * frequency) * amplitude;amplitude *= 0.5;frequency *= 2;}y *= heightScale;vertices.push(x * spacing, y, z * spacing);uvs.push(x / this.width, z / this.depth);}}// 设置顶点属性this.geometry.setAttribute('position',new THREE.Float32BufferAttribute(vertices, 3));this.geometry.setAttribute('uv',new THREE.Float32BufferAttribute(uvs, 2));// 生成索引this.generateIndices();this.geometry.computeVertexNormals();}// 生成三角形索引generateIndices() {const indices = [];for (let z = 0; z < this.depth - 1; z++) {for (let x = 0; x < this.width - 1; x++) {const a = z * this.width + x;const b = a + 1;const c = a + this.width;const d = c + 1;indices.push(a, b, c);indices.push(b, d, c);}}this.geometry.setIndex(indices);}// 获取网格对象getMesh(material) {return new THREE.Mesh(this.geometry, material);}
}// 使用示例
const terrain = new FractalTerrain(200, 200, {heightScale: 5,noiseScale: 0.05
});
const material = new THREE.MeshStandardMaterial({ color: 0x3a7c40,wireframe: false 
});
scene.add(terrain.getMesh(material));

6. 学习路线图

基础三角形
参数化几何体
动态顶点更新
噪声地形生成
GPU实例化
ComputeShader

7. 常见问题解答

Q1:如何高效更新顶点数据?

// 获取顶点数组引用
const positions = geometry.attributes.position.array;// 直接修改数据
positions[vertexIndex * 3 + 1] = newY; // 修改Y坐标// 标记需要更新
geometry.attributes.position.needsUpdate = true;// 更新法线(可选)
geometry.computeVertexNormals(); 

Q2:为什么我的自定义几何体没有光照?

  • 原因:缺少法线数据
  • 解决方案:
    1. 调用geometry.computeVertexNormals()自动计算
    2. 手动设置法线属性:
    const normals = new Float32Array([...]); // 每个顶点法向量
    geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3));
    

Q3:如何实现LOD(多细节层次)?

const lod = new THREE.LOD();// 高细节模型(近处)
const highDetail = generateTerrain(200, 200, 0.05);
highDetail.updateMatrix();
lod.addLevel(highDetail, 0);// 中细节模型(中距离)
const midDetail = generateTerrain(100, 100, 0.1);
midDetail.updateMatrix();
lod.addLevel(midDetail, 50);// 低细节模型(远处)
const lowDetail = generateTerrain(50, 50, 0.2);
lowDetail.updateMatrix();
lod.addLevel(lowDetail, 100);scene.add(lod);

下一节预告:高级材质 - ShaderMaterial揭秘

第17节:用GLSL编写自定义着色器

你将掌握

  1. GLSL语法精髓

    • 数据类型/向量操作/矩阵变换
    • 片元着色器 vs 顶点着色器
  2. 特效开发四部曲

    输入参数
    顶点变换
    光栅化
    片元计算
  3. 实战特效案例

    • 动态波浪水面 🌊
    • 全息投影效果 👽
    • 地形等高线 🗺️
  4. 着色器调试技巧

    • 颜色调试法
    • 数值可视化工具

🚀 进入图形编程的魔法世界,用代码直接操控GPU创造视觉奇迹!

http://www.dtcms.com/a/335808.html

相关文章:

  • 检查xrdp远程连接桌面卡顿的问题(附解决sh脚本)
  • Oracle查看历史会话信息视图介绍
  • 【大语言模型 04】Cross-Attention vs Self-Attention实战对比:解码器中的双重注意力机制
  • NumPy 库介绍:核心 API 详解
  • MYSQL-175. 组合两个表
  • Java 学习笔记(基础篇4)
  • Java学习笔记:IDEA简单使用技巧
  • 安卓14系统应用收不到开机广播
  • HTTP请求参数类型及对应的后端注解
  • AMBA-AXI and ACE协议详解(七)
  • 【学习笔记】面向AI安全的26个缓解措施
  • API网关实施中典型陷阱
  • 【数据结构与算法】单调队列的定义和运用
  • 整体设计 之“凝聚式中心点”原型 --整除:智能合约和DBMS的深层联合 之1
  • Android Jetpack | Livedata
  • Gradle快速入门学习
  • 【算法】模拟专题
  • riscv中断处理软硬件流程总结
  • C++算法题目分享:二叉搜索树相关的习题
  • 原子指标、派生指标和复合指标
  • nodejs 中间件
  • 【Jenkins】01 - Jenkins安装
  • C语言网络编程TCP通信实战:客户端↔服务器双向键盘互动全流程解析
  • [GWCTF 2019]枯燥的抽奖
  • Java线程的6种状态和JVM状态打印
  • [TryHackMe]Brainpan1( 内存溢出)
  • PERCEIVER IO:一种用于结构化输入与输出的通用架构
  • 脉冲计数实现
  • 深入剖析ROS参数服务器通信机制 ——共享全局数据的“云端仓库”实现原理
  • Node.js安装教程