Get 3D Position of shadow pixels in ThreeJS - javascript

I have the following project below created using ThreeJS. You will notice the gold object creates a shadow behind it on a sphere where I'm only rendering the backside so we can see the objects inside. I'm using a point light in the very center of the eye model to create the shadow evenly in all directions. This is the reason the shadow is curved.
I need to know how to get the 3D coordinates (x,y,z) of each pixel of this shadow that was created. For reference here is the code that creates the shadow with a lot removed for simplicity.
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap//THREE.PCFSoftShadowMap;
const light = new THREE.PointLight( 0xffffff, 20, 0 );
light.position.set( 0, 0, 0 );
light.castShadow = true;
light.shadow.mapSize.width = 512;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500;
scene.add( light );
const sphereGeometry = new THREE.SphereGeometry( 25, 32, 32 );
const sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } );
sphereMaterial.side=THREE.BackSide;
const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.castShadow = false;
sphere.receiveShadow = true;
scene.add( sphere );
I have researched some into this and I think it may be storing the shadow information in the matrix property of the model but this is not clear for sure in any documentation, so I'm not sure where to look to get this information. Any help is appreciated!
--- Extra not important info ---
Also, in case you are curious, the reason I need the shadow coordinates is because I will use those to raycast back into the eye and create a different kind of shadow on an azimuthal equidistant project on the right (it's complicated...), but just know that if I have the 3D coordinates of the shadow pixels I can do this :). I'm already doing it for the muscles of the eye for example.

You can't extract the shadow into a new geometry because this is all calculated in the GPU shaders upon rendertime, so JavaScript doesn't really have access to the shadowMap positions. However, there is a solution.
Assuming your point light is at (0, 0, 0), and it's at the center of the sphere, you could iterate through the vertices of the gold object and project these positions onto the sphere:
// Sphere radius
const radius = 25;
const vec3 = new THREE.Vector3();
// Get the vertex position array
const vertices = goldObject.geometry.getAttribute("position").array;
// Loop that iterates through all vertex positions
for (let i3 = 0; i3 < vertices.length; i3 += 3) {
// Set this vertex into our vec3
vec3.set(
vertices[i3 + 0], // x
vertices[i3 + 1], // y
vertices[i3 + 2] // z
);
// Set vector magnitude to 1
vec3.normalize();
// Set vector magnitude to radius of sphere
vec3.multiplyScalar(sphereRadius);
// Now you have the spherical projection of this vertex!
console.log(vec3);
}
Since the light source is the exact center of the sphere, you could take the position of each vertex of the gold object, normalize it, then multiply it by the radius of the sphere. Now that you have the vec3 on each iteration, you could add it to your own array to build your own THREE.BufferGeometry that's pushed against the sphere.
Of course, if you've translated or rotated the gold object, then that will affect the vertex positions, so you'd have to undo those translations, rotations, etc. when iterating through all the vertices.

Related

Get latitude longitude on click sphere with threejs

I am here today because I would like your help.
I'm trying to create the earth globe with lat and long, but I can't get the geographic coordinates.
This is my code:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
scene.add(camera);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
renderer.render(scene, camera);
function anime() {
renderer.render(scene, camera);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
requestAnimationFrame(anime);
}
anime();
class Earth {
constructor(radius, scene, camera, renderer) {
this.scene = scene;
this.radius = radius;
this.camera = camera;
this.renderer = renderer;
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.geometry = new THREE.SphereGeometry(this.radius, 32, 32);
this.texture = new THREE.TextureLoader().load('https://i.ibb.co/4KGwCLD/earth-atmos-2048.jpg');
this.material = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: this.texture,
});
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.obj = new THREE.Object3D();
}
initObject() {
// this.scene.add(this.scene);
this.obj.add(this.mesh);
this.obj.name = 'earth';
this.scene.add(this.obj);
}
onClick(event) {
event.preventDefault();
let canvas = this.renderer.domElement;
let vector = new THREE.Vector3(
((event.offsetX) / canvas.width) * 2 - 1, -((event.offsetY) / canvas.height) * 2 + 1,
0.5);
vector.unproject(this.camera);
this.raycaster.set(this.camera.position, vector.sub(this.camera.position).normalize());
let intersects = this.raycaster.intersectObjects(this.scene.children);
if (intersects.length > 0) {
console.log(intersects[0].point.x);
console.log(intersects[0].point.y);
console.log(intersects[0].point.z);
}
}
earthRotate() {
this.obj.rotation.y += 0.01;
requestAnimationFrame(this.earthRotate.bind(this));
}
}
const earth = new Earth(3, scene, camera, renderer);
earth.initObject();
addEventListener("click", (event) => {
earth.onClick(event);
});
/* earth.earthRotate(); */
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.min.js" integrity="sha512-0tlhMhMGPohLm/YwaskxH7jJuUGqU/XPTl+HE0dWrhGbpEBRIZYMQdbHC0CmyNPzZKTBd8JoVZnvMcL7hzlFOg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.js" integrity="sha512-RixpleQfVXfhJUrmXlxWwitZGvMWk13+KhCsaYdeod5xryBN6gqo3RJ9xvaHn8VUeNuUnYfvzKBhLBnJnpEsgA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Is it possible to have geographic coordinates on a sphere with ThreeJS.
If so do you know how to do it?
Thank you in advance for your help !
Intersections are returned in world coordinates, so if you click the same place over and over as your globe is spinning, you will get (about) the same hit position every time.
THAT SAID, these hit points are based on the geometry of the sphere, so a low-poly sphere could actually return a position that does not match a lat/lon position as if the sphere were perfect.
To get a better result, you'll need to take a couple more steps.
1. Create a sphere representation
Use a Sphere to create a mathematically perfect representation of your globe.
2. Use Ray to find the intersection point
The Three Raycaster is a great tool for easily setting up raycasting. But you can use Raycaster.ray (Ray) to perform manual steps, too.
For example, use Ray.intersectSphere against your perfect sphere to get the "real" intersection point.
3. Convert to local coordinates
You were getting the same(-ish) click point because the intersection is in world coordinates. To convert to local coordinates, use the very convenient Object3D.worldToLocal to transform the returned world hit point into a local hit point (the Vector3 is changed using this method, so keep this in mind!).
4. Get your lat/lon
Pick a point on your globe which will represent the intersection point of your equator and prime meridian (0, 0). For a sphere where north is +Y, your origin could be new Vector3( 0, 0, sphere.radius )
Assuming you rotate about the Y axis...
For longitude, remove the Y component of your hit vector (hit.y = 0), set the length of the vector to your sphere radius (hit.setLength( sphere.radius )), then use Vector3.angleTo to find the angle (in radians) between the hit vector and and your "zero" vector, and convert to degrees. Use the X component to determine the East/West component of your value.
For latitude, create a copy of your hit vector. Remove the Y component from the copy and set its length to the sphere's radius, just like for longitude. But instead of comparing to the origin vector, use angleTo to get the angle between the copy and the hit vectors. Convert to degrees. Use the Y value to determine the North/South component of your value.
It's possible to use normalized vectors to perform these comparisons instead, but I think it's easier to visualize if you picture the vectors representing points on the surface of your sphere.

