THREE.LineBasicMaterial的线条粗细

48

我正在使用下面的代码在我的three.js场景中创建数百行

edgeGeometry[i] = new THREE.Geometry();
edgeGeometry[i].vertices[0] = v(x1,y1,z1);
edgeGeometry[i].vertices[1] = v(x2,y2,z2);
edgesMat[i] = new THREE.LineBasicMaterial({
    color: 0x6699FF, linewidth: 1, fog:true});
edge[i] = new THREE.Line(edgeGeometry[i], edgesMat[i]);
edge[i].type = THREE.Lines;
scene2.add(edge[i]);

它能正常工作,但当我将 "linewidth" 的值更改为更大或更小的值时,场景中没有任何区别。
我应该如何更改线条的粗细? 有什么建议吗?
谢谢,Dimitris


4
根据THREE.JS文档,由于在大多数平台上,WebGL渲染器的OpenGL核心配置文件的限制,linewidth属性始终为1,而不管设置了什么值。 - Cong Dan Luong
9个回答

36

1) 使用本地OpenGL

您可以通过将浏览器设置为使用本地OpenGL而不是ANGLE来实现线条粗细的渲染。 您可以在此处阅读有关如何在Chrome上执行此操作的说明。请注意,如果切换到本地OpenGL,您将会遇到性能差异。

编辑:

大师级人物MrDoob本人在此发布了如何在Chrome和Firefox上执行此操作的说明

注意: 由于最新的OpenGL版本也不再支持设置线条粗细,因此这个选项已经不再是有效的解决方案了。请参见 gman的答案。这意味着如果您要使用线条粗细,则第二个选项是正确的选择。


2) 使用THREE.MeshLine

还有另一种解决方案;github上的这个THREE.MeshLine是一个不错的解决方案。它附带了一个特殊的THREE.MeshLineMaterial。根据文档,只需执行以下步骤:

  • 创建并填充几何体。
  • 创建一个THREE.MeshLine并分配几何体。
  • 创建一个THREE.MeshLineMaterial
  • 使用THREE.MeshLineTHREE.MeshLineMaterial创建THREE.Mesh

1
three.meshline不支持buffergeometry。有人知道我该如何让它与buffergeometry一起工作吗? - Lordking
@FilipPetrovic 我仔细看了一下,似乎这个 MeshLineMaterial 的自定义着色器代码中并没有包含雾化着色器代码。你可以尝试添加它或者提出一个功能请求问题。 - Wilt
即将发布带有示例代码的答案。 - Andrew
如果您有最新版本的three.js,这些解决方法可能会有所帮助: https://github.com/spite/THREE.MeshLine/issues/133 我简直不敢相信我们已经进入21世纪了,却无法使用three.js修改线宽。 - Patryk Janik
3
这些解决方案已经过时,使用最新的ThreeJS不容易让Meshline正常工作。我现在会使用ThreeJS中的粗线实现,效果很好。请查看Line2类。 - Cryptovirus
显示剩余6条评论

21

你正在使用Windows吗?
我记得这在Windows上不能工作,因为它没有在ANGLE中实现。


是的,我正在使用Windows 7。既然你提到了这一点,那肯定是因为一个月前我在Ubuntu上运行我的应用程序时宽度完美地改变了。 我该如何让它在Windows上也能正常工作? - Dimitris
嗯,不太确定...这是一个ANGLE的限制。我不知道他们是否可以添加这个功能。 - mrdoob
谢谢您的快速回复……至少现在我知道不是我的代码导致了这个问题。 - Dimitris
很遗憾,无论我尝试什么,总是在一开始就遇到麻烦,也许我需要成为一个QA。我试图实现一个简单的时钟图像,现在我必须解决线宽的问题。我想我们可以尝试使用矩形代替线条(2像素宽的矩形)。 - Rantiev
4
我也在使用OS X Chrome时遇到了这个问题,刚刚找到了这份报告:https://github.com/mrdoob/three.js/issues/10357 - Matthew Taylor

12

这个问题发生在Windows Chrome和Firefox中,两者都使用ANGLE(WebGL到DirectX包装器)。

这个问题仍未被ANGLE项目解决。您可以在此处将其标记为星号,以获得更高的优先级,并在实施时获得通知:

https://code.google.com/p/angleproject/issues/detail?id=119


星星,我期待着它。 - Nathan Bernard
期待着,我很期待。 - undefined

11

我使用TubeGeometry在两点之间创建了一条粗线:

在Helix中可以看到绿色的线。

enter image description here

// line material
var lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00 });


let startVector = new THREE.Vector3(
    RADI * Math.cos(t),
    RADI * Math.sin(t),
    3 * t
  );
  let endVector = new THREE.Vector3(
    RADI * Math.cos(t + 10),
    RADI * Math.sin(t + 10),
    3 * t
  );

  let linePoints = [];
  linePoints.push(startVector, endVector);

  // Create Tube Geometry
  var tubeGeometry = new THREE.TubeGeometry(
    new THREE.CatmullRomCurve3(linePoints),
    512,// path segments
    0.5,// THICKNESS
    8, //Roundness of Tube
    false //closed
  );

  let line = new THREE.Line(tubeGeometry, lineMaterial);
  scene.add(line);

