ThreeJS - adding objects in different order affects alpha / display - javascript

My program creates dynamic number of point cloud objects with custom attributes that includes the alpha value of each particle. This works fine, however, when the objects are nested within each other (say spheres) the smaller (inner) ones are getting obscured by the bigger ones, even though their particles' alpha is set properly. When I reverse the order of adding the point-cloud objects to the scene, starting with the bigger ones, going down to the smaller ones, I can see the smaller ones thru the bigger ones.
My question is whether there is a way to tell the renderer to update or recalculate the alpha values or re-render the smaller inner objects so that they show up?

I ran into the same problem as you do. I fixed it to calculate and set the renderdepth for each mesh. For this you need the camera position and the center of your mesh.
You probably already created meshes for each object. If you save all these meshes into an array, it's easier to calculate and set the renderdepth on these objects.
Here's an example how I did it.
updateRenderDepthOnRooms(cameraPosition: THREE.Vector3): void {
var rooms: Room[] = this.getAllRooms();
rooms.forEach((room) => {
var roomCenter = getCenter(room.mesh.geometry);
var renderDepth = 0 - roomCenter.distanceToSquared(cameraPosition);
room.mesh.renderDepth = renderDepth;
});
}
function getCenter(geometry: THREE.Geometry): THREE.Vector3 {
geometry.computeBoundingBox();
var bb = geometry.boundingBox;
var offset = new THREE.Vector3();
offset.addVectors(bb.min, bb.max);
offset.multiplyScalar(0.5);
return offset;
}
So, to get the center of your object, you can ask the geometry from your mesh and use the getCenter(..) function from my example. Then you calculate the renderdepth with the ThreeJs function distanceToSquared(..) and then set this renderdepth to your mesh.
That's it. Hope this will help you.

Related

AFrame: position offset units when adding new Object3D to the current one

I'm trying to add an Object3D to my gltf model and place it above the model. I'm doing it the following way:
this.el.addEventListener('model-loaded', () => {
this.bar = new MyCustomObject3D();
const size = new THREE.Vector3();
let box = new THREE.Box3().setFromObject(this.el.object3D);
box.getSize(size)
let height = size.y + 1;
this.bar.position.set(0, height, 0);
this.el.setObject3D("bar", this.bar);
// same result:
// this.el.object3D.add(this.bar);
})
The height is 2 and if I placed an element with this position into root (i.e. scene) it would be placed correctly right above the model. But when I add it to the Object3D it's being placed somewhere below the model on height ~0.5. Only by multiplying the height by 25 I could achieve the right position.
So how to calculate the exact offset needed to place the new Object3D above the model without multiplying it to a random number?
UPDATE:
Adding reproducible example. Note the width and height I had to pass to GLTF model.
One way of placing objects above a model, would be grabbing its bounding box, and placing an object above it.
In general, it it simple - just like you did it:
let box = new THREE.Box3().setFromObject(this.el.object3D);
box.getSize(size)
let height = size.y + 1;
this.bar.position.set(0, height, 0);
But in this case - the bounding box is off. Way off. The minimum is way too low, and the maximum is somewhere in the middle. Why is that? (tldr: check it out here)
The cuprit is: skinning. The model is transformed by its bones - which is a form of vertex displacement that happens on the GPU (vertex shader), and has nothing to do with the geometry (source).
Here is some visual aid - the model with its armature:
And without the armature applied:
Now we see why the box is off - its corresponding to the bottom picture!
So we need to re-create what the bones are doing to the geometry:
1. The hard route
You need to take a THREE.Box3.
Iterate through each geometry point of the model
Apply the bone transform to each point (it is done here - but not available in a-frame 1.0.4)
Expand the THREE.Box3
2. The easy route
While looking into this, I've made a utility function THREE.Box3Utils.fromSkinnedMesh(mesh, box3); - box3 will be the bounding box of the model at the time when the function is called.
The function is a part of this repo.
Its used on this example.

Helper Function Needed to Turn WebGL / Three.js Lengths to Pixesl

I'm am searching for how WebGL / Three.js in general sets their heights and widths.
As in what numbers systems do they use to set x,y,z.
For the Example below, the arrow it pointing straight up with the Y being set to 1 but in pixels it looks like 15- - 200 pixels.
Is there a helper function that i can write that I could pass in 100 for the pixels and it would return me the correct number to float number to use with THREE.js.
Excuse me if I am not talking in correct terms when it comes to number system but this is he only way i know how to reference it at this point.
The only thing i am missing below is creating the scene. but the rest is there, the image shows what it looks lik.
Once again is there a helper function that i can pass pixels to and in return get back the correct number in float for use with THREE.js?
Here is my arrow:
//scene.remove(cube);
scene.remove(group);
// create a new one
var sphere = createMesh(new THREE.SphereGeometry(5, 10, 10));
var cube = createMesh(new THREE.BoxGeometry(6, 6, 6));
sphere.position.set(controls.spherePosX, controls.spherePosY, controls.spherePosZ);
cube.position.set(controls.cubePosX, controls.cubePosY, controls.cubePosZ);
// add it to the scene.
// also create a group, only used for rotating
var group = new THREE.Group();
group.add(sphere);
group.add(cube);
scene.add(group);
controls.positionBoundingBox();
var arrow = new THREE.ArrowHelper(new THREE.Vector3(0, 1, 0), 0, 10, 0x0000ff);
scene.add(arrow);
I receive these JS objects with the Pixels then write to screen, but how do i convert the pixels down to usable units in 3D?
The lengths in 3D do not translate to lengths in 2D uniformly. Especially when perspective projection is employed.
Let's consider your example: Two arrows of the same 3D length and the same orientation would render to different 2D lengths depending on their distance from the camera. The arrow that is closer to camera will be rendered longer than the arrow farther from camera.
In order to maintain a certain pixel length for a certain arrow, you'd have to adjust the 3D length of the arrow every time some parameters of the camera change (e.g. position, orientation, FOV). And also every time the position or orientation of the arrow changes. This is possible (see comment by #WacławJasper ) but rather complicated.
If you could explain the bigger picture of what you wish to achieve there might be a simpler solution to your problem.

