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

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线宽限制,是官方推荐的解决方案。修改步骤如下:

  1. 引入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>
  1. 重写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);
}
  1. 更新窗口 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 07:10:27