当前位置: 首页 > 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.xdnf.cn/news/1315225.html

相关文章:

  • 【MySQL学习|黑马笔记|Day7】触发器和锁(全局锁、表级锁、行级锁、)
  • 《Python学习之文件操作:从入门到精通》
  • Linux 服务:iSCSI 存储服务配置全流程指南
  • Java基础面试题(3)—Java(String字符串的存储方式,字面量)
  • 链表OJ题讲解---试金石含金量
  • 6个日常工作中常用的工作法:清单工作法、PDCA循环、SMART原则、6W2H 分析法等方法
  • CSS中linear-gradient 的用法
  • 《Vuejs设计与实现》第 14 章(内建组件和模块)
  • Docker+飞算JavaAI=未来:全流程容器化AI开发实战
  • Matlab课程实践——基于MATLAB设计的计算器软件(简单、科学、电工、矩阵及贷款计算)
  • python实现梅尔频率倒谱系数(MFCC) 除了傅里叶变换和离散余弦变换
  • p5.js 3D 形状 “预制工厂“——buildGeometry ()
  • Mitt 事件发射器完全指南:200字节的轻量级解决方案
  • fastadmin 后台列表自定义搜索
  • 【递归、搜索与回溯算法】记忆化搜索
  • 当 AI 开始 “理解” 情感:情感计算技术正在改写人机交互规则
  • KingbaseES:一体化架构与多层防护,支撑业务的持续稳定运行与扩展
  • geekbench riscv镜像下载
  • 【Virtual Globe 渲染技术笔记】8 顶点变换精度
  • 提升 LLM 推理效率的秘密武器:LM Cache 架构与实践
  • Node.js导入MongoDB具体操作
  • 埃式筛法欧拉筛法质数分布定理
  • C++核心语言元素与构建块全解析:从语法规范到高效设计
  • EC11编码器
  • 关于原理解析和编程技巧的深度探索!
  • 【计算机网络面试】TCP/IP网络模型有哪几层
  • LaTeX中表示实数集R的方法
  • 19.5 「4步压缩大模型:GPTQ量化实战让OPT-1.3B显存直降75%」
  • 计算机网络 HTTP和HTTPS 区别
  • 字符串的说明以及应用