Three.js touching faces artifacts - javascript

I've created two transparent boxes whose faces touch. This works great unless the boxes' faces touch.
// inner object
var mesh2 = new THREE.Mesh(geometry, material);
mesh2.position.x = 0;
mesh2.position.y = 0;
mesh2.position.z = 0;
mesh2.scale.x = 100;
mesh2.scale.y = 50;
mesh2.scale.z = 100;
scene.add( mesh2 );
// outer object
var mesh1 = new THREE.Mesh(geometry, material);
mesh1.position.x = 0;
mesh1.position.y = 0;
mesh1.position.z = 0;
mesh1.scale.x = 100;
mesh1.scale.y = 100;
mesh1.scale.z = 100;
scene.add( mesh1 );
Here's the code:
http://jsfiddle.net/unkya/14/
How do I get rid of these artifacts and still have the faces touch?
Also, is there a way to add the boxes to the scene without having to insert the inner most ones first?
Many thanks!

This is called z-fighting.
There are two ways around this.
The first is simply to offset the values by a small amount. Even 0.01 might do it. The important part here is to ensure your camera's near plane and far plane are within ranges that are reasonable.
The second way is to use the polygonOffset property of THREE.js materials. This will allow you to force an object to render above or below other objects, similar to a z-index ordering. I believe transparency also needs to be enabled, so you should put this on your semi-transparent cube if possible.

Related

Rotating icosahedron with circles located at every vertex in three.js

I have an icosahedron mesh which I am rotating and then adding circle geometries and setting their location to each vertex at every frame in the animation loop.
geometry = new THREE.IcosahedronGeometry(isoRadius, 1);
var material = new THREE.MeshBasicMaterial({
color: wireframeColor,
wireframe: true
});
isoMesh = new THREE.Mesh(geometry, material);
scene.add(isoMesh);
Set each circle geometries location as the icosahedron mesh rotates:
function animate() {
isoMesh.rotation.x += 0.005;
isoMesh.rotation.y += 0.002;
// update vertices
isoMesh.updateMatrix();
isoMesh.geometry.applyMatrix(isoMesh.matrix);
isoMesh.rotation.set(0, 0, 0);
for (var i = 0; i < geometry.vertices.length; i++) {
nodes[i].position.copy(geometry.vertices[i]);
nodes[i].lookAt(camera.position);
}
Where var geometry is the geometry of the icosahedron. If I remove the line "isoMesh.rotation.set(0, 0, 0);", the icosahedron rotates correctly, but the rotation of the nodes compounds and spins way too quickly. If I add that line, the nodes rotate correctly, but the icosahedron does not move at all.
I do not understand three.js well enough yet to understand what is happening. Why would adding and removing this affect the nodes' and icosahedron's rotations separately? I believe it has something to do with the difference between the mesh and the geometry since I am using the geometry to position the nodes, but the rotation of the mesh is what shows visually. Any idea what is happening here?
The solution it multi-layered.
Your Icosahedron:
You were half-way there with rotating your icosahedron and its vertices. Rather than applying the rotation to all the vertices (which would actually cause some pretty extreme rotation), apply the rotation to the mesh only. But that doesn't update the vertices, right? Right. More on that in a moment.
Your Circles:
You have the right idea of placing them at each vertex, but as WestLangley said, you can't use lookAt for objects with rotated/translated parents, so you'll need to add them directly to the scene. Also, if you can't get the new positions of the vertices for the rotated icosahedron, the circles will simply remain in place. So let's get those updated vertices.
Getting Updated Vertex Positions:
Like I said above, rotating the mesh updates its transformation matrix, not the vertices. But we can USE that updated transformation matrix to get the updated matrix positions for the circles. Object3D.localToWorld allows us to transform a local THREE.Vector3 (like your icosahedron's vertices) into world coordinates. (Also note that I did a clone of each vertex, because localToWorld overwrites the given THREE.Vector3).
Takeaways:
I've tried to isolate the parts relative to your question into the JavaScript portion of the snippet below.
Try not to update geometry unless you have to.
Only use lookAt with objects in the world coordinate system
Use localToWorld and worldToLocal to transform vectors between
coordinate systems.
// You already had this part
var geometry = new THREE.IcosahedronGeometry(10, 1);
var material = new THREE.MeshBasicMaterial({
color: "blue",
wireframe: true
});
var isoMesh = new THREE.Mesh(geometry, material);
scene.add(isoMesh);
// Add your circles directly to the scene
var nodes = [];
for(var i = 0, l = geometry.vertices.length; i < l; ++i){
nodes.push(new THREE.Mesh(new THREE.CircleGeometry(1, 32), material));
scene.add(nodes[nodes.length - 1]);
}
// This is called in render. Get the world positions of the vertices and apply them to the circles.
var tempVector = new THREE.Vector3();
function updateVertices(){
if(typeof isoMesh !== "undefined" && typeof nodes !== "undefined" && nodes.length === isoMesh.geometry.vertices.length){
isoMesh.rotation.x += 0.005;
isoMesh.rotation.y += 0.002;
for(var i = 0, l = nodes.length; i < l; ++i){
tempVector.copy(isoMesh.geometry.vertices[i]);
nodes[i].position.copy(isoMesh.localToWorld(tempVector));
nodes[i].lookAt(camera.position);
}
}
}
html *{
padding: 0;
margin: 0;
width: 100%;
overflow: hidden;
}
#host {
width: 100%;
height: 100%;
}
<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<div id="host"></div>
<script>
// INITIALIZE
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
document.getElementById('host').appendChild(renderer.domElement);
var stats= new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 5.0; // need to speed it up a little
var scene = new THREE.Scene();
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
scene.add(light);
function render(){
if(typeof updateVertices !== "undefined"){
updateVertices();
}
renderer.render(scene, camera);
stats.update();
}
function animate(){
requestAnimationFrame(animate);
trackballControl.update();
render();
}
animate();
</script>

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.

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.

Using multiuple textures on a sphere [Three.js]

Is it possible to load multiple textures on a sphere?
I mean to say is there any way in Three.js to split a sphere into n pieces , texture them separately and render those pieces once again as a whole sphere?
I do not want to load the entire texture on the sphere, instead, only those parts are to be rendered which the user will first see on the screen and as the user rotates the sphere the rest part of the texture must be loaded.
Moreover, when I use a single image on a sphere it seems to converge at poles making it worse.
This should help: https://open.bekk.no/procedural-planet-in-webgl-and-three-js
Instead of using a sphere try using a cube and expanding it into a sphere. Cube logic on the cube sphere will save you a good amount of time.
var geometry = new THREE.BoxGeometry( 1, 1, 1, 8, 8, 8 );
for ( var i in geometry.vertices ) {
var vertex = geometry.vertices[ i ];
vertex.normalize().multiplyScalar(radius);
}
var materialArray = [];
var faceMaterial = new THREE.MeshLambertMaterial({
color: sphereColor,
transparent: true,
opacity: 0.4
});
for (var i = 0; i < 6; i++) {
materialArray.push(faceMaterial);
}
var material = new THREE.MeshFaceMaterial(materialArray);
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );

