How to map an image on a partial sphere in three.js - javascript

I created a full sphere out of sphere tiles, to handle all of them individually when they are inside the FOV.
To do that I divided an equirectangular panorama into a few quadratic images to map them onto these tiles. But somehow the texture is shifted on the highest and lowest row of segments in every tile.
Example with some imported sqare images:
The original test image:
How can I geht the textures map correctly to the tiles? The code for creating the tiles is below:
/*
segment Data:
x/y are generated by a loop
maxX/maxY are the maximum values of these loops
*/
sphere = new THREE.SphereGeometry(radius, 4, 4, segmentData.x * 2 * Math.PI / segmentData.maxX, 2 * Math.PI / segmentData.maxX, segmentData.y * Math.PI / segmentData.maxY, Math.PI / segmentData.maxY);
texture = new THREE.TextureLoader().load(tileImagePath);
material = new THREE.MeshBasicMaterial({map: texture});
mesh = new THREE.Mesh(sphere, material);
scene.add(mesh);
I'm quite new to three.js so maybe this is a very simple problem.
Hopefully somebody can help?

Thanks to ScieCode, it turned out, that the problem was caused by a bug in versions r103 and r104.
It will be fixed in the next release: https://github.com/mrdoob/three.js/issues/16454

Related

Three.jsMesh Matrixworld does not update

Good morning, I am trying to orient subpanels on each plane, but seems that using the method that I used to create the mesh(planes) the matrix and matrixWorld does not update. I need to "update" each plane Matrix to be able to translate and rotate the subplanes accordantly. Below I am adding a picture with the steps of geometry generation. I am using BufferGeometry to create the vertices of the panels, and then triangulating them to create the meshes. Below I am including an image with the step-by-step.
Any help would be helpful!
Step by step - geometry generation
In the end, I figured it out. I built the transformation matrix4 directly from corner vectors. Maybe it can be useful for someone (code below):
let vX = new THREE.Vector3(ndes[3][0] - ndes[0][0], ndes[3][1] - ndes[0][1], ndes[3][2] - ndes[0][2]).normalize(),
vY = new THREE.Vector3(ndes[1][0] - ndes[0][0], ndes[1][1] - ndes[0][1], ndes[1][2] - ndes[0][2]).normalize(),
vZ = new THREE.Vector3().crossVectors(vX, vY).normalize();
let matrix = new THREE.Matrix4();
matrix.set( vX.x, vY.x, vZ.x, ndes[0][0],
vX.y, vY.y, vZ.y, ndes[0][1],
vX.z, vY.z, vZ.z, ndes[0][2],
0, 0, 0, 1);
panels.applyMatrix4( matrix );
paneling surfaces

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):

Apply 2D orientation to particles - three.js

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.

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