three EdgeSplitModifier

EdgeSplitModifier 是 Three.js 中用于分割网格的边缘,以便对具有较大角度的相邻面进行处理,使它们拥有独立的法线,解决法线平滑和光照不自然的问题。这个修饰符常用于在模型中创建硬边效果,特别是在需要表现出锐利边缘的几何体(例如机械零件、建筑等)时。

EdgeSplitModifier 有一个方法

js 复制代码
    EdgeSplitModifier()
    创建一个新的EdgeSplitModifier对象。

    // 创建一个 IcosahedronGeometry 几何体
    const geometry = new THREE.IcosahedronGeometry(10, 3);
    // 创建一个 MeshPhongMaterial 材质
    const material = new THREE.MeshPhongMaterial({ color: 0x00ff00, flatShading: true });
    const mesh = new THREE.Mesh(geometry, material);  // 使用修改后的几何体创建网格
    mesh.castShadow = true;
    mesh.receiveShadow = true;
    // 将网格添加到场景中
    scene.add(mesh);
    // 从上方照射的白色平行光,强度为 0.5。
    const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
    directionalLight.position.set(0, 50, 0);
    directionalLight.castShadow = true; 
    scene.add( directionalLight );
    // 添加环境光来补充场景的整体光照 使得反光物体能显示
    const ambientLight = new THREE.AmbientLight(0x404040); // 环境光
    scene.add(ambientLight);

方法

  • modify(geometry, cutOffAngle, tryKeepNormals)
    • 这是 EdgeSplitModifier 的核心方法,用于对给定的几何体进行边缘分割。其参数如下:
    • geometry (THREE.Geometry 或 THREE.BufferGeometry): 需要修改的几何体对象。此几何体会被分割,边缘将会被处理。
    • cutOffAngle (float): 切割角度阈值,单位为弧度。只有当相邻面的角度大于这个值时,边缘才会被分割。典型值范围为 0 到 π(大约 3.14)。例如,cutOffAngle = 0.5 表示切割角度大于约 28.6 度的边缘会被分割。较小的角度会产生更多的分割。
    • tryKeepNormals (boolean): 指定是否尝试保留原有的法线。设置为 true 时,尽可能保留原来的法线方向;设置为 false 时,会重新计算法线。一般情况下,若法线需要独立计算,tryKeepNormals 设置为 false 会更合适。

主要作用

  1. 分割边缘:
    • 当相邻面之间的角度大于 cutOffAngle 时,EdgeSplitModifier 会将共享边的面分割开,确保每个面具有独立的法线。这样做的主要目的是避免光照计算时出现不自然的平滑过渡。
  2. 控制切割角度:
    • 通过设置 cutOffAngle,用户可以控制分割的敏感度。较大的 cutOffAngle 会减少分割的数量,只有明显尖锐的角度才会被分割;较小的角度会导致更多的边缘分割,适用于需要更细致控制的情况。
  3. 法线处理:
    • 设置 tryKeepNormals 为 false 时,EdgeSplitModifier 会重新计算分割后面片的法线,确保它们符合新的分割结果。若保留原始法线,可能会造成法线计算的不一致或光照效果不佳,因此通常建议在进行分割时重新计算法线。
  4. 适用场景:
    • 硬边效果:当模型有明显的硬边或棱角时,可以使用此修饰符来分割边缘,使其更加清晰。常用于建筑、机械模型等。
    • 光照优化:通过分割边缘,避免模型面之间的平滑过渡,使光照效果更加真实。

  1. 观察效果:
    • 左侧网格(meshOriginal)没有应用 EdgeSplitModifier,你会看到面之间是平滑的,没有明显的硬边。
    • 右侧网格(meshModified)应用了 EdgeSplitModifier,你会看到面之间的硬边更加明显,尤其是在光照变化下,效果更加显著。

在 3D 图形学中,硬边(Hard Edges)指的是一个几何体中相邻的两个面之间具有显著的角度差,从而形成一种明显的锐利边界效果。这种边界通常使几何体看起来更加棱角分明,而不是光滑连续的。 在示例中 当 cutOffAngle 接近 180 时边角会滑些,为0时边角较明显

