Three.js中LineBasicMaterial线宽设置无效的解决方法
问题描述
环境:Ubuntu 20.04、Firefox 107 x64
尝试通过LineBasicMaterial的linewidth属性修改Three.js中线的宽度,但设置后无效果,代码如下:
JavaScript代码
var camera, scene, renderer; init(); animate(); /** draw arrow */ function drawArrow(pick_pos, place_pos, line_width, scene) { /** * Curve */ const start = new THREE.Vector3(); start.add(pick_pos) const finish = new THREE.Vector3(); finish.add(place_pos) let mid = new THREE.Vector3(); mid.add(start); let dist = finish.x + mid.x; mid.x = dist/2; mid.y += 3; const b_curve = new THREE.QuadraticBezierCurve3( start, mid, finish ); const points = b_curve.getPoints( 100 ); const line_geometry = new THREE.BufferGeometry().setFromPoints( points ); const material = new THREE.LineBasicMaterial( { color: 0x0ffffb, linewidth: line_width } ); const curve = new THREE.Line( line_geometry, material ); scene.add(curve); } /** Create the scene, camera, renderer */ function init() { scene = new THREE.Scene(); scene.background = new THREE.Color(0x21252d); renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000); camera.position.x = 0; camera.position.y = 8; camera.position.z = 10; scene.add(camera); controls = new THREE.OrbitControls(camera, renderer.domElement); const pick = new THREE.Vector3(0, 0, 0); const place = new THREE.Vector3(5, 0, 5); drawArrow(pick, place, 1, scene); const pick2 = new THREE.Vector3(-5, 0, -5); const place2 = new THREE.Vector3(1, 0, 1); drawArrow(pick2, place2, 5, scene); window.addEventListener('resize', onWindowResize); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function animate() { requestAnimationFrame(animate); render(); } function render() { renderer.render(scene, camera); }
HTML代码
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/js/controls/OrbitControls.min.js"></script>
解决方案
原因
WebGL 1.0规范中,gl.lineWidth()的实现被大多数浏览器(包括Firefox)限制为仅支持线宽1,更高的数值不会生效。LineBasicMaterial的linewidth属性依赖该WebGL特性,因此无法直接设置大于1的线宽。
可行方案
方案1:使用Three.js官方Line2实现宽线
Line2通过网格模拟宽线,不受WebGL线宽限制,是官方推荐的解决方案。修改步骤如下:
- 引入
Line2相关依赖:
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/lines/Line2.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/lines/LineGeometry.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/jsm/lines/LineMaterial.js"></script>
- 重写
drawArrow函数:
function drawArrow(pick_pos, place_pos, line_width, scene) { const start = new THREE.Vector3().copy(pick_pos); const finish = new THREE.Vector3().copy(place_pos); let mid = new THREE.Vector3().copy(start); let dist = finish.x + mid.x; mid.x = dist/2; mid.y += 3; const b_curve = new THREE.QuadraticBezierCurve3(start, mid, finish); const points = b_curve.getPoints(100); // 转换为LineGeometry所需的位置数组 const positions = []; points.forEach(point => { positions.push(point.x, point.y, point.z); }); const lineGeometry = new LineGeometry(); lineGeometry.setPositions(positions); const lineMaterial = new LineMaterial({ color: 0x0ffffb, linewidth: line_width, // 此处线宽设置会生效 resolution: new THREE.Vector2(window.innerWidth, window.innerHeight) // 必须传入当前窗口分辨率 }); const curve = new Line2(lineGeometry, lineMaterial); curve.computeLineDistances(); // 计算线距离以确保正确渲染 scene.add(curve); }
- 更新窗口 resize 逻辑,同步
LineMaterial的分辨率:
function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); // 遍历场景更新所有LineMaterial的分辨率 scene.traverse(obj => { if (obj.material instanceof LineMaterial) { obj.material.resolution.set(window.innerWidth, window.innerHeight); obj.material.needsUpdate = true; } }); }
方案2:用平面几何体模拟宽线
若不想引入额外依赖,可手动将曲线转换为带宽度的平面:
- 计算曲线上每个点的切线方向
- 生成垂直于切线的两个偏移顶点,组成平面的顶点数据
- 使用
Mesh和MeshBasicMaterial渲染该平面
此方案实现稍复杂,适合简单曲线场景。
补充说明
- 若坚持使用
LineBasicMaterial,只能接受线宽为1的限制,这是WebGL 1.0的底层约束 Line2支持任意线宽,渲染效果更稳定,是宽线需求的首选方案
内容的提问来源于stack exchange,提问作者Bilal
相关产品推荐
相关产品推荐