Scaling Raster with Paper.js using Tween.js

I'm think I'm having a similar issue as this in that I can not work out (or know if it exists) whereby I can get access to the scaling applied to a given object (in my instance, a raster).
I need to know this so I can animate the scaling via Tween.js.
Anyone have any ideas or know if indeed it is possible to find out the current scaling applied to a raster (or any) object?
I thought it was an issue with Rasters so I tried tweening the scale property of a Path and then a Group and I couldn't get access to the values in order to animate it.
Because I am using Tween.js I can not simply use the object.scale(value) function.
UPDATE
I even tried applying an arbitrary (animated) number to the scale function and it failed to work... i.e.:
object.scale( 0 );
object.arbitraryNumber = 0;
createjs.Tween.get( object )
.to( { arbitraryNumber:1 } , 1000, createjs.Ease.getPowInOut(2) )
.addEventListener( "change", function( event ) {
event.target.target.scale( event.target.target.arbitraryNumber);
} );
Although this did not work, when the same approach was applied to the x position of the object, it animated fine.
Is there anything that needs to be flagged in order to update scaling of an object?
When calling Item.scale() method on each frame with values from 0 to 1, you are actually scaling down item exponentially because each call scales the item relatively to the previous value.
What you want to do is animate the Item.scaling property instead.
You also have to know that by default, PaperJS use global coordinates system and apply every transformations directly to points.
You can change this behavior by setting Item.applyMatrix property to false.
Doing this, scale change will affect item matrix instead of affecting points coordinates and you will be able to animate it as you expect.
Here is simple Sketch of a scale animation:
var circle = new Path.Circle(view.center, 50);
circle.fillColor = 'orange';
circle.applyMatrix = false;
function onFrame(event)
{
circle.scaling = Math.sin(1 + event.count * 0.05);
}
You should be able to transpose this example to your Tween.js context easily.

three js same position (reference) for 2 Object3D()

I am trying to add 2 (or more) Meshes to the same scene, but i want all the Object3D to share the same position (but with a different rotation for each object).
I know the ".copy()" method, but in my case, there are so many objects and using a loop to change the position of all the objects (60 times per seconds) is resulting a poor performance.
So, I tried to use the same position reference for the objects:
var o=new THREE.Object3D(); // the first object
var p=new THREE.Object3D(); // the second object
o.position=p.position; // the position of the first object becomes the reference to the position of the second object
o.position==p.position; // false, but why?
But it isn't working and I don't get why!
My question is:
Is it possible to change the position reference of a THREE.Object3D()?
And if it isn't, how can I improve the performance of my scene?
Thanks in advance!
Edit: seems to works for someone (this answer is exactly what i can't do): How to set the Object3D position and rotation same as the added mesh ? [three.js]

Three.js: Updating Geometries vs Replacing

I have a scene with lots of objects using ExtrudeGeometry. Each of these need to update each frame, where the shape that is being extruded is changing, along with the amount of extrusion. The shapes are being generated using d3's voronoi algorithm.
See example.
Right now I am achieving this by removing every object from the scene and redrawing them each frame. This is very costly and causing performance issues. Is there a way to edit each mesh/geometry instead of removing from the scene? Would this help with performance? Or is there a more efficient way of redrawing the scene?
I'd need to edit both the shape of the extrusion and the amount of extrusion.
Thanks for taking a look!
If you're not changing the number of faces, you can use morph targets http://threejs.org/examples/webgl_morphtargets.html
You should
Create your geometry
Clone the geometry and make your modifications to it, such as the maximum length of your geometry pillar
Set both geometries as morph targets to your base geometry, for example
baseGeo.morphTargets.push(
{ name: "targetName", vertices: [ modifiedVertexArray ] }
);
After that, you can animate the mesh this using mesh.updateMorphTargets()
See http://threejs.org/examples/webgl_morphtargets.html
So I managed to come up with a way of not having to redraw the scene every time and it massively improved performance.
http://jsfiddle.net/x00xsdrt/4/
This is how I did it:
Created a "template geometry" with ExtrudeGeometry using a dummy
10 sided polygon.
As before, created a bunch of "points", this time assigning each
point one of these template geometries.
On each frame, iterated through each geometry, updating each vertex
to that of the new one (using the voronoi alg as before).
If there are extra vertices left over, "bunch" them up into a single point. (see http://github.com/mrdoob/three.js/wiki/Updates.)
Looking at it now, it's quite a simple process. Before, the thought of manipulating each vertex seemed otherworldly to me, but it's not actually too tricky with simple shapes!
Here's how I did the iteration, polycColumn is just a 2 item array with the same polygon in each item:
// Set the vertex index
var v = 0;
// Iterate over both top and bottom of poly
for (var p=0;p<polyColumn.length;p++) {
// Iterate over half the vertices
for (var j=0;j<verts.length/2;j++) {
// create correct z-index depending on top/bottom
if (p == 1) {
var z = point.extrudeAmount;
} else {
var z = 0;
}
// If there are still legitimate verts
if (j < poly.length) {
verts[v].x = poly[j][0];
verts[v].y = poly[j][1];
verts[v].z = z;
// If we've got extra verts, bunch them up in the same place
} else {
verts[v].x = verts[v - 1].x;
verts[v].y = verts[v - 1].y;
verts[v].z = z;
}
v++;
}
}
point.mesh.geometry.verticesNeedUpdate = true;

Categories

Resources