I'm working on visualizing paths of lines in Three.JS and have successfully added a bunch of lines to the scene with the correct vertices, and material that I want but the lines are hard to see. Is there a way to convert a line segment into a tube of sorts without having to start from scratch and change the type of geometry I'm using?
I may not be using the correct terminology but I basically want to turn a generated THREE.LineSegments() into a thicker line in 3D. Below is a snippet of my code:
var geo = new THREE.BufferGeometry();
geo.addAttribute('position', new THREE.BufferAttribute(new Float32Array(2*numTravelVertices), 3));
var travelVertices = geo.attributes.position.array;
var vertIndex = 0;
this.set('travelVertices', travelVertices);
<add vertex indicies for points on the path>
geo.rotateX(-Math.PI / 2);
var mat = new THREE.LineBasicMaterial({color: parseInt(this.get('travelColor')), transparent: false});
var lineSegments = new THREE.LineSegments(geo, new THREE.MultiMaterial([mat]));
You can draw thick lines by setting the LineBasicMaterial linewidth parameter:
material.linewidth = 4; // default is 1
This currently does not work on some Windows platforms. So an alternate solution is to use the thrid-party class THREE.MeshLine, which renders thick lines by drawing a strip of triangles.
You can use THREE.TubeGeometry, but that would not be as performant as MeshLine.
three.js r.82
Related
I am working with THREE.js and BufferGeometry to create a view of a set of points based on an image. Currently, I have the following:
As can be seen, the "object" that I have constructed has faces that can only be viewed from the opposite side as intended.
I have constructed my BufferGeometry with the a set of points with a corresponding array of normals, implemented like so (assuming that verts and norms are valid):
vertices = Float32Array.from(verts)
normals = Float32Array.from(norms)
// Make new geometry
geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3))
material = new THREE.MeshPhongMaterial({color: 0xffffff})
mesh = new THREE.Mesh(geometry, material)
geometry.computeVertexNormals()
scene.add(mesh)
As you can see from trying to add the array of normals, I have tried adding normals in which all are (0,1,0). Without geometry.computeVertexNormals(), this still results in none of the faces being solid/behaving correctly. With such, the result in the image is generated.
What could be causing the solid to behave as such?
I've got thousands of sparsely arranged curved lines (made with CubicBezierCurve3) being merged into one geometry for obvious performance reasons.
Is it possible to feed them all into a single THREE.Line without the interconnecting segments becoming connected? Or make those interconnecting line segments invisible?
LinePieces/LineSegments (line strip) would work, but it would double the number of vertices and doesn't seem like the right approach..
So yes you can in fact send over NaN to instruct OpenGL to jump. Then just give three.js a bogus empty bounding sphere (or do a proper calculation of if). The bogus bounding sphere will mess up normal calculation, which didn't matter in my use but might in others'. Here's the pseudocode solution:
var positions = [];
for(....)
{
...
var pts = curve.getPoints(...);
for(var p = 0; p < pts.length; p++)
positions.push(pts[p].x, pts[p].y, pts[p].z);
positions.push(NaN, NaN, NaN);
}
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array(positions), 3, true ) );
geometry.boundingSphere = new THREE.Sphere();
I'm working on a terrain generator, but I can't seen to figure out how to do the colors. I want to be able to generate an image that will take up my whole PlaneGeometry. My question is how can I create a single image that will cover the entire PlaneGeometry (with no wrapping) based off my height map? I can think of one way, but I'm not sure it would fully cover the PlaneGeometry and it would be very inefficient. I'd draw it in a two-dimensional view with colors on a canvas. I'd then convert the canvas to the texture Is that the best/only way?
UPDATE: Using DataTexture, I got some errors. I have absolutely no idea where I went wrong. Here's the error I got:
WebGL: drawElements: texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'. Or the texture is Float or Half Float type with linear filtering while OES_float_linear or OES_half_float_linear extension is not enabled.
Both the DataTexture and the PlaneGeometry have a size of 512^2. What can I do to fix this?
Here's some of the code I use:
EDIT: I fixed it. Here's the working code I used.
function genDataTexture(){
//Set the size.
var dataMap = new Uint8Array(1 << (Math.floor(Math.log(map.length * map[0].length * 4) / Math.log(2))));
/* ... */
//Set the r,g,b for each pixel, color determined above
dataMap[count++] = color.r;
dataMap[count++] = color.g;
dataMap[count++] = color.b;
dataMap[count++] = 255;
}
var texture = new THREE.DataTexture(dataMap, map.length, map[0].length, THREE.RGBAFormat);
texture.needsUpdate = true;
return texture;
}
/* ... */
//Create the material
var material = new THREE.MeshBasicMaterial({map: genDataTexture()});
//Here, I mesh it and add it to scene. I don't change anything after this.
The optimal way, if the data is already in your Javascript code, is to use a DataTexture -- see https://threejs.org/docs/#api/textures/DataTexture for the general docs, or look at THREE.ImageUtils.generateDataTexture() for a fairly-handy way to make them. http://threejs.org/docs/#Reference/Extras/ImageUtils
I'm trying to create a vertex animation for a mesh.
Just imagine a vertex shader, but in software instead of hardware.
Basically what I do is to apply a transformation matrix to each vertex. The mesh it's ok but the normals doesn't look good at all.
I've try to use both computeVertexNormals() and computeFaceNormals() but it just doesn't work.
The following code is the one I used for the animation (initialVertices are the initial vertices generated by the CubeGeometry):
for (var i=0;i<mesh1.geometry.vertices.length; i++)
{
var vtx=initialVertices[i].clone();
var dist = vtx.y;
var rot=clock.getElapsedTime() - dist*0.02;
matrix.makeRotationY(rot);
vtx.applyMatrix4(matrix);
mesh1.geometry.vertices[i]=vtx;
}
mesh1.geometry.verticesNeedUpdate = true;
Here there're two examples, one working correctly with CanvasRenderer:
http://kile.stravaganza.org/lab/js/dynamic/canvas.html
and the one that doesn't works in WebGL:
http://kile.stravaganza.org/lab/js/dynamic/webgl.html
Any idea what I'm missing?
You are missing several things.
(1) You need to set the ambient reflectance of the material. It is reasonable to set it equal to the diffuse reflectance, or color.
var material = new THREE.MeshLambertMaterial( {
color:0xff0000,
ambient:0xff0000
} );
(2) If you are moving vertices, you need to update centroids, face normals, and vertex normals -- in the proper order. See the source code.
mesh1.geometry.computeCentroids();
mesh1.geometry.computeFaceNormals();
mesh1.geometry.computeVertexNormals();
(3) When you are using WebGLRenderer, you need to set the required update flags:
mesh1.geometry.verticesNeedUpdate = true;
mesh1.geometry.normalsNeedUpdate = true;
Tip: is it a good idea to avoid new and clone in tight loops.
three.js r.63
I am using Three.js to generate a polyhedron with differing colors and text on each face, generated from a canvas element. For now, I'm sticking with polyhedra for which Three.js includes native classes, but at some point, I'd like to branch out into more irregular shapes.
There are a number of examples available online (including StackOverflow posts, like Three.js cube with different texture on each face) that explain how to do this with cubes. I haven't succeeded in finding any samples that show the same technique applied to non-cubes, but for the most part, the same process that works for CubeGeometry also works for TetrahedronGeometry and so forth.
Here's a simplified version of the code I'm using to generate the polyhedron:
switch (shape) {
case "ICOSAHEDRON" :
// Step 1: Create the appropriate geometry.
geometry = new THREE.IcosahedronGeometry(PolyHeatMap.GEOMETRY_CIRCUMRADIUS);
// Step 2: Create one material for each face, and combine them into one big
// MeshFaceMaterial.
material = new THREE.MeshFaceMaterial(createMaterials(20, textArray));
// Step 3: Pair each face with one of the materials.
for (x = 0; face = geometry.faces[x]; x++)
{
face.materialIndex = x;
}
break;
// And so on, for other shapes.
}
function createTexture (title, color) {
var canvas = document.createElement("canvas");
// Magical canvas generation happens here.
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return new THREE.MeshLambertMaterial({ map : texture });
}
function createMaterials (numFacets, textArray)
{
var materialsArray = [],
material;
for (var x = 0, xl = numFacets; x < xl; x++)
{
material = createTexture(textArray[x], generateColor(textArray[x]));
material.side = THREE.DoubleSide;
materials.push(oMaterial);
}
return materials;
}
Cubes render perfectly using this technique, but with other polyhedra, the textures do not behave as expected:
It's hard to explain precisely what's happening here. Essentially, each face is displaying the correct texture, but the texture itself has been stretched and shifted as if to cover the entire polyhedron. In other words - looking at the shape dead-on - the upper-left face is only showing the upper-left portion of its texture, the upper-right face is only showing the upper-right portion, and so on.
The faces on the opposite side of the polyhedron shows no texture detail at all; only colors.
I had no experience with 3D rendering prior to experimenting with Three.js, so I imagine that there's some step I'm missing that is handled automatically by CubeGeometry but not its sister classes. I'd refer to other examples that have been posted, but most examples are rendering cubes, and those that don't are usually using solid colors.
What needs to happen for the textures on the non-cube shapes to be scaled and centered properly?
You need to set new UVs.
I made a simple example how to do it, don't know if it's the best way.
jsFiddle example
Update
geometry.faceVertexUvs[0] = [];
for(var i = 0; i < geometry.faces.length; i++){
// set new coordinates, all faces will have same mapping.
geometry.faceVertexUvs[0].push([
new THREE.Vector2( 0,0 ),
new THREE.Vector2( 0,1 ),
new THREE.Vector2( 1,1),
]);
}