convert bezier into a plane road in three.js

I am trying to draw a curved road in three.js from some beziers I get with previous calculations, the problem is that I can't find the way to convert the sequence of curved lines (one starting at the end of the previous one) to a curved plane.
I have a 3D scene where there are some cars, a road created with a plane and the path of the coming road is painted. I use that Bezier curves I said to represent the path as a Line with
function createAdasisBezier(initx, inity, cp1x, cp1y, cp2x, cp2y, finalx, finaly) {
bezier = new THREE.CubicBezierCurve3(
new THREE.Vector3(initx, inity, 0),
new THREE.Vector3(cp1x, cp1y, 0),
new THREE.Vector3( cp2x, cp2y, 0),
new THREE.Vector3(finalx, finaly, 0)
);
curvePath = new THREE.CurvePath();
curvePath.add(bezier);
var geoPath = curvePath.createPointsGeometry( 5 );
var lineMat = new THREE.LineBasicMaterial({color: 0xff0000});
curveLine = new THREE.Line(geoPath, lineMat);
curveLine.rotation.set(-Math.PI/2,0,0);
curveLine.position.y = 0.1;
scene.add(curveLine);
}
First, I tried extruding the line, but then I realized that it might not be the solution because I wanted to do a road, and although I could move top vertices on X and Y to place them near the bezier in order to be the external part of the curve, the result was not only unfavourable, it also made impossible to preserve a relation between a left and a right curve.
To move vertices (once identified) I did a loop and move them manually:
for (var i = 0; i < geoPath.vertices.length; ++i) {
geoPath.vertices[i].y += 10;
}
Bevel is not enabled in the extude.
Then I tried to draw a plane over each bezier (as a child of them) and rotate it to face the path, but the result was not as I expected, and it if it were, it would spoil the arcs of the curves.
To do it, I created a copy of every bezier, and place it aside the original ones, then I created the plane.
var plane = new THREE.PlaneBufferGeometry(10,25,1,1);
var planemesh = new THREE.Mesh(plane, material);
planemesh.position.set(copy.geometry.vertices[0].x, copy.geometry.vertices[0].y, 0);
Last thing I was trying to do is creating a clone of the line, separate it some meters and "connect" the first vertex from one, to the first of the other, so I get a closed geometry, and I can create a Face, but I don't find how to "connect" vertices from 2 different geometries. I tried adding the vertex from one to the other, but it did not work.
Does anybody have an idea how could I convert the line into a curved road? Thanks in adcance.
You should try looking at the Geometry > Extrude > Shapes example. As you can see, all extruded shapes maintain their width and direction, despite turning left/right or looping completely.
Instead of using bezier curves, they're using a CatmullRomCurve3 to define the extrusion. If you look at the source code, the essential code to make the red extruded shape begins in line 69:
// Define the curve
var closedSpline = new THREE.CatmullRomCurve3( [
new THREE.Vector3( -60, -100, 60 ),
new THREE.Vector3( -60, 20, 60 ),
new THREE.Vector3( -60, 120, 60 ),
new THREE.Vector3( 60, 20, -60 ),
new THREE.Vector3( 60, -100, -60 )
] );
closedSpline.type = 'catmullrom';
closedSpline.closed = true;
// Set up settings for later extrusion
var extrudeSettings = {
steps : 100,
bevelEnabled : false,
extrudePath : closedSpline
};
// Define a triangle
var pts = [], count = 3;
for ( var i = 0; i < count; i ++ ) {
var l = 20;
var a = 2 * i / count * Math.PI;
pts.push( new THREE.Vector2 ( Math.cos( a ) * l, Math.sin( a ) * l ) );
}
var shape = new THREE.Shape( pts );
// Extrude the triangle along the CatmullRom curve
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var material = new THREE.MeshLambertMaterial( { color: 0xb00000, wireframe: false } );
// Create mesh with the resulting geometry
var mesh = new THREE.Mesh( geometry, material );
From here, it should only be a matter of small tweaks to these parameters to get the specific road shape you want.

