如何为@react-three/drei的MeshLine宽线应用顶点着色器?
解决方案:基于@react-three/drei 实现逐像素Y阈值变色宽线
核心思路是利用onBeforeCompile钩子在MeshLine的内置着色器中注入自定义逻辑,将世界空间坐标传递到片段着色器,实现逐像素的颜色判断,同时保留原有宽线的渲染能力。
实现代码
import { Line } from '@react-three/drei'; import { useRef } from 'react'; export default function ColoredWideLine() { const lineRef = useRef(null); return ( <Line ref={lineRef} // 自定义线的顶点数据 points={[[-1.5, -1, 0], [0, 1.2, 0], [1.5, -1, 0]]} lineWidth={0.12} // 基础颜色(会被片段着色器逻辑覆盖) color="#ff0000" onBeforeCompile={(shader) => { // 1. 在顶点着色器头部声明varying变量,用于传递世界坐标到片段着色器 shader.vertexShader = ` varying vec3 vWorldPosition; ${shader.vertexShader} `; // 2. 在顶点着色器中计算世界坐标,插入到原有位置变换逻辑之后 // 匹配MeshLine内置顶点着色器中的位置投影代码段 shader.vertexShader = shader.vertexShader.replace( '#include <project_vertex>', `#include <project_vertex> // 计算当前顶点的世界空间坐标 vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;` ); // 3. 在片段着色器头部声明对应的varying变量 shader.fragmentShader = ` varying vec3 vWorldPosition; ${shader.fragmentShader} `; // 4. 替换原有颜色输出逻辑,添加Y阈值判断 shader.fragmentShader = shader.fragmentShader.replace( 'gl_FragColor = vec4( vColor, opacity );', ` // 根据世界空间Y值切换颜色 vec3 finalColor = vColor; if (vWorldPosition.y >= 0.5) { finalColor = vec3(0.0, 1.0, 0.0); // 绿色 } gl_FragColor = vec4(finalColor, opacity); ` ); // 可选:添加可动态调整的阈值uniform shader.uniforms.yThreshold = { value: 0.5 }; // 如果用uniform,上面的判断改成:if (vWorldPosition.y >= yThreshold) }} /> ); }
关键细节说明
- 不破坏原有宽线逻辑:通过
onBeforeCompile扩展内置着色器,而非完全替换,保留MeshLine处理宽线的核心算法。 - 逐像素判断:将世界坐标传递到片段着色器后,每个像素都会独立判断Y值,避免顶点着色方式导致的色块过渡问题。
- 灵活扩展:可以将阈值设为uniform,后续通过
lineRef.current.material.uniforms.yThreshold.value动态修改阈值,无需重新渲染组件。
可能的适配调整
如果MeshLine的内置顶点着色器没有#include <project_vertex>片段,可以直接匹配gl_Position赋值语句进行替换:
shader.vertexShader = shader.vertexShader.replace( 'gl_Position = projectionMatrix * modelViewMatrix * vec4( transformed, 1.0 );', `gl_Position = projectionMatrix * modelViewMatrix * vec4( transformed, 1.0 ); vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;` );
内容的提问来源于stack exchange,提问作者Paul M.
相关产品推荐
相关产品推荐

