Mesh replacement for THREE.Line
Instead of using GL_LINE, it uses a strip of triangles billboarded.
MeshLine to plot graphsMeshLine with textureMeshLine rendering SVG PathsMeshLine created from a meshMeshLine.advance() by @caramelcode (Jared Sprague) and @mwcz (Michael Clayton)Disclaimer: this library is from 2015. three.js has had lines with width in its addons since r91 (2018): Line2, LineSegments2 and LineMaterial, with widths in pixels or in world units, dashes, and colors per vertex. If that’s all you need, use those. There’s also meshline, a fork of this library maintained by pmndrs
What this library has that the lines of three.js don’t: a width that changes along the line, a texture along the line, a visibility to draw only part of it, and advance() to move a trail cheaply.
The library has been updated for current three.js, so code written for version 1.x might need changes. What changed:
require are gonesizeAttenuation off, lineWidth is the width in pixels. It used to be half of that, so those lines are now twice as thick: halve the value to get the old lookresolution is not required anymore. The material takes the size of what it’s being rendered to, canvas or render target, and follows it when it changesvisibility, are not drawn at all. They used to be drawn transparent, hiding what was behind themmaterial.fog = false to leave them outsetPoints() takes any typed array, an array of vectors or a BufferAttribute too, and throws on anything elsematerial.clone() and material.copy() workvisibility into account, and returns the closest hit onlyUse npm to install it, alongside three.js
npm i three three.meshline
and import it in your code
import * as THREE from 'three';
import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline';
It’s a plain ES module, so it also works without a bundler as long as an import map resolves three (see demo/index.html).
First, create the list of numbers that will define the 3D points for the line.
const points = [];
for (let j = 0; j < Math.PI; j += (2 * Math.PI) / 100) {
points.push(Math.cos(j), Math.sin(j), 0);
}
An array of THREE.Vector3, any typed array and a BufferAttribute work as well. MeshLine also accepts a BufferGeometry looking up the vertices in it.
const points = [];
for (let j = 0; j < Math.PI; j += 2 * Math.PI / 100) {
points.push(new THREE.Vector3(Math.cos(j), Math.sin(j), 0));
}
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new MeshLine();
line.setGeometry(geometry);
Once you have that, you can create a new MeshLine, and call .setPoints() passing the list of points.
const line = new MeshLine();
line.setPoints(points);
Note: .setPoints accepts a second parameter, which is a function to define the width in each point along the line. By default that value is 1, making the line width 1 * lineWidth in the material.
// p is a decimal percentage of the number of points
// ie. point 200 of 250 points, p = 0.8
line.setPoints(points, p => 2); // makes width 2 * lineWidth
line.setPoints(points, p => 1 - p); // makes width taper
line.setPoints(points, p => 2 + Math.sin(50 * p)); // makes width sinusoidal
To change a line, call .setPoints() again. If the number of points is the same, the buffers are reused. For a trail there’s .advance(), which drops the first point and adds a new one at the end:
line.advance(new THREE.Vector3(x, y, z));
A MeshLine needs a MeshLineMaterial:
const material = new MeshLineMaterial(OPTIONS);
By default it’s a white material of width 1 unit.
MeshLineMaterial has several attributes to control the appereance of the MeshLine:
map - a THREE.Texture to paint along the line (requires useMap set to true)useMap - tells the material to use map (0 - solid color, 1 use texture)alphaMap - a THREE.Texture to use as alpha along the line (requires useAlphaMap set to true)useAlphaMap - tells the material to use alphaMap (0 - no alpha, 1 modulate alpha)repeat - THREE.Vector2 to define the texture tiling (applies to map and alphaMap - MIGHT CHANGE IN THE FUTURE)color - THREE.Color, or anything it can be made from, to paint the line with, or tint the texture withopacity - alpha value from 0 to 1 (requires transparent set to true)alphaTest - cutoff value from 0 to 1dashArray - the length and space between dashes. (0 - no dash)dashOffset - defines the location where the dash will begin. Ideal to animate the line.dashRatio - defines the ratio between that is visible or not (0 - more visible, 1 - more invisible).visibility - how much of the line is drawn, from the start (0 - nothing, 1 - the whole line). Ideal to animate the line being drawn.sizeAttenuation - makes the line smaller with distance, like any other object (1 - attenuate, the default; 0 - don’t attenuate, the width is constant on screen)lineWidth - float defining width (if sizeAttenuation is true, it’s world units; else is screen pixels)resolution - THREE.Vector2 with the size that the pixels of lineWidth refer to. Optional: by default it’s the size of the canvas, or of the render targetIf you’re rendering transparent lines or using a texture with alpha map, you should set depthTest to false, transparent to true and blending to an appropriate blending mode, or use alphaTest.
Finally, we create a mesh and add it to the scene:
const mesh = new THREE.Mesh(line, material);
scene.add(mesh);
You can optionally add raycast support with the following.
mesh.raycast = MeshLineRaycast;
The intersection has the point on the line, the distance, and the index of the segment that was hit. Lines with sizeAttenuation off need the camera, so use raycaster.setFromCamera() with them.
THREE.meshline can be used declaritively. This is how it would look like in react-three-fiber. The live examples below were made with version 1.x.
import { extend, Canvas } from '@react-three/fiber'
import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline'
extend({ MeshLine, MeshLineMaterial })
function Line({ points, width, color }) {
return (
<Canvas>
<mesh raycast={MeshLineRaycast}>
<meshLine attach="geometry" points={points} />
<meshLineMaterial
attach="material"
transparent
depthTest={false}
lineWidth={width}
color={color}
dashArray={0.05}
dashRatio={0.95}
/>
</mesh>
</Canvas>
)
}
Dynamic line widths can be set along each point using the widthCallback prop.
<meshLine attach='geometry' points={points} widthCallback={pointWidth => pointWidth * Math.random()} />
three.js r155 or later, on any browser that runs it. Tested on r155, r163 and r186. For older versions of three.js use version 1.x of this library.
MIT licensed
Copyright (C) 2015-2016 Jaume Sanchez Elias, https://www.clicktorelease.com