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.
相关产品推荐
相关产品推荐

