Threejs draw diiferent lines - javascript

We use three.js to draw some maps. On a map there are should be some lines (like some borders, isolines). So we know how to draw just dashed lines. But also I want to draw lines with such type of patterns. So what would be better way to do that? Texturing?
UPD:
Map is gonna be 3D (but the lines are not), you can rotate it and zoom it etc.
It uses WebGl renderer.

Showing you exactly what needs to be done to achieve all this would cover several tutorials by itself, so I'm going to link you to some.
You could start here: http://www.html5canvastutorials.com/tutorials/html5-canvas-element ... that'll give you a basic page and a canvas to start drawing on, and various instructions on how to draw some shapes (this is going to be the hardest part for you; it'll require many different draw calls for these various shapes and it'll probably be somewhat difficult to position them correctly on your map). I'd suggest you create functions to draw the various basic shapes you need, and you will need plenty of 2D math in these functions which are perhaps worthy of SO questions by themselves.
To make dashed lines like in your example picture, http://www.rgraph.net/blog/2013/january/html5-canvas-dashed-lines.html is useful. Anything more advanced than that you'll have to draw yourself though, combining various shapes on the canvas.
Also note you'll want to use a canvas size where the width and height are a power of two, so use something like 512x512 or 1024x1024 before you use it as a texture.
Once you have your canvas rendered the way you want it to, applying it to an object in a three.js scene is pretty easy:
var texture = new THREE.Texture( canvas );
var material = new THREE.MeshBasicMaterial({ map: texture });
var geometry = new THREE.PlaneGeometry( 10, 10 );
var plane = new THREE.Mesh( geometry, material );
scene.add( plane );
You'll have to use a more advanced material if you're not copying the map you want to draw on to the canvas first as you can only apply limited textures to a single surface. But, get your 2D drawing sorted first as that is the hard part.

Related

Set material to a fixed part of mesh in Three.js

Let's say I have a 3DObject in three.js:
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const mesh = new THREE.Mesh(geometry, material);
This mesh is also dynamic, that means, user sets the size of it, which is dynamically set as the scale of the mesh.
widthInput.subscribe(value => this.mesh.scale.x = +value); // just to get the idea
So, I know it's possible to set separate materials to the different sides of geometry. I'm also aware that it should be possible to set it on separate segments of that geometry's sides (if I'd had more).
The problem is that the user can set the width from the range 200 - 260, but I need a different material on the very right of the mesh with a fixed size of 10. I'm not really sure how would I do that without creating another geometry. Is there any way to set the material on the fixed part of the mesh? or is there any way to set the segments the way one of them will always have the fixed size? Thank you in advance.
To visualize the problem (white area needs to have a fixed width of 10 while the red area resizes)
Is there any way to set the material on the fixed part of the mesh?
As you've already mentioned there is a way to set different materials on different parts of the geometry. The problem here is defining what fixed means:
or is there any way to set the segments the way one of them will always have the fixed size?
Yes. You'd have to modify the geometry yourself. Reach into say g.attributes.position.array and modify the vertices that make the segment. It's lower level and different than the scene graph.
There may not be a good reason for wanting to keep everything in the same geometry. It would make sense if you used vertex colors for example to paint the different segments, and perhaps animated the extrusion in GLSL rather than with scale.set(). But since you want to apply different materials and are not writing GLSL, you will end up with multiple draw calls anyway.
You actually can save a bit of memory by storing just the simple cube and referencing it twice, than storing extra vertices and faces. So what you're trying to do is more likely going to consume more memory, and have the same amount of render overhead. In which case, doing everything with the scene graph, with two meshes and one geometry (you dont need to duplicate the box, you only need two nodes) should be as performant, and much easier to work with.

Threejs - Best way to handle partial transforms on geometry vertices

I have mesh with a geometry of about 5k vertices. This is the result of multiple geometries being merged so it's all in a flat array. So far all good, I can modify individual vertices and by setting the flag verticesNeedUpdate=true I can see the changes reflected on my mesh. My problem is that I need to make translations and rotations on some of this vertices and I was wondering if there is better way of applying transforms for each collection apart from modifying each vertex position inside a loop to apply the transforms.
One idea I had was to create a new geometry and assign the vertices subset (by reference) so I could then do the transforms but that seems weird so I stopped and ask this question instead.
I took a look at this example https://threejs.org/examples/?q=constr#webgl_buffergeometry_constructed_from_geometry but I have no idea how would I go about rotating/scaling groups of vertices.
Also from what I understand this will reduce the calls to the GPU by uploading only one set of vertices instead of having hundreds of calls. But then I wonder if modifying this geometry vertices on each frame defeats the all purpose of doing all this?
As i can see, the example creates multiple heartshapes (geometry) and applies a transformation, then the transformed vertices are combined to one geometry (bufferGeometry). So all hearts are in the same geometry and drawn in one call. The downside is, that you can't manipulate the heart individually.
The clue here is that the transformations are done initially and the transformed coords are uploaded to the gpu. You don't want to update the vertices each frame by the cpu.
geometry.lookAt( vector );
geometry.translate( vector.x, vector.y, vector.z );
is responsible for transforming the vertices, before they are added to the bufferGeometry.
If you can add an 'index' to each vertex, you could use a UBO for storing matrices and give vertices different transformations (in vertexshader) within the same drawcall.

