40

I am using the code below to create hundreds of lines in my three.js scene

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]);

It works just fine, but when i change the value of "linewidth" to a bigger OR smaller value, i see NO difference in the scene.
How should i change the thickness of the lines? Any ideas?
Thanks, Dimitris

mrdoob
  • 18,129
  • 3
  • 56
  • 59
Dimitris
  • 2,937
  • 4
  • 21
  • 27
  • 1
    Hello, according to THREE.JS [document](https://threejs.org/docs/#api/en/materials/LineBasicMaterial): "Due to limitations of the OpenGL Core Profile with the WebGL renderer on most platforms linewidth will always be 1 regardless of the set value." – Cong Dan Luong Oct 12 '18 at 03:30

8 Answers8

31

1) Use native OpenGL

You can achieve rendering of line thicknesses with a workaround by setting your browser to use native OpenGL instead of ANGLE. You can read here on how to do this on Chrome. Keep in mind that you will experience performance differences if you swap to native OpenGL.

EDIT:

The master MrDoob himself posted here how to do this for both Chrome and Firefox.

Note: This first option is no longer a valid solution since the latest OpenGL versions no longer support line thickness either. Check also @gman his answer. This means if you want to use line thickness the second option is the way to go.


2) Use THREE.MeshLine class

There is also another solution; this THREE.MeshLine class on github is a nice workaround. It comes with a special THREE.MeshLineMaterial. According to the docs it is as simple as:

  • Create and populate a geometry
  • Create a THREE.MeshLine and assign the geometry
  • Create a THREE.MeshLineMaterial
  • Use THREE.MeshLine and THREE.MeshLineMaterial to create a THREE.Mesh
Community
  • 1
  • 1