Calculate direction of rotation after object collision in three.js

I'm detecting collision in my three.js demo using a Raycaster which points out the front of my camera (which can only move in the direction it's facing - though the mouse steers the camera in any direction):
var ray = new THREE.Raycaster( camera.position, cameraDirectionVector );
var collisionResults = ray.intersectObjects( scene.children );
if ( collisionResults.length > 0 )
{
if (collisionResults[0].distance < 0.15) {
var crossAxis = new THREE.Vector3( 0, 0, 1 );
crossAxis.crossVectors(cameraDirectionVector, collisionResults[0].face.normal)
camera.rotateOnAxis( crossAxis, 0.2); // this second parameter is the number I'd like to calculate correctly
}
}
So when there's a collision, I'm using the cross product of the collision face normal and the camera's direction of travel as the axis of rotation, with the aim of effectively spinning the camera away from the collision.
I'd like to know how to correctly calculate the direction of the rotation around that axis; Depending on the direction of crossAxis Vector3 relative to the face normal and direction of travel, the value might either need to be positive or negative.
I should point out this collision detection is aiming only to be very rudimentary - and limited by the fact that the player can only move forward.
How would I figure out if the camera needs to rotate clockwise or anticlockwise around that cross product axis? (Once I know how this number can be calculated I can have the collision expressed more 'realistically'; eg, by having the rotation value decrease over a set amount of frames).
Any help, much appreciated!
Maybe I missunderstood your question, but depending on what direction the camera is approaching the collision face normal, the cross product will be facing in different direction, and thereby changing the rotation direction.
In the example below, the blue arrow would be your collision face normal, the red arrow is your cameras direction, and the green arrow is the cross product betweeen the two (red X blue). The cameras direction is coming in towards the face normal in different directions, thus creating a different cross product each time.
You'll notice that each time the angle between the red and blue arrow passes 180 degrees of eachother, the rotation direction of the white sphere changes. This is because I am using the green arrow (the cross product of red and blue arrow) as the axis of rotation, and when the axis of rotation changes side, the rotation direction changes aswell due to the right hand rule.
Because of this, you should not have to put any sign infront of the second parameter, the direction of rotation is already given in the (cross product) vector that you use as rotation axis.
But since your asking this question, and you are providing some example code, I assume that the solution you have provided is not what you want. Maybe you mean that you always want the camera to rotate away from the collision normal, have you tried simply to put a negative value as the second parameter?
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var origin = new THREE.Vector3(0, 0, 0)
var a = new THREE.Vector3(1, 0, 0);
var arrowHelperA = new THREE.ArrowHelper( a, origin, 1, 0xff0000 );
scene.add( arrowHelperA );
var b = new THREE.Vector3(0, 1, 0);
var arrowHelperB = new THREE.ArrowHelper( b, origin, 1, 0x0000ff );
scene.add( arrowHelperB );
var crossAB = new THREE.Vector3().crossVectors(a, b);
var arrowHelperCrossAB = new THREE.ArrowHelper( crossAB, origin, 1, 0x00ff00 );
scene.add( arrowHelperCrossAB );
var geometry = new THREE.SphereGeometry( 0.02, 32, 32 );
var material = new THREE.MeshBasicMaterial( {color: 0xffffff} );
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
sphere.position.copy(crossAB);
sphere.position.x = 0.3;
camera.position.z = 2;
camera.position.x = 2;
camera.position.y = 2;
camera.lookAt(origin);
var animate = function () {
requestAnimationFrame( animate );
a.applyAxisAngle(new THREE.Vector3(0, 0, 1), 0.01);
arrowHelperA.setDirection(a);
crossAB = new THREE.Vector3().crossVectors(a, b);
arrowHelperCrossAB.setDirection(crossAB);
arrowHelperCrossAB.setLength(crossAB.length())
sphere.position.z = crossAB.z;
sphere.position.applyAxisAngle(crossAB.normalize(), .1);
renderer.render(scene, camera);
};
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>

