Create 50000+ Text Particles in THREE.js - javascript

Im trying to create a character particlesystem with more than 50000 single letters.
Found something similar but written with XG here.
Problem with creating this is the performance of the application.
Here some short pseudo code:
var field = new THREE.Object3D();
for (var i = 0; i < 50000; i++) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.fillText(char);
var texture = new THREE.Texture(canvas)
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({map: texture});
var sprite = new THREE.Sprite(spriteMaterial);
sprite .position.set(x, y, z);
field.add(textSprite);
}
scene.add(field);
So my question is now, is there some example or something where i can see the best way to create this number of textsprites?!
I've also tried this example without a good result.

As vals noted, you are creating material and a texture for every letter. The fact that you are creating a canvas too is beside the point, that's just a one-off overhead.
Every texture you create will have to take up graphics memory. After the fact of texture, every material is computed separately in every render pass, so for 50000 materials you have a lot of computation.
A simple way to speed what you have up would be to use look-up tables:
var materialLUT = {};
function getMaterialForLetter(c){
var m = materialLUT[c];
if(m === void 0){
//material doesn't exist, lets create it
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.fillText(c);
var texture = new THREE.Texture(canvas)
texture.needsUpdate = true;
m = materialLUT[c] = new THREE.SpriteMaterial({map: texture});
}
return m;
}
var field = new THREE.Object3D();
for (var i = 0; i < 50000; i++) {
var spriteMaterial = getMaterialForLetter(char);
var sprite = new THREE.Sprite(spriteMaterial);
sprite.position.set(x, y, z);
field.add(textSprite);
}
scene.add(field);
Another thing i see that should be improved here is use of PointCloud. And lastly - I think it would be best to use a single texture and get relevant characters via UV.

Related

Substract 3D text to a Geometry in Three.js