THREE.js: How to improve performance of multiple materials on multiple merged cubes?

I'm using THREE.js to display several thousand cubes at a time. The geometries of these cubes have been merged (I'm displaying several thousand at a time, too slow if they aren't merged) and a requirement I have is to colour the cubes based on a value. The example here colours each cube individually but is grindingly slow once more than 1000 cubes are generated. If I simply use one material for all objects, I can render 100,000+ cubes and the animation is still smooth.
Is there a better way to accomplish what I need?
Code
/* snip */
materials = [];
geometry = new THREE.CubeGeometry(20, 20, 20);
mesh = new THREE.Mesh(geometry);
mergedCubes = new THREE.Geometry();
// Create randomly positioned cubes
for (var i = 0; i < numCubes; i++) {
material = new THREE.MeshBasicMaterial({color:0xffffff * Math.random(),wireframe:false});
materials.push(material);
THREE.GeometryUtils.setMaterialIndex(mesh.geometry, i);
mesh.position.x = Math.random() * 300 - 150;
mesh.position.y = Math.random() * 300 - 150;
mesh.position.z = Math.random() * 300 - 150;
THREE.GeometryUtils.merge(mergedCubes, mesh);
}
// Create merged mesh
group = new THREE.Mesh(mergedCubes, new THREE.MeshFaceMaterial( materials ));
group.matrixAutoUpdate = false;
group.updateMatrix();
/* snip */
jsfiddle
One approach would be to split the difference - what if you calculate a smaller number of materials and assign those to the cubes. If performance is orders of magnitude faster with a single material, then I'd venture to guess that having dozens or even hundreds of materials would be a lot faster than having thousands.
So, calculate your material array outside the cube loop, and then assign them based on a random position in the materials array.

Categories

Resources