Three JS Pivot point

What I'm trying to achieve is a rotation of the geometry around pivot point and make that the new definition of the geometry. I do not want te keep editing the rotationZ but I want to have the current rotationZ to be the new rotationZ 0.
This way when I create a new rotation task, it will start from the new given pivot point and the newly given rad.
What I've tried, but then the rotation point moves:
// Add cube to do calculations
var box = new THREE.Box3().setFromObject( o );
var size = box.getSize();
var offsetZ = size.z / 2;
o.geometry.translate(0, -offsetZ, 0)
// Do ratation
o.rotateZ(CalcUtils.degreeToRad(degree));
o.geometry.translate(0, offsetZ, 0)
I also tried to add a Group and rotate that group and then remove the group. But I need to keep the rotation without all the extra objects. The code I created
var box = new THREE.Box3().setFromObject( o );
var size = box.size();
var geometry = new THREE.BoxGeometry( 20, 20, 20 );
var material = new THREE.MeshBasicMaterial( { color: 0xcc0000 } );
var cube = new THREE.Mesh( geometry, material );
cube.position.x = o.position.x;
cube.position.y = 0; // Height / 2
cube.position.z = -size.z / 2;
o.position.x = 0;
o.position.y = 0;
o.position.z = size.z / 2;
cube.add(o);
scene.add(cube);
// Do ratation
cube.rotateY(CalcUtils.degreeToRad(degree));
// Remove cube, and go back to single object
var position = o.getWorldPosition();
scene.add(o)
scene.remove(cube);
console.log(o);
o.position.x = position.x;
o.position.y = position.y;
o.position.z = position.z;
So my question, how do I save the current rotation as the new 0 rotation point. Make the rotation final
EDIT
I added an image of what I want to do. The object is green. I have a 0 point of the world (black). I have a 0 point of the object (red). And I have rotation point (blue).
How can I rotate the object around the blue point?
I wouldn't recommend updating the vertices, because you'll run into trouble with the normals (unless you keep them up-to-date, too). Basically, it's a lot of hassle to perform an action for which the transformation matrices were intended.
You came pretty close by translating, rotating, and un-translating, so you were on the right track. There are some built-in methods which can help make this super easy.
// obj - your object (THREE.Object3D or derived)
// point - the point of rotation (THREE.Vector3)
// axis - the axis of rotation (normalized THREE.Vector3)
// theta - radian value of rotation
// pointIsWorld - boolean indicating the point is in world coordinates (default = false)
function rotateAboutPoint(obj, point, axis, theta, pointIsWorld){
pointIsWorld = (pointIsWorld === undefined)? false : pointIsWorld;
if(pointIsWorld){
obj.parent.localToWorld(obj.position); // compensate for world coordinate
}
obj.position.sub(point); // remove the offset
obj.position.applyAxisAngle(axis, theta); // rotate the POSITION
obj.position.add(point); // re-add the offset
if(pointIsWorld){
obj.parent.worldToLocal(obj.position); // undo world coordinates compensation
}
obj.rotateOnAxis(axis, theta); // rotate the OBJECT
}
After this method completes, the rotation/position IS persisted. The next time you call the method, it will transform the object from its current state to wherever your inputs define next.
Also note the compensation for using world coordinates. This allows you to use a point in either world coordinates or local space by converting the object's position vector into the correct coordinate system. It's probably best to use it this way any time your point and object are in different coordinate systems, though your observations may differ.
As a simple solution for anyone trying to quickly change the pivot point of an object, I would recommend creating a group and adding the mesh to the group, and rotating around that.
Full example
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
Right now, this will just rotate around its center
cube.rotation.z = Math.PI / 4
Create a new group and add the cube
const group = new THREE.Group();
group.add(cube)
scene.add(group)
At this point we are back where we started. Now move the mesh:
cube.position.set(0.5,0.5,0)
Then move the group
group.position.set(-0.5, -0.5, 0)
Now use your group to rotate the object:
group.rotation.z = Math.PI / 4