Wilt
  • 33,082
  • 11
  • 129
  • 176
  • Using Native OpenGL is no longer a solution - maybe you want to update your answer? – gman Jan 28 '17 at 15:36
  • @gman Thanks for your comment. Could you add some reference to the source of this information. Then I can update my question and add a reference to that source. – Wilt Jan 29 '17 at 09:57
  • I did, see my answer – gman Jan 29 '17 at 14:41
  • The three.meshline doesn't support buffergeometry. anyone know how i can make it work with buffergeometry ? – Lordking Mar 13 '18 at 22:15
  • Not the best option, isn't affected by THREE.Fog – Filip Petrovic Apr 09 '18 at 11:39
  • @FilipPetrovic If you want your material to be affected by fog you need to set `true` for the fog attribute. See the reference [here in the documentation](https://threejs.org/docs/#api/materials/Material.fog). Did you try this? Does it still not work? – Wilt Apr 10 '18 at 07:32
  • @FilipPetrovic I looked a bit more closely, seems the fog shader code is not included in the custom shader code for this `MeshLineMaterial`. You could try to add this or make an issue with a feature request. – Wilt Apr 10 '18 at 07:44
  • About to post an answer with example code for this. – Andrew Apr 19 '20 at 22:46
  • If you have newest versions of three js those workarounds can be helpful: https://github.com/spite/THREE.MeshLine/issues/133 I can't believe we are in the XXI century and you cannot modify linewidth with three.js – Patryk Janik Mar 26 '21 at 14:48
20

Are you using Windows?
I remember this not working on Windows because it wasn't implemented in ANGLE.

mrdoob
  • 18,129
  • 3
  • 56
  • 59
  • Yes I am using Windows 7. Now that you mention it, it must be that because i run my application on Ubuntu a month ago and the width changed perfectly. How can I make it work in Windows also? – Dimitris Jul 25 '12 at 10:11
  • Uhm, not sure... It's a ANGLE limitation. I don't know if it's something they can add. – mrdoob Jul 25 '12 at 10:20
  • Thanx for the quick replies ... At least now I know it's not an error in my code that's causing the issue... – Dimitris Jul 25 '12 at 10:55
  • It's a pity. Whatever i try, i got into any trouble in the beginning always, i might need to be a QA. Have tried to implement simple clock image, and now i have to override problem with line width ) I think we can try rectangles instead lines (2px width rectangles) – Rantiev Jun 08 '16 at 11:44
  • 4
    This is happening for me on OS X Chrome as well, just found this report about it: https://github.com/mrdoob/three.js/issues/10357 – Matthew Taylor Feb 10 '17 at 18:33
12

This occurs in Windows Chrome and Firefox, both using ANGLE (WebGL to DirectX wrapper).

The issue is still not solved by the ANGLE project. You can star the issue here to get higher priority and get a notification if it's going to be implemented:

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

user2090712
  • 121
  • 1
  • 2
4

This is no longer an issue just in ANGLE it's an issue on all platforms. Browsers needed to switching to the OpenGL 4+ core profile to support WebGL2 and the OpenGL 4+ core profile does not support line widths greater than 1. From the OpenGL 4.0+ spec, section E.2.1

E.2.1 Deprecated But Still Supported Features

The following features are deprecated, but still present in the core profile. They may be removed from a future version of OpenGL, and are removed in a forward compatible context implementing the core profile.

  • Wide lines - LineWidth values greater than 1.0 will generate an INVALID_VALUE error.

To draw thicker lines you need generate geometry. For three.js there is this library (pointed out by Wilt as well)

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

Community
  • 1
  • 1
gman
  • 83,286
  • 25
  • 191
  • 301
  • As of today (26 Frb 2018), Safari actually still properly renders lineWidth > 1, whereas up-to-date Chrome does not ; – karni Feb 26 '18 at 21:17
  • Yes, I used an incorrect word there. It really sucks the spec changed, but that's life ;). Will probably use THREE.LineMesh from github. – karni Feb 27 '18 at 00:15
  • The WebGL spec didn't change, the implementations did. The spec has always said 1 is all that has to be supported and on windows for most people that's all that's ever been supported. If you ever wanted lines > 1 and you wanted your app to work everywhere you've always had to implement solutions like THREE.MeshLine. – gman Feb 27 '18 at 00:23
  • Yes, I'm using THREE.MeshLine now :). Cheers. – karni Feb 28 '18 at 01:58
  • I have > 10k lines so THREE.MeshLine is not sufficient for me. What can I do? Extrude lines to meshes on server? – SalientBrain Mar 22 '18 at 23:36
3

I use TubeGeometry to create a Thick line between two points:

See Green lines in 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);
Hitesh Sahu
  • 31,496
  • 11
  • 150
  • 116
1

You can use CanvasRenderer instead of Webglrenderer. Check out the ifficial documentation here where each shape has a border of linewidth = 10;

raulsi
  • 41
  • 1
  • 5
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/10593534) – Serafeim Dec 17 '15 at 06:48
  • 1
    @Serafeim link is just an example. answer is to use the CanvasRenderer. – raulsi Dec 17 '15 at 11:19
1

You can achieve the same effect using extrude-polyline to generate a simplicial complex for the thickened (poly)line and three-simplicial-complex to convert this to a three.js Mesh:

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);

If you want to texture map the polyline, things get a little more complicated.

morph3us
  • 177
  • 1
  • 10
danvk
  • 13,227
  • 3
  • 51
  • 86
  • You might find [this three.js pull request](https://github.com/mrdoob/three.js/pull/11349) of interest. – WestLangley Nov 09 '17 at 20:09
  • 1
    That PR uses a shader to generate lines of constant thickness. If you'd like to generate polylines which really do have a specific thickness in world coordinates (e.g. generating a road from its centerline), then this approach makes more sense. – danvk Nov 10 '17 at 15:30
0

Thanks to Wilt's answer for pointing me in the right direction with THREE.MeshLine.

It can be slightly trickier than they make it out to be, however... So here's my solution following their docs and their demo code very carefully... (assuming you've already included Three and 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;

With size attenuation off and using THREE.DoubleSide, like I did above, the circle will look like a nice, consistent circle no matter where you're looking at it from (not "true 3D").

For just a line, you can obviously easily adapt.

Andrew
  • 3,330
  • 1
  • 33
  • 53