Apply 2D orientation to particles - three.js - javascript

First time using three.js and I'm doing a very simple particle animation in which I'm mapping 4 different textures. So far everything is working as desired except that I can't figure out how to rotate particles so that they're rendered with a random orientation (upside down, sideways, etc.) Any help would be appreciated!
You can see my progress so far here: http://development.shapes.divshot.io/particles.html
And here is the relevant code:
sprite1 = THREE.ImageUtils.loadTexture( "sprite1.png" );
sprite2 = THREE.ImageUtils.loadTexture( "sprite2.png" );
sprite3 = THREE.ImageUtils.loadTexture( "sprite3.png" );
sprite4 = THREE.ImageUtils.loadTexture( "sprite4.png" );
parameters = [ sprite1, sprite2, sprite3, sprite4];
for ( i = 0; i < parameters.length; i ++ ) {
sprite = parameters[i];
materials[i] = new THREE.PointCloudMaterial( { size: 45, map: sprite, depthTest: false, transparent : true} );
particles = new THREE.PointCloud( geometry, materials[i] );
particles.rotation.x = Math.random() * 60;
particles.rotation.y = Math.random() * 60;
particles.rotation.z = Math.random() * 60;
scene.add( particles );
}
Using three.js r71

AFAIK the three.js PointCloud/PointCloudMaterial particle system uses gl.POINTS to draw the points. Which means it has several limitations.
You can't rotate the points.
You can rotate the UV coordinates in your fragment shader if you write a custom shader but that won't help if your image fills the point because rotating a square texture inside a square will clip the corners as it rotates.
You can't make points larger than the max point side of the GPU/Driver you're on.
WebGL only requires the max size = 1.0 which means there are GPUs/Drivers that only support 1 pixel large points.
Checking webglstats.com it looks like the number of GPUs/Drivers that only support 1 pixel large points has gotten smaller. There's still about 5% of machines that only support points 63 pixels and smaller which should only be an issue if you're flying through a point field.
You can only have square points.
You can't have rectangular point if you wanted something long and thin like a spark for example.
One solution is to make your own particle system that uses quads and can rotate their vertices as well as scale them in multiple directions. This example runs entirely on the GPU. Unfortunately it is not three.js based.

Related

THREEJS Convert XYZ coordinate to Object rotation

I'm working with THREE JS. I have a sphere object (made using THREE.SphereGeometry) and inside this sphere I have another smaller sphere. Both spheres have the same center (so the smaller sphere sits exactly in the middle of the bigger sphere). My camera is located inside the bigger sphere and I can see the inside surface of the bigger sphere (Im inside a 3D shop). Here I create the bigger outer sphere:
setupSphereProjection() {
// creation of a big sphere geometry
//THREE.SphereGeometry(SPHERE RADIUS, WIDTH SEGMENTS, HEIGHT SEGMENTS)
this.sphere = new THREE.SphereGeometry(sphereRadius, 40, 40);
this.sphere.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
// creation of the sphere material
let sphereMaterial = new THREE.MeshBasicMaterial();
let loader = new THREE.TextureLoader();
loader.crossOrigin = "anonymous";
sphereMaterial.map = loader.load(testImage);
// geometry + material = mesh (actual object)
this.sphereMesh = new THREE.Mesh(this.sphere, sphereMaterial);
this.scene.add(this.sphereMesh);
}
My setup is such that when I click on the surface of the bigger sphere I get an XYZ coordinate of the click on the bigger sphere surface. What I want is for the smaller sphere to rotate to face the location of the XYZ coordinate. (so when the user clicks on the surface of the bigger sphere the smaller sphere rotates to look at the location of the click on the outer sphere). Here is the THREE.Vector3 I get when I click on the surface of the outer sphere (radius is 100):
THREEE.Vector3 {x: -8.755543860298761, y: -2.284054920077946, z: -99.22118491615181}
I'm struggling to convert the XYZ coordinate into a rotation of the inner sphere. I'm not sure if I should use Quaternions or Euler angles or both. I've tried different approaches and none of them have worked so far. Any help/advice would be warmly welcomed.

Partial Equirectangular Panorama Three.js

I've got full equirectangular images working well with Three.js:
scene = new THREE.Scene();
geometry = new THREE.SphereBufferGeometry( 500, 60, 40 );
geometry.scale(-1, 1, 1);
material = new THREE.MeshBasicMaterial({ map: texture });
mesh = new THREE.Mesh(geometry, material);
mesh.rotation.y = Math.PI;
scene.add( mesh );
But my images actually only contain 180x180 degrees (half the sphere) so I'm trying to get a square texture partially applied on the spherical mesh without stretching the image across the entire sphere. I figure it has something to do with the texture.offset.xyz parameters, but I haven't been successful. While I can continue to pad my images to conform to 2x1 Equirectangular standards, I'd rather cut this step out of my processing workflow.
Below you'll find both the full equirectangular image and the square one I'm trying to get working. Does anyone have any clues on how to accomplish this? Thanks!
SphereBufferGeometry has more optional parameters:
SphereBufferGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
radius — sphere radius. Default is 50.
widthSegments — number of horizontal segments. Minimum value is 3, and the default is 8.
heightSegments — number of vertical segments. Minimum value is 2, and the default is 6.
phiStart — specify horizontal starting angle. Default is 0.
phiLength — specify horizontal sweep angle size. Default is Math.PI * 2.
thetaStart — specify vertical starting angle. Default is 0.
thetaLength — specify vertical sweep angle size. Default is Math.PI.
you can use phiStart, phiLength, thetaStart and thetaLength to define partial sphere
so to do an half sphere you can try something like:
geometry = new THREE.SphereBufferGeometry( 500, 60, 40, 0, Math.PI, 0, Math.PI );
reference http://threejs.org/docs/#Reference/Extras.Geometries/SphereBufferGeometry
The error is not in source code, it's in texture images: they are both wrong.
A 180 degrees fisheye like this:
reprojected into equirectangular will look like this:
Your textures looks like a mix of 360x180 equirectangular and 270° fisheye, wihich looks like this (with wrong labels/numbers, as I used same 180 FOV fisheye to create it):

