• 【bug优化】Three.js 中实现圆角立方体的详尽指南


          在Three.js中,要创建一个具有圆角的立方体,通常不会直接修改立方体几何体,而是使用更高级的几何体生成器,如THREE.BoxBufferGeometry结合THREE.SphereGeometry来实现圆角效果。Three.js自带一个更简单的解决方案:THREE.RoundedBoxGeometry。这个类可以直接生成带有圆角的立方体。

    原理

    1. 几何体生成THREE.RoundedBoxGeometry会根据给定的尺寸和圆角半径生成一个立方体几何体,其中每个角落都被替换为一个球面的一部分。
    2. 材质应用:需要为这个几何体应用一个材质(如THREE.MeshStandardMaterial),以定义立方体的外观,包括颜色、反射等属性。
    3. 网格创建:将几何体和材质组合成一个THREE.Mesh对象,并将其添加到场景中。
    1. // 引入Three.js库和BufferGeometryUtils
    2. import * as THREE from 'three';
    3. import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
    4. // 创建场景和相机
    5. const scene = new THREE.Scene();
    6. const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    7. camera.position.z = 5;
    8. // 创建渲染器并添加到DOM
    9. const renderer = new THREE.WebGLRenderer();
    10. renderer.setSize(window.innerWidth, window.innerHeight);
    11. document.body.appendChild(renderer.domElement);
    12. // 创建基本的立方体几何体
    13. const cubeGeometry = new THREE.BoxBufferGeometry(1, 1, 1);
    14. const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
    15. // 定义角的半径和分段数
    16. const cornerRadius = 0.1;
    17. const cornerSegments = 16;
    18. // 创建一个函数来生成弧形角的几何体
    19. function createCornerGeometry(x, y, z) {
    20. const cornerGeometry = new THREE.TorusBufferGeometry(cornerRadius, 0.05, cornerSegments, 100);
    21. const matrix = new THREE.Matrix4();
    22. matrix.makeTranslation(x, y, z);
    23. matrix.multiply(new THREE.Matrix4().makeRotationX(Math.PI / 2));
    24. matrix.multiply(new THREE.Matrix4().makeRotationY(Math.PI / 2));
    25. const cornerGeometryTransformed = new THREE.BufferGeometry();
    26. cornerGeometryTransformed.applyMatrix4(cornerGeometry, matrix);
    27. return cornerGeometryTransformed;
    28. }
    29. // 创建所有8个角的几何体
    30. const cornerGeometries = [
    31. createCornerGeometry(-0.5, -0.5, -0.5),
    32. createCornerGeometry(0.5, -0.5, -0.5),
    33. createCornerGeometry(0.5, 0.5, -0.5),
    34. createCornerGeometry(-0.5, 0.5, -0.5),
    35. createCornerGeometry(-0.5, -0.5, 0.5),
    36. createCornerGeometry(0.5, -0.5, 0.5),
    37. createCornerGeometry(0.5, 0.5, 0.5),
    38. createCornerGeometry(-0.5, 0.5, 0.5),
    39. ];
    40. // 合并立方体几何体和所有角的几何体
    41. const mergedGeometry = mergeBufferGeometries([cubeGeometry, ...cornerGeometries]);
    42. // 创建并添加网格对象到场景
    43. const mesh = new THREE.Mesh(mergedGeometry, cubeMaterial);
    44. scene.add(mesh);
    45. // 创建光源
    46. const light = new THREE.AmbientLight(0xffffff, 0.5); // 环境光源
    47. const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
    48. directionalLight.position.set(1, 1, 1).normalize();
    49. scene.add(light);
    50. scene.add(directionalLight);
    51. // 渲染函数
    52. function animate() {
    53. requestAnimationFrame(animate);
    54. mesh.rotation.y += 0.01;
    55. renderer.render(scene, camera);
    56. }
    57. animate();

           在Three.js中创建具有弧形角的立方体时,确保每个角的纹理正确贴合的关键在于正确处理UV坐标。在合并多个几何体时,UV坐标可能会变得不一致,导致纹理映射出现错误。以下策略:

    1. 在创建几何体时设置UV坐标:在创建每个环面几何体和立方体几何体时,确保UV坐标是正确的。可以使用THREE.TorusBufferGeometryTHREE.BoxBufferGeometry的参数来控制UV坐标。

    2. 调整UV坐标:在合并几何体之后,检查合并后的BufferGeometry的UV坐标。如果发现UV坐标不正确,可以使用THREE.BufferGeometrysetAttribute方法来修改UV坐标。

    3. 使用纹理坐标偏移和缩放:在THREE.Material中使用offsetrepeat属性来调整纹理在几何体上的位置和大小。这可以让你在不修改UV坐标的情况下调整纹理的贴合。

    4. 自定义顶点着色器:在THREE.ShaderMaterial中编写自定义的顶点着色器来处理UV坐标。这可以在渲染时实时调整UV坐标,以适应几何体的形状。

    5. 使用UV贴图工具:在3D建模软件中创建具有弧形角的立方体,在软件中为模型创建UV贴图。将UV贴图导出为图片文件,在Three.js中使用这个UV贴图作为纹理。可以确保纹理正确贴合,但可能需要额外的时间和工作。

    6. 使用UV贴图插件:使用Three.js的UV贴图插件,如THREE.UVMappingPlugin,来自动处理UV坐标。这可以简化纹理映射的过程,但可能需要额外的配置和设置。

      1. // 引入Three.js库和BufferGeometryUtils
      2. import * as THREE from 'three';
      3. import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
      4. // 创建场景和相机
      5. const scene = new THREE.Scene();
      6. const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
      7. camera.position.z = 5;
      8. // 创建渲染器并添加到DOM
      9. const renderer = new THREE.WebGLRenderer();
      10. renderer.setSize(window.innerWidth, window.innerHeight);
      11. document.body.appendChild(renderer.domElement);
      12. // 创建基本的立方体几何体
      13. const cubeGeometry = new THREE.BoxBufferGeometry(1, 1, 1);
      14. const cubeMaterial = new THREE.MeshStandardMaterial({ map: new THREE.TextureLoader().load('textures/cube.jpg') });
      15. // 定义角的半径和分段数
      16. const cornerRadius = 0.1;
      17. const cornerSegments = 16;
      18. // 创建一个函数来生成弧形角的几何体
      19. function createCornerGeometry(x, y, z) {
      20. const cornerGeometry = new THREE.TorusBufferGeometry(cornerRadius, 0.05, cornerSegments, 100);
      21. const matrix = new THREE.Matrix4();
      22. matrix.makeTranslation(x, y, z);
      23. matrix.multiply(new THREE.Matrix4().makeRotationX(Math.PI / 2));
      24. matrix.multiply(new THREE.Matrix4().makeRotationY(Math.PI / 2));
      25. const cornerGeometryTransformed = new THREE.BufferGeometry();
      26. cornerGeometryTransformed.applyMatrix4(cornerGeometry, matrix);
      27. return cornerGeometryTransformed;
      28. }
      29. // 创建所有8个角的几何体
      30. const cornerGeometries = [
      31. createCornerGeometry(-0.5, -0.5, -0.5),
      32. createCornerGeometry(0.5, -0.5, -0.5),
      33. createCornerGeometry(0.5, 0.5, -0.5),
      34. createCornerGeometry(-0.5, 0.5, -0.5),
      35. createCornerGeometry(-0.5, -0.5, 0.5),
      36. createCornerGeometry(0.5, -0.5, 0.5),
      37. createCornerGeometry(0.5, 0.5, 0.5),
      38. createCornerGeometry(-0.5, 0.5, 0.5),
      39. ];
      40. // 合并立方体几何体和所有角的几何体
      41. const mergedGeometry = mergeBufferGeometries([cubeGeometry, ...cornerGeometries]);
      42. // 调整UV坐标
      43. const uvAttribute = mergedGeometry.attributes.uv;
      44. const uvData = uvAttribute.array;
      45. for (let i = 0; i < uvData.length; i += 2) {
      46. uvData[i] = (uvData[i] * 2) % 1; // 调整U坐标
      47. uvData[i + 1] = (uvData[i + 1] * 2) % 1; // 调整V坐标
      48. }
      49. uvAttribute.needsUpdate = true;
      50. // 创建并添加网格对象到场景
      51. const mesh = new THREE.Mesh(mergedGeometry, cubeMaterial);
      52. scene.add(mesh);
      53. // 创建光源
      54. const light = new THREE.AmbientLight(0xffffff, 0.5); // 环境光源
      55. const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
      56. directionalLight.position.set(1, 1, 1).normalize();
      57. scene.add(light);
      58. scene.add(directionalLight);
      59. // 渲染函数
      60. function animate() {
      61. requestAnimationFrame(animate);
      62. mesh.rotation.y += 0.01;
      63. renderer.render(scene, camera);
      64. }
      65. animate();

      在处理大规模场景和更复杂的几何体与纹理时,保持良好的性能和视觉效果,如下策略优化:

    • 几何体简化

         对于复杂的几何体,可以使用LOD(Level of Detail)技术来根据视距远近使用不同的细节级别。在视距较远时使用简化后的几何体,从而减少渲染负担。Three.js有内置的LOD对象可以实现这一点。

    • 动态纹理贴图

          当场景中的纹理非常复杂或者需要实时更新时,可以使用动态纹理贴图技术。这包括使用基于像素的渲染技术(如像素着色器)来实时渲染纹理,或者使用动态生成的纹理贴图。

    • 分块加载与渲染

            对于大规模场景,可以将场景划分为多个块,只渲染当前视锥体内的块,避免一次性加载整个场景。Three.js的Object3D.visible属性可以用来控制对象的可见性。

    • 使用GPU计算

            复杂的计算,如光照计算、物理模拟等,可以卸载到GPU上。Three.js支持WebGL 2.0,可以使用更复杂的着色器和计算着色器来实现GPU计算。

    • 多线程渲染

          使用Web Workers或者WebAssembly来实现多线程渲染,从而提高渲染效率。尽管Three.js本身不支持多线程渲染,但可以结合Web Workers实现部分计算的并行化。

    • 优化渲染管线

         减少不必要的渲染调用,如使用批处理(Batching)和实例化(Instancing)技术来减少Draw Calls。Three.js可以通过合并共享材质的网格对象,或者使用THREE.InstancedMesh来实现实例化。

    • 使用合适的材质

           对于复杂的几何体和纹理,使用适合的材质类型可以减少性能开销。例如,使用THREE.MeshStandardMaterial而非THREE.MeshPhysicalMaterial,或者使用基于图像的渲染(IBL)而非动态光源计算。

    • 场景管理与优化

          定期清理不再需要的对象,避免内存泄漏。使用场景图的遍历机制,如THREE.Scene.traverse,来检查和更新场景中的对象。

  • 相关阅读:
    Ubuntu 22.04 开机闪logo后卡在/dev/sda3: clean
    提升数据安全的五大原则
    LORA详解
    继承于QObject 的多线程实现
    Tdengine-Linux删除
    「BUAA OO Unit 2 HW8」第二单元总结
    第22集丨人生的智慧:通权达变的智慧
    Verilog 函数
    Java:实现测试一个数是否为素数算法(附完整源码)
    01准备阶段 Latex相关软件安装
  • 原文地址:https://blog.csdn.net/weixin_43298211/article/details/140285673