rotate around pendulum 0.85 cylinder height

im stuck with a work that i need to do, basicly first i needed to create a pendulum that basicly is 2 spheres and 1 cylinder the 2 spheres are in the up and down a bigger 1 and a smaller 1, what i need to do is the pendulum should move not around his own center but about a center that is 0.85 the height of my cylinder, my idea was to create a pivot point, but since i dont know very good how the pivot point work i tryed a lot of things, i tryed first to add the cylinder to the scene and the spheres to the cylinder, then i created the pivot point and add the pivot point to the cylinder, in the animate function i just tried to rotate the pivot point in the x axis but nothing happens :/
Here is my code guys hope someone can give me a hand
var scene, camera, renderer;
var caixaGrande, caixaPequena1, caixaPequena2,cylinder,Cylinder2,esferaGrande;
var pivotPoint1, pivotPoint2;
const RAIOCILINDRO = 2.5;
const ALTURACILINDRO = 100;
const RAIOESFERAGRANDE = 15;
const RAIOESFERAPEQUENA = 5;
var rotacao = Math.PI/180;
window.onload = function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.up=new THREE.Vector3(0,1,0);
camera.position.set(150,50,50);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Add a directional light to show off the object
var light = new THREE.DirectionalLight( 0xffffff, 1.5);
// Position the light out from the scene, pointing at the origin
light.position.set(1.5, 1.5, 1);
scene.add( light );
var textureCylindro = new THREE.TextureLoader().load("CylinderTexture.png");
var textureSphere = new THREE.TextureLoader().load("SphereTexture.png");
geometry = new THREE.CylinderGeometry(RAIOCILINDRO,RAIOCILINDRO,ALTURACILINDRO);
material = new THREE.MeshPhongMaterial( {color: 0xffffff , map:textureCylindro} );
cylinder = new THREE.Mesh(geometry,material);
scene.add(cylinder);
pivotPoint1 = new THREE.Object3D();
pivotPoint1.position.y=ALTURACILINDRO*0.15;
cylinder.add(pivotPoint1);
var geometry = new THREE.SphereGeometry(RAIOESFERAGRANDE);
var material = new THREE.MeshPhongMaterial( {color: 0xffffff,map:textureSphere} );
esferaGrande = new THREE.Mesh( geometry, material );
esferaGrande.position.y = -ALTURACILINDRO/2;
cylinder.add( esferaGrande );
var geometry = new THREE.SphereGeometry(RAIOESFERAPEQUENA);
var material = new THREE.MeshPhongMaterial( {color: 0xffffff,map:textureSphere} );
var esferaPequena = new THREE.Mesh( geometry, material );
esferaPequena.position.y = ALTURACILINDRO/2;
cylinder.add( esferaPequena );
Cylinder2 = cylinder.clone();
Cylinder2.position.z = 3 * RAIOESFERAGRANDE;
scene.add(Cylinder2);
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
animate();
}
function animate() {
pivotPoint1.rotation.x += 10;
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
What you should do is create an Object3D that you add the other 3 elements to, then you add that Object3D to the scene. Then when you want to rotate the whole thing, you rotate your Object3D.
So for example:
var axis = new Object3D();
sphere1.position.y = -.15 * pendulumLength;
sphere2.position.y = .85 * pendulumLength;
cylinder.position.y = .35 * pendulumLength;
//assuming the cylinder is pendulumLength long, this^ puts it right between the two balls
axis.add(sphere1);
axis.add(sphere2);
axis.add(cylinder);
scene.add(axis);
then in your animate() function, simply rotate your Axis:
axis.rotation.z += .01;
EDIT:
So, here is my bad drawing of what happens. If you look here, the circle when positioned at 0,0 when rotated, rotates around the axis. Again when you move it up to 1,1 it rotates around its center point, because rotation is relative to the center point of the circle.
Again the same is true of the cylinder. At 0,0 it rotates around its center. At .5,.5 it also rotates around its center. It doesn't care where it is, it will rotate around its position point.
So if we want to rotate these two as a group relative to some other point, we need to make them the children of another object, because when we move the parent, the children maintain their relationship to the parent, even though their positions are still 1,1 and .5,.5 respectively.
The reason that they get rotated in the manner on the right is because their relationship to the parent object is 1,1 and .5,.5 relative to a parent object with a rotation of 0 radians. When we rotate the parent object by some number of radians, they need to move in order to maintain their original relationship.
Another way to think of it: You have a wooden board with a nail perfectly through the center and you have a shoe. You set the shoe in the upper right hand corner of the board with the toe pointing away from you. If you rotate the wooden board Math.PI/2 radians, the shoe will not stay in the upper right hand corner (relative to you), even though that's where you put it, because the shoe has been added to the board, and you have moved the board. Now, the shoe should be in the bottom right, and will be facing to your right. Object3D.add() is like the process of putting the shoe on the board.

Categories