How to strech(scale) mesh on the world axis

There is an online 3d editor where you can edit individual meshes (move, scale, rotate). Ability to edit meshes implemented using custom transform controls which based on threejs's TransformControls code. This is fragment from mousemove event:
var intersect = intersectObjects(pointer, [xzPlane]); // intersect mouse's pointer with horizontal plane
var point = new THREE.Vector3();
point.copy(intersect.point);
point.sub(offset); // coords from mousedown event (from start stretching)
// some code for 'scale' value calculating base on 'point' variable
// var scale = ...;
//
mesh.scale.x = scale;
This code works well if the mesh does not rotate.
Requires scaling always happened to the world coordinate system. This is programming question
For example, from this:
To this:
P.S. I think that custom mesh matrix must be created, but I have very little experience with matrices
Thanks!
Instead of setting the rotation, like so:
mesh.rotation.set( Math.PI/4, 0, 0 );
apply the identical rotation to the geometry, instead:
var euler = new THREE.Euler( Math.PI / 4, 0, 0 );
mesh.geometry.applyMatrix( new THREE.Matrix4().makeRotationFromEuler( euler ) );
Now, you can set the scale and get the result you want.
mesh.scale.z = 2;
fiddle: http://jsfiddle.net/Tm7Ab/5/
three.js r.67

Applying Textures to Non-Cube Polyhedra with Three.js

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),
]);
}

Three.js Rotate camera around object (which may move)

I have a camera that moves in a few different ways in the scene. The camera should rotate around a target position. In my case, this is a point on a mesh that the user has targeted. Because the camera usually doesn't require moving relative to this point, I was not able to use the pivot idea here: https://github.com/mrdoob/three.js/issues/1830. My current solution uses the following code:
var rotationY = new THREE.Matrix4();
var rotationX = new THREE.Matrix4();
var translation = new THREE.Matrix4();
var translationInverse = new THREE.Matrix4();
var matrix = new THREE.Matrix4();
function rotateCameraAroundObject(dx, dy, target) {
// collect up and right vectors from camera perspective
camComponents.up = rotateVectorForObject(new THREE.Vector3(0,1,0), camera.matrixWorld);
camComponents.right = rotateVectorForObject(new THREE.Vector3(1,0,0), camera.matrixWorld);
matrix.identity();
rotationX.makeRotationAxis(camComponents.right, -dx);
rotationY.makeRotationAxis(camComponents.up, -dy);
translation.makeTranslation(
target.position.x - camera.position.x,
target.position.y - camera.position.y,
target.position.z - camera.position.z);
translationInverse.getInverse(translation);
matrix.multiply(translation).multiply(rotationY).multiply(rotationX).multiply(translationInverse);
camera.applyMatrix(matrix);
camera.lookAt(target.position);
}
The issue is that we do not want to use lookAt, because of the reorientation. We want to be able to remove that line.
If we use the code above without lookAt, we rotate around the point but we do not look at the point. My understanding is that my method should rotate the camera's view as much as the camera itself is rotate, but instead the camera is rotated only a small amount. Could anyone help me understand what's wrong?
EDIT: Cleaned up the original post and code to hopefully clarify my question.
My thinking is that I can translate to the origin (my target position), rotate my desired amount, and then translate back to the beginning position. Because of the rotation, I expect to be in a new position looking at the origin.
In fact, I'm testing it now without the translation matrices being used, so the matrix multiplication line is:
matrix.multiply(rotationY).multiply(rotationX);
and it seems to be behaving the same. Thanks for all the help so far!
ONE MORE THING! A part of the problem is that when the camera behaves badly close to the north or south poles. I am looking for a 'free roaming' sort of feel.
Put the following in your render loop:
camera.position.x = target.position.x + radius * Math.cos( constant * elapsedTime );
camera.position.z = target.position.z + radius * Math.sin( constant * elapsedTime );
camera.lookAt( target.position );
renderer.render( scene, camera );
Alternatively, you can use THREE.OrbitControls or THREE.TrackballControls. See the three.js examples.
The Gimbal lock that you are referring to (reorientation) is because of the use of Euler angles in the default implementation of the camera lookat. If you set
camera.useQuaternion = true;
before your call to lookat, then euler angles will not be used. Would this solve your problem ?

Categories

Resources