I want to create a line that smoothly curves into space and is colored by gradient, but also has 3d properties (something I do not get 100% from the MeshLine library, and closely approximates the fat-lines example - https://threejs.org/examples/webgl_lines_fat.html)
Issues I'm facing:
- Creating gradient or shading on line
- Making line "solid"
- Line "resolution" (seems jagged) when using MeshLine
- I would like to get advantage of MeshLine's easy-to-animate properties in the long term
Do you have any suggestions on how I could achieve that? It is my first time experimenting with THREE.js so I'm a bit of a novice when it comes to its limitations
My code so far:
function makeLine(geometry, color, width) {
var newLine = new MeshLineGeometry()
newLine.setPoints(geometry)
var material = new MeshLineMaterial({
//map - a THREE.Texture to paint along the line (requires useMap set to true)
useMap: false, //tells the material to use map (0 - solid color, 1 use texture)
//alphaMap - a THREE.Texture to paint 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: new THREE.Color(color), //THREE.Color to paint the line width, or tint the texture with
opacity: 1, //alpha value from 0 to 1 (requires transparent set to true)
//alphaTest - cutoff value from 0 to 1
//dashArray - 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).
resolution: resolution, //THREE.Vector2 specifying the canvas size (REQUIRED)
sizeAttenuation: false, //makes the line width constant regardless distance (1 unit is 1px on screen) (0 - attenuate, 1 - don't attenuate)
lineWidth: width, //float defining width (if sizeAttenuation is true, it's world units; else is screen pixels)
//near: camera.near,
//far: camera.far
})
// Create a new Mesh and set its geometry and material, then add it to the graph
var mesh = new MeshLine(newLine, material)
graph.add(mesh)
}
// Define the init function to create the lines
function init() {
createLines();
createAxes();
createCurve();
createSpline()
}
function createSpline() {
// create a curve using CatmullRomCurve3, which is a type of curve that smoothly interpolates points in 3D space
var spline = new THREE.CatmullRomCurve3(
[
new THREE.Vector3(-30, 2, 0), // starting point of the curve
new THREE.Vector3(10, 10, 0),
new THREE.Vector3(30, 1, 0),
new THREE.Vector3(-30, 12, 20) // ending point of the curve
]
);
var divisions = Math.round(12 * spline.points.length);
console.log(spline.points.length);
var positions = [];
var colors = [];
var color = new THREE.Color();
for (var i = 0, l = divisions; i < l; i++) {
var point = spline.getPoint(i / l);
positions.push(point.x, point.y, point.z);
color.setHSL(i / l, 1.0, 0.5);
colors.push(color.r, color.g, color.b);
}
makeLine(positions, 'red', 40);
}
function createCurve() {
// create a curve using CatmullRomCurve3, which is a type of curve that smoothly interpolates points in 3D space
var curve = new THREE.CatmullRomCurve3(
[
new THREE.Vector3(-30, 2, 0), // starting point of the curve
new THREE.Vector3(10, 10, 0),
new THREE.Vector3(30, 1, 0),
new THREE.Vector3(-30, 12, 20) // ending point of the curve
]
);
// define the number of points that will be used to create the ribbon
var pointsCount = 100;
// increase the number of points to create a more detailed ribbon
var pointsCount1 = pointsCount + 1;
// create a new array of points by offsetting the original points along the z-axis
var points = curve.getPoints(pointsCount);
var width = 5; // the width of the ribbon
var widthSteps = 1; // the number of steps to use when creating the ribbon width
let ptsZ = curve.getPoints(pointsCount);
ptsZ.forEach(p => {
p.z += width; // move each point in pts2 along the z-axis by the width of the ribbon
});
points = points.concat(ptsZ); // concatenate the two arrays of points to create the final array of points for the ribbon
// create a BufferGeometry from the array of points
var ribbonGeom = new THREE.BufferGeometry().setFromPoints(points);
// create the faces of the ribbon using the indices of the points in the array
var indices = [];
for (var iy = 0; iy < widthSteps; iy++) { // iterate over the number of steps in the width of the ribbon
for (var ix = 0; ix < pointsCount; ix++) { // iterate over the number of points along the curve
var a = ix + pointsCount1 * iy;
var b = ix + pointsCount1 * (iy + 1);
var c = (ix + 1) + pointsCount1 * (iy + 1);
var d = (ix + 1) + pointsCount1 * iy;
// create two faces for each "quad" of points, which will create the shape of the ribbon
indices.push(a, b, d);
indices.push(b, c, d);
}
}
ribbonGeom.setIndex(indices); // set the indices for the BufferGeometry to define the faces of the ribbon
ribbonGeom.computeVertexNormals(); // compute the vertex normals for the ribbon, which will be used for shading
// create a new Mesh using the BufferGeometry and a Material, and add it to the scene
var ribbon = new THREE.Mesh(
ribbonGeom,
new THREE.MeshNormalMaterial({
side: THREE.DoubleSide // make the ribbon double-sided to ensure it's visible from all angles
})
);
scene.add(ribbon);
}
function createAxes() {
var axesColor = 0x5ca4a9 //color of graph
var axesWidth = 10 //width of graph
//x-axis
var line = [];
line.push(new THREE.Vector3(-30, -30, -30)); //Point 1 x, z, y
line.push(new THREE.Vector3(30, -30, -30));//Point 2 x, z, y
//line connects points 1 and 2
//more points can be added to create more complex lines
makeLine(line, axesColor, axesWidth);
//z-axis (vertical)
var line = [];
line.push(new THREE.Vector3(-30, -30, -30));
line.push(new THREE.Vector3(-30, 30, -30));
makeLine(line, axesColor, axesWidth);
//y-axis
var line = [];
line.push(new THREE.Vector3(-30, -30, -30));
line.push(new THREE.Vector3(-30, -30, 30));
makeLine(line, axesColor, axesWidth);
}
function createLines() {
// Creates a Float32Array for line data with 600 positions
var line = new Float32Array(600);
// For loop that increments by 3
for (var j = 0; j < 200 * 3; j += 3) {
// Assigns x, y, and z coordinates to line array
line[j] = -30 + .1 * j;
line[j + 1] = 5 * Math.sin(.01 * j);
line[j + 2] = -20;
}
// Calls makeLine function and passes in the line array and color index 0
makeLine(line, colors[0], 10);
}
}
Code is based on: http://spite.github.io/THREE.MeshLine/demo/graph.html How Can I Convert a THREE.CatmullRomCurve3 to a Mesh?
Tried using MeshLine and fat-lines, no progress