5
这不再是ANGLE特有的问题,它在所有平台上都存在。浏览器需要切换到OpenGL 4+核心配置文件以支持WebGL2,而OpenGL 4+核心配置文件不支持大于1的线宽。从OpenGL 4.0+规范的E.2.1节中可以看出。
“E.2.1已弃用但仍受支持的功能”
以下功能已弃用,但仍然存在于核心配置文件中。它们可能会在OpenGL的未来版本中被删除,并且在实现核心配置文件的向前兼容上下文中已被删除。
- Wide lines - 线宽大于1.0的LineWidth值将生成INVALID_VALUE错误。
要绘制更粗的线条,您需要生成几何图形。对于three.js,有这个库(Wilt也指出了)。

https://github.com/spite/THREE.MeshLine


截至今天(2018年2月26日),Safari实际上仍然可以正确地呈现lineWidth > 1,而最新的Chrome却不能;< - karni
是的,我在那里使用了一个不正确的单词。规格的改变确实很糟糕,但这就是生活;)。可能会使用来自github的THREE.LineMesh。 - karni
WebGL规范没有改变,实现方式有所不同。规范始终指出只需要支持1,对于大多数人来说,在Windows上也只支持1。如果您想要使用大于1的线条,并且希望您的应用程序在任何地方都能正常工作,您一直需要实现像THREE.MeshLine这样的解决方案。 - gman
是的,我现在正在使用THREE.MeshLine :)。干杯。 - karni
我有超过10k行,所以 THREE.MeshLine 对我来说不够用。我该怎么办?在服务器上将线条挤压成网格吗? - SalientBrain

1
你可以使用extrude-polyline来生成一个简单复合体,用于加粗(多边形)线条,然后使用three-simplicial-complex将其转换为three.js网格。
const THREE = require('three');
const extrudePolyline = require('extrude-polyline');
const Complex = require('three-simplicial-complex')(THREE);

function thickPolyline(points, lineWidth) {
  const simplicialComplex = extrudePolyline({
    // Adjust to taste!
    thickness: lineWidth,
    cap: 'square',  // or 'butt'
    join: 'bevel',  // or 'miter',
    miterLimit: 10,
  }).build(points);

  // Add a z-coordinate.
  for (const position of simplicialComplex.positions) {
    position[2] = 0;
  }

  return Complex(simplicialComplex);
}

const vertices = [[0, 0], [10, 0], [10, 10], [20, 10], [30, 00]];
const geometry = thickPolyline(vertices, 10);

const material = new THREE.MeshBasicMaterial({
  color: 0x009900,
  side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

如果您想对折线进行纹理映射,事情会变得更加复杂。

你可能会对这个three.js的pull request感兴趣。 - WestLangley
1
那个PR使用着色器来生成具有恒定线条厚度的线条。如果您想要在世界坐标系中生成真正具有特定厚度的折线(例如从其中心线生成道路),那么这种方法更为合理。 - danvk

1
您可以使用CanvasRenderer而不是Webglrenderer。在官方文档这里中查看,每个形状都有一个linewidth = 10的边界。

虽然此链接可能回答了问题,但最好在此处包含答案的基本部分并提供链接作为参考。如果链接页面更改,仅链接的回答可能会失效。 -【来自审核】 - Serafeim
1
@Serafeim的链接只是一个例子。答案是使用CanvasRenderer。 - raulsi

0
为什么不将不透明度设置为0.1之类的值呢? 注意:这只适用于给某些东西添加边框的情况,如果它后面没有任何东西,那么它将不起作用。

0

感谢 Wilt's answer 指引我正确的方向,使我能够使用 THREE.MeshLine

这可能比他们说的要棘手一些... 所以这是我的解决方案,我仔细跟随他们的文档和 演示 代码...(假设您已经包含了 Three 和 MeshLine):

    renderer = new THREE.WebGLRenderer({ canvas });

    //...

    function createCircle(resolution) {
        let circleGeometry = new THREE.Geometry();
        for (let rotation = 0; rotation <= Math.PI * 2.0; rotation += Math.PI * 0.1) {
            circleGeometry.vertices.push(
                new THREE.Vector3(Math.cos(rotation), Math.sin(rotation), 0));
        }
        let circleLine = new MeshLine();
        circleLine.setGeometry(circleGeometry);
        //Bonus: parabolic width! (See Z rotation below.)
        //circleLine.setGeometry(circleGeometry, function(point) {
            //return Math.pow(4 * point * (1 - point), 1);
        //});

        //Note: resolution is *required*!
        return new THREE.Mesh(circleLine.geometry,
            new MeshLineMaterial({
                color: 'blue',
                resolution,
                sizeAttenuation: 0,
                lineWidth: 5.0,
                side: THREE.DoubleSide
            }));
    }

    let circle = createCircle(new THREE.Vector2(canvas.width, canvas.height));
    circle.rotation.x = Math.PI * 0.5;
    circle.position.y = 20.0;
    scene.add(circle);

    //In update, to rotate the circle (e.g. if using parabola above):
    world.circle.rotation.z += 0.05;

关闭大小衰减并使用THREE.DoubleSide,如我上面所做的那样,无论从哪个角度看,圆圈看起来都像一个漂亮、一致的圆形(不是"真正的3D")。

对于只有一条线,你显然可以轻松地适应。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接