js 复制代码
    // 声明一些变量,包括材质、修改器、网格和基础几何体
    let map, modifier, mesh, baseGeometry;
    // 参数配置对象,控制是否应用平滑着色、边缘分割、切割角度等
    const params = {
        smoothShading: true,        // 是否启用平滑着色
        edgeSplit: true,            // 是否启用边缘分割
        cutOffAngle: 20,            // 切割角度,单位为度
        showMap: false,             // 是否显示纹理贴图
        tryKeepNormals: true,       // 是否保留法线
    };
    // 添加环境光,提供基础的光照
    scene.add( new THREE.HemisphereLight( 0xffffff, 0x444444, 3 ) );
    // 使用 OBJLoader 加载一个 Cerberus 模型
    new OBJLoader().load(
        'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/models/obj/cerberus/Cerberus.obj',
        function ( group ) {
            // 获取加载的模型的几何体
            const cerberus = group.children[ 0 ];
            const modelGeometry = cerberus.geometry;
            // 初始化边缘分割修改器
            modifier = new EdgeSplitModifier();
            // 合并几何体的重复顶点(优化几何体)
            baseGeometry = BufferGeometryUtils.mergeVertices( modelGeometry );
            // 创建网格并设置初始材质
            mesh = new THREE.Mesh( getGeometry(), new THREE.MeshStandardMaterial() );
            // 根据平滑着色选项设置材质的 flatShading 属性
            mesh.material.flatShading = ! params.smoothShading;
            // 旋转模型,使其正确朝向
            mesh.rotateY( - Math.PI / 2 );
            // 设置模型的缩放和位移
            mesh.scale.set( 3.5, 3.5, 3.5 );
            mesh.translateZ( 1.5 );
            // 将网格添加到场景中
            scene.add( mesh );
            // 如果设置了显示纹理,并且纹理已经加载,应用纹理到网格
            if ( map !== undefined && params.showMap ) {
                mesh.material.map = map;
                mesh.material.needsUpdate = true;
            }
            // 执行渲染
            render();
        }
    );

    // 加载纹理贴图
    new THREE.TextureLoader().load( 'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/models/obj/cerberus/Cerberus_A.jpg', function ( texture ) {
        // 加载纹理并设置颜色空间
        map = texture;
        map.colorSpace = THREE.SRGBColorSpace;
        // 如果网格已经创建,并且需要显示纹理,更新网格的材质
        if ( mesh !== undefined && params.showMap ) {
            mesh.material.map = map;
            mesh.material.needsUpdate = true;
        }
    });

    // 更新网格的几何体和材质
    function updateMesh() {
        // 切换切割角度的值(0 或 180)
        params.cutOffAngle = params.cutOffAngle == 0 ? 180 : 0;
        // 如果网格存在,更新其几何体和材质
        if ( mesh !== undefined ) {
            // 获取新的几何体
            mesh.geometry = getGeometry();
            // 检查材质是否需要更新
            let needsUpdate = mesh.material.flatShading === params.smoothShading;
            // 根据平滑着色参数调整材质的 flatShading 属性
            mesh.material.flatShading = params.smoothShading === false;
            // 如果纹理存在,并且显示纹理的选项被启用,更新纹理
            if ( map !== undefined ) {
                needsUpdate = needsUpdate || mesh.material.map !== ( params.showMap ? map : null );
                mesh.material.map = params.showMap ? map : null;
            }
            // 更新材质
            mesh.material.needsUpdate = needsUpdate;
        }
    }
    // 绑定 DOM 元素点击事件,点击时更新网格
    DOMEl.onclick = () => {
        updateMesh();
    }
    // 根据参数选择是否应用边缘分割,返回相应的几何体
    function getGeometry() {
        let geometry;
        // 如果启用了边缘分割,应用 EdgeSplitModifier
        if ( params.edgeSplit ) {
            geometry = modifier.modify(
                baseGeometry,
                params.cutOffAngle * Math.PI / 180,  // 将角度转为弧度
                params.tryKeepNormals  // 保留法线选项
            );
        } else {
            geometry = baseGeometry;  // 否则使用原始几何体
        }
        return geometry;
    }
相关推荐
Ling_suu2 分钟前
SpringBoot3——Web开发
java·服务器·前端
Yvemil79 分钟前
《开启微服务之旅:Spring Boot Web开发》(二)
前端·spring boot·微服务
hanglove_lucky11 分钟前
本地摄像头视频流在html中打开
前端·后端·html
维李设论14 分钟前
Node.js的Web服务在Nacos中的实践
前端·spring cloud·微服务·eureka·nacos·node.js·express
2401_8576009520 分钟前
基于 SSM 框架 Vue 电脑测评系统:赋能电脑品质鉴定
前端·javascript·vue.js
天之涯上上25 分钟前
Pinia 是一个专为 Vue.js 3 设计的状态管理库
前端·javascript·vue.js
@大迁世界32 分钟前
摆脱 `<div>`!7 种更语义化的 HTML 标签替代方案
前端·html
高山我梦口香糖1 小时前
[react] <NavLink>自带激活属性
前端·javascript·react.js
撸码到无法自拔1 小时前
React:组件、状态与事件处理的完整指南
前端·javascript·react.js·前端框架·ecmascript
高山我梦口香糖1 小时前
[react]不能将类型“string | undefined”分配给类型“To”。 不能将类型“undefined”分配给类型“To”
前端·javascript·react.js