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

Three.js r83:如何实现模型的可动画笛卡尔网格线框?

解决Three.js r83中带动画模型的笛卡尔网格线框问题

我之前也踩过这个坑——用EdgesGeometry+LineSegments做的线框完全没法跟着模型的morph动画走,毕竟它是基于模型静态几何体生成的边缘数据,根本不会响应顶点的动态变化。不过用自定义着色器的思路确实是对的,下面给你一套适配r83的具体实现方案:


核心思路

既然要让线框跟着动画顶点同步更新,就得在顶点着色器里处理morphTarget的顶点偏移,然后在片元着色器里基于变换后的顶点位置生成笛卡尔网格线框,这样整个线框就会完全贴合模型的动画状态。

具体实现步骤

1. 编写顶点着色器(处理morph动画)

这个着色器的关键是叠加morphTarget的影响,把变换后的顶点位置传递给片元着色器:

varying vec3 vPosition;

void main() {
    // 初始化顶点位置为原始位置
    vec3 morphed = position;
    
    // 开启morphTargets支持,循环叠加每个目标的影响(r83的写法)
    #ifdef USE_MORPHTARGETS
        for (int i = 0; i < MORPH_TARGET_COUNT; i++) {
            morphed += morphTargetInfluences[i] * morphTargets[i];
        }
    #endif
    
    // 将顶点转换到世界空间,方便生成全局笛卡尔网格
    vPosition = (modelMatrix * vec4(morphed, 1.0)).xyz;
    
    // 正常输出裁剪空间位置
    gl_Position = projectionMatrix * modelViewMatrix * vec4(morphed, 1.0);
}

2. 编写片元着色器(生成笛卡尔网格)

这里用fract和fwidth函数来计算网格线,实现抗锯齿的笛卡尔风格线框:

varying vec3 vPosition;
uniform vec3 gridSize; // 网格间距,比如vec3(1.0,1.0,1.0)
uniform vec3 gridColor; // 网格线颜色
uniform vec3 backgroundColor; // 背景色(模型本身的颜色)

void main() {
    // 计算每个轴向上的网格线强度,fwidth用来适配不同距离的抗锯齿
    vec3 grid = abs(fract(vPosition / gridSize - 0.5) - 0.5) / fwidth(vPosition / gridSize);
    // 取三个轴向上最接近线框的那个值
    float line = min(min(grid.x, grid.y), grid.z);
    
    // 混合网格线和背景色,smoothstep实现平滑过渡
    vec3 color = mix(gridColor, backgroundColor, smoothstep(0.0, 1.0, line));
    
    gl_FragColor = vec4(color, 1.0);
}

3. 创建ShaderMaterial并应用到模型

注意一定要开启morphTargets: true,否则着色器不会处理morph动画:

// 假设你已经有了带morphTargets的模型mesh,以及动画clip
const cartesianGridMaterial = new THREE.ShaderMaterial({
    uniforms: {
        gridSize: { value: new THREE.Vector3(1, 1, 1) }, // 可根据需求调整间距
        gridColor: { value: new THREE.Color(0xffffff) },
        backgroundColor: { value: new THREE.Color(0x222222) }
    },
    vertexShader: document.getElementById('vertex-shader').textContent, // 或者直接写字符串
    fragmentShader: document.getElementById('fragment-shader').textContent,
    morphTargets: true, // 关键!必须开启才能支持morph动画
    // 可选:如果需要半透明效果可以开启transparent
    // transparent: true,
    // depthTest: true
});

// 替换模型原有材质
mesh.material = cartesianGridMaterial;

// 正常初始化AnimationMixer播放动画
const mixer = new THREE.AnimationMixer(mesh);
const clipAction = mixer.clipAction(yourAnimationClip);
clipAction.play();

额外调整建议

  • 如果想要更细的线框,可以调小gridSize,或者调整片元着色器里smoothstep(0.0, 0.8, line)的第二个参数(值越小线越细)
  • 要是需要基于模型局部坐标的网格,把顶点着色器里的vPosition = (modelMatrix * vec4(morphed, 1.0)).xyz改成vPosition = morphed就行
  • r83里的MORPH_TARGET_COUNT是Three.js自动注入的常量,不用手动定义,只要模型有morphTargets就会生效

内容的提问来源于stack exchange,提问作者P.S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:23:50