THREE.js Render target texture will not draw in a different scene

So if any THREE.js pros can understand why I can't get the WebGLRenderTarget to be used as a material for a plane in another scene I'd be pretty happy.
How it works right now is I create a scene with a perspective camera which renders a simple plane. This happens in the Application object.
I also have a WaveMap object that uses another scene and an orthographic camera and using a fragment shader draws the cos(x) * sin(y) function on another quad that takes up the entire screen. I render this to a texture and then I create a material that uses this texture.
I then pass that Material to be used in the Application object to draw the texture on the first plane I mentioned.
Problem is for some reason I could get this to work in the scene with the orthographic camera inside the WaveMap object but not in the scene with the perspective camera in the Application object after passing the material over. :(
I've tried simply passing a simple material with a solid color and that works but when I try to pass over a material which uses a WebGLRenderTarget as a texture it doesn't show up anymore.
https://github.com/ArminTaheri/rendertotexture-threejs
You need to clone any material/texture that you want to render by two different renderers.
var materialOnOther = originalMaterial.clone();
Prior to r72 you needed to force the image buffers for the textures to be updated like so:
materialOnOther.uniforms.exampleOfATexture.value.needsUpdate = true;

Selecting Canvas Elements [duplicate]

Take a look at this example:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
// First rectangle created
ctx.fillRect(20,20,150,100);
// Second rectangle created
ctx.fillRect(20,150,150,100);
// Third rectangle created
ctx.fillRect(20,300,150,100);
I created three rectangles here. After creating third rectangle I want to rotate first rectangle. How do i get reference of first rectangle now?
A canvas is just a dumb grid of pixels. It doesn't understand what shapes have been drawn on it. Your code (or a library that your code uses) must keep track of the shapes that you've drawn.
Instead, it sounds like you want a library to create a scene graph, like EaselJS, Paper.js, or KineticJS. These libraries will maintain a data structure that tracks what shapes have been drawn on the canvas, and they will then redraw them when you want to manipulate those shapes.
You don't "get the reference" of a rectangle or something with canvas. All you have is a canvas with a context. On which you can draw. Period.
If you want to move the first rectangle, then clear it (using clearRect) and redraw the new one.
Canvas itself is just pixels. It knows how to draw rectangles, but doesn't keep them layered.
To quote Simon Sarris:
HTML5 Canvas is simply a drawing surface for a bit map. You set up a
draw (Say with a color and line thickness) , draw that thing, and then
the Canvas has no knowledge of that thing: It doesn't know where it is
or what it is, it's just pixels. If you want to draw rectangles and
have them move around or be selectable then you have to code all of
that from scratch, including the code to remember that you drew them.
The only exception is the isPointInPath method, but it has limitations.
However, there are some libraries that provide object-oriented interface for Canvas. Like Fabric.js or KineticJS. They remember what you draw as objects (rectangles, circles and so on) and can layer them one over another, move around and add mouse/touch events. Much like DOM.
Drawing functions like fillRect() does not return anything (returns void).
Meaning it simply renders the pixels, it does not create a rectangle object and return it. You'll need to store the rectangle coordinates yourself.

How do I get reference of old generated elements in HTML Canvas?

Take a look at this example:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
// First rectangle created
ctx.fillRect(20,20,150,100);
// Second rectangle created
ctx.fillRect(20,150,150,100);
// Third rectangle created
ctx.fillRect(20,300,150,100);
I created three rectangles here. After creating third rectangle I want to rotate first rectangle. How do i get reference of first rectangle now?
A canvas is just a dumb grid of pixels. It doesn't understand what shapes have been drawn on it. Your code (or a library that your code uses) must keep track of the shapes that you've drawn.
Instead, it sounds like you want a library to create a scene graph, like EaselJS, Paper.js, or KineticJS. These libraries will maintain a data structure that tracks what shapes have been drawn on the canvas, and they will then redraw them when you want to manipulate those shapes.
You don't "get the reference" of a rectangle or something with canvas. All you have is a canvas with a context. On which you can draw. Period.
If you want to move the first rectangle, then clear it (using clearRect) and redraw the new one.
Canvas itself is just pixels. It knows how to draw rectangles, but doesn't keep them layered.
To quote Simon Sarris:
HTML5 Canvas is simply a drawing surface for a bit map. You set up a
draw (Say with a color and line thickness) , draw that thing, and then
the Canvas has no knowledge of that thing: It doesn't know where it is
or what it is, it's just pixels. If you want to draw rectangles and
have them move around or be selectable then you have to code all of
that from scratch, including the code to remember that you drew them.
The only exception is the isPointInPath method, but it has limitations.
However, there are some libraries that provide object-oriented interface for Canvas. Like Fabric.js or KineticJS. They remember what you draw as objects (rectangles, circles and so on) and can layer them one over another, move around and add mouse/touch events. Much like DOM.
Drawing functions like fillRect() does not return anything (returns void).
Meaning it simply renders the pixels, it does not create a rectangle object and return it. You'll need to store the rectangle coordinates yourself.

Categories

Resources