You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让等参曲线在ThreeJS的Verb NURBS曲面正反两面正确显示?

解决ThreeJS中Verb NURBS曲面等参曲线正反两面Z-fighting问题

这个问题我之前在做NURBS曲面可视化时也碰到过,线框模式的对角线确实不符合需求,而共面曲线和曲面的Z-fighting真的很棘手。给你几个实用的解决方案,按推荐程度排序:

方案1:给曲线顶点添加法向偏移(最推荐)

核心思路是让曲线稍微“脱离”曲面一点,不管正面还是背面,都和曲面保持微小距离,从根源避免Z-fighting。因为Verb的NURBS曲面可以精准获取任意点的法向量,我们可以基于这个来偏移曲线顶点:

// 提前定义曲线材质
const lineMat = new THREE.LineBasicMaterial({ color: 0x000000, side: THREE.DoubleSide });

getIsoCurves(srf_ft, 4).forEach(verbCurve => {
    // 转换为ThreeJS曲线并生成采样点
    const threeCurve = verbCurve.toThreeCurve();
    const originalPoints = threeCurve.getPoints(100); // 采样密度按需调整
    
    // 生成两组偏移点:一组向曲面正面偏移,一组向背面偏移
    const offsetFront = originalPoints.map(point => {
        // 把三维点转回曲面UV参数,确保法向量计算准确
        const uv = srf_ft.closestPoint(point);
        const normal = srf_ft.normal(uv[0], uv[1]).normalize();
        return point.clone().add(normal.multiplyScalar(0.001)); // 偏移量根据场景缩放调整
    });
    const offsetBack = originalPoints.map(point => {
        const uv = srf_ft.closestPoint(point);
        const normal = srf_ft.normal(uv[0], uv[1]).normalize();
        return point.clone().add(normal.multiplyScalar(-0.001));
    });
    
    // 创建正反两组曲线并添加到场景
    const frontGeo = new THREE.BufferGeometry().setFromPoints(offsetFront);
    obj.add(new THREE.Line(frontGeo, lineMat));
    
    const backGeo = new THREE.BufferGeometry().setFromPoints(offsetBack);
    obj.add(new THREE.Line(backGeo, lineMat));
});

注意:偏移量要匹配你的场景缩放——如果场景单位是米,0.001可能太小,换成0.01试试;如果是厘米级,0.001就足够。Verb的closestPoint方法能帮我们把三维点转换为曲面的UV参数,保证法向量计算精准。

方案2:使用PolygonOffset(原生解决Z-fighting)

ThreeJS提供了专门的多边形偏移参数,不需要修改顶点数据,直接通过材质设置来拉开曲面和曲线的深度层级:

// 曲面材质设置:让曲面稍微向相机方向偏移
activeMat.side = THREE.DoubleSide;
activeMat.polygonOffset = true;
activeMat.polygonOffsetFactor = -1;
activeMat.polygonOffsetUnits = -1;

// 曲线材质设置:让曲线稍微远离相机方向,和曲面拉开距离
const lineMat = new THREE.LineBasicMaterial({
    color: 0x000000,
    side: THREE.DoubleSide,
    polygonOffset: true,
    polygonOffsetFactor: 1,
    polygonOffsetUnits: 1
});

polygonOffsetFactor和polygonOffsetUnits的组合值需要微调:负数会让物体靠近相机,正数则远离。这个方法的好处是不需要修改几何数据,适合不想动顶点的场景。

方案3:禁用曲面的深度写入(适合简单场景)

如果你的场景里只有这个曲面和曲线,没有其他复杂的遮挡关系,可以让曲面不写入深度缓冲,这样曲线会始终显示在曲面上方:

// 曲面材质关闭深度写入
activeMat.side = THREE.DoubleSide;
activeMat.depthWrite = false;

// 曲线材质保持默认的深度写入和测试
const lineMat = new THREE.LineBasicMaterial({ color: 0x000000, side: THREE.DoubleSide });

缺点是如果有其他物体在曲面后面,会被曲面挡住(因为曲面虽然不写深度,但还是会参与深度测试)。如果连这个都要避免,可以把曲面的depthTest也设为false,但这样曲面会显示在所有物体前面,可能不符合需求。

最后别忘了确认曲面材质是双面的,否则背面的曲面不会渲染,曲线在背面会看起来悬空:activeMat.side = THREE.DoubleSide;

内容的提问来源于stack exchange,提问作者Amit Nambiar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:12:17