I want to transform my 3D text to Geometry so I can use it with CSG (I used a Three.js CSG wrapper also) to substract it from another object like in this question.
My 3D text :
loader.load('optimer_regular.typeface.json', function (font){
var text = new THREE.TextGeometry('Tayst', {
font: font,
size: 4,
height: 3
});
var textMat = new THREE.MeshBasicMaterial({color: 0xffffff});
var textGeo = new THREE.Mesh(text, textMat);
textGeo.position.x = -7;
textGeo.position.z = 6;
scene.add(textGeo);
});
My substract thing I want to do for the 3D text (but here it's from circles):
var dots = new THREE.Geometry();
for(var i = 0; i < coordinates.length; i++){
var coords = coordinates[i];
sphereMesh.position.x = coords[0];
sphereMesh.position.y = coords[1];
sphereMesh.position.z = coords[2];
sphereMesh.updateMatrix();
dots.merge(sphereMesh.geometry, sphereMesh.matrix);
}
var sphereCsg = THREE.CSG.toCSG(dots);
var resultGeo = THREE.CSG.fromCSG(resultCsg.subtract(sphereCsg));
cube = new THREE.Mesh(resultGeo, material);
But the thing is, I think, I need to convert my text to a real Geometry so I can substract it?
Well i found out using the library I already got, but instead I didn't had to do the "merge" for example.
Here is the working code (in case someone needs it) :
var loader = new THREE.FontLoader();
loader.load('helvetiker_regular.typeface.json', function (font){
var text = new THREE.TextGeometry('Test', {
font: font,
size: 4,
height: 0.5
});
var textGeo = new THREE.Mesh(text);
textGeo.position.x = -5;
textGeo.position.z = 7.5;
var txtCsg = THREE.CSG.toCSG(textGeo);
var resultGeo = THREE.CSG.fromCSG(resultCsg.subtract(txtCsg));
cube = new THREE.Mesh(resultGeo, material);
scene.add(cube);
});

Three JS - Find all points where a mesh intersects a plane

I have created a three.js scene that includes a plane that intersects a mesh. What I would like to do is get an array of points for all locations where an edge of the mesh crosses the plane. I have had a good look for solutions and can't seem to find anything.
Here is an image of what I currently have:
And here I have highlighted the coordinates I am trying to gather:
If anybody can point me in the right direction, that would be most appreciated.
Thanks,
S
This is not the ultimate solution. This is just a point where you can start from.
UPD: Here is an extension of this answer, how to form contours from given points.
Also, it's referred to this SO question with awesome anwers from WestLangley and Lee Stemkoski about the .localToWorld() method of THREE.Object3D().
Let's imagine that you want to find points of intersection of a usual geometry (for example, THREE.DodecahedronGeometry()).
The idea:
THREE.Plane() has the .intersectLine ( line, optionalTarget ) method
A mesh contains faces (THREE.Face3())
Each face has a, b, c properties, where indices of vertices are stored.
When we know indices of vertices, we can get them from the array of vertices
When we know coordinates of vertices of a face, we can build three THREE.Line3() objects
When we have three lines, we can check if our plane intersects them.
If we have a point of intersection, we can store it in an array.
Repeat steps 3 - 7 for each face of the mesh
Some explanation with code:
We have plane which is THREE.PlaneGeometry() and obj which is THREE.DodecahedronGeometry()
So, let's create a THREE.Plane():
var planePointA = new THREE.Vector3(),
planePointB = new THREE.Vector3(),
planePointC = new THREE.Vector3();
var mathPlane = new THREE.Plane();
plane.localToWorld(planePointA.copy(plane.geometry.vertices[plane.geometry.faces[0].a]));
plane.localToWorld(planePointB.copy(plane.geometry.vertices[plane.geometry.faces[0].b]));
plane.localToWorld(planePointC.copy(plane.geometry.vertices[plane.geometry.faces[0].c]));
mathPlane.setFromCoplanarPoints(planePointA, planePointB, planePointC);
Here, three vertices of any face of plane are co-planar, thus we can create mathPlane from them, using the .setFromCoplanarPoints() method.
Then we'll loop through faces of our obj:
var a = new THREE.Vector3(),
b = new THREE.Vector3(),
c = new THREE.Vector3();
obj.geometry.faces.forEach(function(face) {
obj.localToWorld(a.copy(obj.geometry.vertices[face.a]));
obj.localToWorld(b.copy(obj.geometry.vertices[face.b]));
obj.localToWorld(c.copy(obj.geometry.vertices[face.c]));
lineAB = new THREE.Line3(a, b);
lineBC = new THREE.Line3(b, c);
lineCA = new THREE.Line3(c, a);
setPointOfIntersection(lineAB, mathPlane);
setPointOfIntersection(lineBC, mathPlane);
setPointOfIntersection(lineCA, mathPlane);
});
where
var pointsOfIntersection = new THREE.Geometry();
...
var pointOfIntersection = new THREE.Vector3();
and
function setPointOfIntersection(line, plane) {
pointOfIntersection = plane.intersectLine(line);
if (pointOfIntersection) {
pointsOfIntersection.vertices.push(pointOfIntersection.clone());
};
}
In the end we'll make our points visible:
var pointsMaterial = new THREE.PointsMaterial({
size: .5,
color: "yellow"
});
var points = new THREE.Points(pointsOfIntersection, pointsMaterial);
scene.add(points);
jsfiddle example. Press the button there to get the points of intersection between the plane and the dodecahedron.
Update THREE.js r.146
Sharing complete example using BufferGeometry since Geometry is deprecated since r.125, while following the wonderful example of #prisoner849 and discourse thread Plane intersects mesh with three.js r125
Example includes clipping the geometry based on the intersection points which are used to generate the LineSegments.
Can also instead create a Plane from the PlanarGeometry Quanternion and Normal
let localPlane = new THREE.Plane();
let normal = new THREE.Vector3();
let point = new THREE.Vector3();
normal.set(0, -1, 0).applyQuaternion(planarGeometry.quaternion);
point.copy(planarGeometry.position);
localPlane.setFromNormalAndCoplanarPoint(normal, point).normalize();**
Function updates Lines with current intersection based on the current position of the PlanarGeometry
let lines = new THREE.LineSegments(
new THREE.BufferGeometry(),
new THREE.LineBasicMaterial({
color: 0x000000,
linewidth: 5
})
);
function drawIntersectionLine() {
let a = new THREE.Vector3();
let b = new THREE.Vector3();
let c = new THREE.Vector3();
const isIndexed = obj.geometry.index != null;
const pos = obj.geometry.attributes.position;
const idx = obj.geometry.index;
const faceCount = (isIndexed ? idx.count : pos.count) / 3;
const clippingPlane = createPlaneFromPlanarGeometry(plane);
obj.material.clippingPlanes = [clippingPlane];
let positions = [];
for (let i = 0; i < faceCount; i++) {
let baseIdx = i * 3;
let idxA = baseIdx + 0;
a.fromBufferAttribute(pos, isIndexed ? idx.getX(idxA) : idxA);
let idxB = baseIdx + 1;
b.fromBufferAttribute(pos, isIndexed ? idx.getX(idxB) : idxB);
let idxC = baseIdx + 2;
c.fromBufferAttribute(pos, isIndexed ? idx.getX(idxC) : idxC);
obj.localToWorld(a);
obj.localToWorld(b);
obj.localToWorld(c);
lineAB = new THREE.Line3(a, b);
lineBC = new THREE.Line3(b, c);
lineCA = new THREE.Line3(c, a);
setPointOfIntersection(lineAB, clippingPlane, positions);
setPointOfIntersection(lineBC, clippingPlane, positions);
setPointOfIntersection(lineCA, clippingPlane, positions);
}
lines.geometry.setAttribute(
"position",
new THREE.BufferAttribute(new Float32Array(positions), 3)
);
}
function setPointOfIntersection(line, planarSrf, pos) {
const intersect = planarSrf.intersectLine(line, new THREE.Vector3());
if (intersect !== null) {
let vec = intersect.clone();
pos.push(vec.x);
pos.push(vec.y);
pos.push(vec.z);
}
}
Example CodePen

how to render alphabets in 2D using threejs

I'm making a 2d game, where blocks are falling down ( tetris style). I need to render alphabets on these blocks. This is how I am creating blocks:
var geometry = new THREE.BoxGeometry( this.BLOCK_WIDTH, this.BLOCK_WIDTH, 4 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
this.blocks = [];
for (var i = 0; i < rows * columns; i++) {
cube = new THREE.Mesh( geometry, material );
cube.visible = false;
cube.letter = letterGenerator.getNextLetter();
this.blocks[i] = cube;
scene.add( this.blocks[i] );
};
As you can see, all blocks will look exactly alike except for the fact, that they will have a different alphabet associated with them. In my update(), I move the block, left/right or down. When I do so, block position will be updated and obviously the alphabet should be rendered accordingly.
How should I go about rendering alphabets on these blocks ?
EDIT: I am using WebGLRenderer.
You can get the screen position of each block (your "cube" variable above) that you want to paint text on and use HTML to paint text at that screen location over each block. Using HTML to make a text sprite like this is discussed here:
https://github.com/mrdoob/three.js/issues/1321
You can get the screen position for your "cube" above like so:
var container = document.getElementById("idcanvas");
var containerWidth = container.clientWidth;
var containerHeight = container.clientHeight;
var widthHalf = containerWidth / 2, heightHalf = containerHeight / 2;
var locvector = new THREE.Vector3();
locvector.setFromMatrixPosition(cube.matrixWorld);
locvector.project(your_camera); //returns center of mesh
var xpos = locvector.x = (locvector.x * widthHalf) + widthHalf; //convert to screen coordinates
var ypos = locvector.y = -(locvector.y * heightHalf) + heightHalf;
You'll have to update the HTML for cube movement.
Another approach is to create specific textures with the text you want and apply each texture as a material to the appropriate cube.

Delete multiple items with the same name?

I am adding "n" number of circles on the scene.
var radius = 1;
var segments = 32;
var circleGeometry = new THREE.CircleGeometry( radius, segments);
function generateCircles(){
//scene.remove(circle);
var count=0;
while (1000> count) {
circle = new THREE.Mesh (circleGeometry, material);
scene.add (circle);
count ++;
}
}
Is it effective to do it this way?.
In my code I call this function, and every time you call it, it all goes back slower, I guess it's because there are more objects in the scene. What can I do?
Each time the function is called, I need to completely erase from the memory stage the circles that were generated.
http://jsfiddle.net/v8oxsxtc/
If you don't need to move or change the circles after adding them to the scene, it would be more efficient to merge them all into one Mesh and add them to the scene as one Mesh. Then, you can simply remove that one Mesh from the scene before generating more circles:
var radius = 1;
var segments = 32;
var circleGeometry = new THREE.CircleGeometry(radius, segments);
var circlesMesh;
function generateCircles() {
if (circlesMesh) { scene.remove(circlesMesh); }
circlesMesh = new THREE.Mesh(circleGeometry, material);
var count = 0;
while (count++ < 999) {
THREE.GeometryUtils.merge(circlesMesh, new THREE.Mesh(circleGeometry, material));
}
scene.add(circlesMesh);
}

What is the most efficient way to display 4 million 2D squares in a browser?

My display has a resolution of 7680x4320 pixels. I want to display up to 4 million different colored squares. And I want to change the number of squares with a slider. If have currently two versions. One with canvas-fillRect which looks somethink like this:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
for (var i = 0; i < num_squares; i ++) {
ctx.fillStyle = someColor;
ctx.fillRect(pos_x, pos_y, pos_x + square_width, pos_y + square_height);
// set pos_x and pos_y for next square
}
And one with webGL and three.js. Same loop, but I create a box geometry and a mesh for every square:
var geometry = new THREE.BoxGeometry( width_height, width_height, 0);
for (var i = 0; i < num_squares; i ++) {
var material = new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } );
material.emissive = new THREE.Color( Math.random(), Math.random(), Math.random() );
var object = new THREE.Mesh( geometry, material );
}
They both work quite fine for a few thousand squares. The first version can do up to one million squares, but everything over a million is just awful slow. I want to update the color and the number of squares dynamically.
Does anyone has tips on how to be more efficient with three.js/ WebGL/ Canvas?
EDIT1: Second version: This is what I do at the beginning and when the slider has changed:
// Remove all objects from scene
var obj, i;
for ( i = scene.children.length - 1; i >= 0 ; i -- ) {
obj = scene.children[ i ];
if ( obj !== camera) {
scene.remove(obj);
}
}
// Fill scene with new objects
num_squares = gui_dat.squareNum;
var window_pixel = window.innerWidth * window.innerHeight;
var pixel_per_square = window_pixel / num_squares;
var width_height = Math.floor(Math.sqrt(pixel_per_square));
var geometry = new THREE.BoxGeometry( width_height, width_height, 0);
var pos_x = width_height/2;
var pos_y = width_height/2;
for (var i = 0; i < num_squares; i ++) {
//var object = new THREE.Mesh( geometry, );
var material = new THREE.Material()( { color: Math.random() * 0xffffff } );
material.emissive = new THREE.Color( Math.random(), Math.random(), Math.random() );
var object = new THREE.Mesh( geometry, material );
object.position.x = pos_x;
object.position.y = pos_y;
pos_x += width_height;
if (pos_x > window.innerWidth) {
pos_x = width_height/2;
pos_y += width_height;
}
scene.add( object );
}
The fastest way to draw squares is to use the gl.POINTS primitive and then setting gl_PointSize to the pixel size.
In three.js, gl.POINTS is wrapped inside the THREE.PointCloud object.
You'll have to create a geometry object with one position for each point and pass that to the PointCloud constructor.
Here is an example of THREE.PointCloud in action:
http://codepen.io/seanseansean/pen/EaBZEY
geometry = new THREE.Geometry();
for (i = 0; i < particleCount; i++) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2000 - 1000;
vertex.y = Math.random() * 2000 - 1000;
vertex.z = Math.random() * 2000 - 1000;
geometry.vertices.push(vertex);
}
...
materials[i] = new THREE.PointCloudMaterial({size:size});
particles = new THREE.PointCloud(geometry, materials[i]);
I didn't dig through all the code but I've set the particle count to 2m and from my understanding, 5 point clouds are generated so 2m*5 = 10m particles and I'm getting around 30fps.
The highest number of individual points I've seen so far was with potree.
http://potree.org/, https://github.com/potree
Try some demo, I was able to observe 5 millions of points in 3D at 20-30fps. I believe this is also current technological limit.
I didn't test potree on my own, so I cant say much about this tech. But there is data convertor and viewer (threejs based) so should only figure out how to convert the data.
Briefly about your question
The best way handle large data is group them as quad-tree (2d) or oct-tree (3d). This will allow you to not bother program with part that is too far from camera or not visible at all.
On the other hand, program doesnt like when you do too many webgl calls. Try to understand it like this, you want to do create ~60 images each second. But each time you set some parameter for GPU, program must do some sync. Spliting data means you will need to do more setup so tree must not be too detialed.
Last thing, someone said:
You'll probably want to pass an array of values as one of the shader uniforms
I dont suggest it, bad idea. Texture lookup is quite fast, but attributes are always faster. If we are talking about 4M points, you cant afford reading data from uniforms.
Sorry I cant help you with the code, I could do it without threejs, Im not threejs expert :)
I would recommend trying pixi framework( as mentioned in above comments ).
It has webgl renderer and some benchmarks are very promising.
http://www.goodboydigital.com/pixijs/bunnymark_v3/
It can handle allot of animated sprites.
If your app only displays the squares, and doesnt animate, and they are very simple sprites( only one color ) then it would give better performance than the demo link above.

Categories

Resources