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;
    }
相关推荐
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte5 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
NEXT065 小时前
前端算法:从 O(n²) 到 O(n),列表转树的极致优化
前端·数据结构·算法
剪刀石头布啊6 小时前
生成随机数,Math.random的使用
前端