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;
    }
相关推荐
程楠楠&M几秒前
mongoDB的安装及使用
前端·数据库·mongodb·node
好奇的菜鸟15 分钟前
Go语言中的类型
服务器·前端·golang
hummhumm17 分钟前
第 14 章 -Go语言 错误处理
java·开发语言·前端·后端·python·sql·golang
linzhisong26 分钟前
LayUI组件国际化多国语言版本脚本-上篇提取中文字符
前端·javascript·python·layui
灵易联盟38 分钟前
Electron教程1-初学入门
前端·javascript·electron
Acacia.~1 小时前
第八章 利用CSS制作导航栏
前端·css
摆烂工程师1 小时前
GPT4变笨了?教你解决GPT4降智问题!同时封装了个Chrome扩展程序进行检查GPT
前端·后端·程序员
编程老船长1 小时前
网页设计基础 第十九讲:CSS定位实战 —— 打造精美布局的个人简介页
前端·css·html
snow_wind_rain2 小时前
网页作业9
前端·css·css3
zhzhzhen_2 小时前
如何在项目中用elementui实现分页器功能
前端·javascript·elementui