Rotating icosahedron with circles located at every

2019-09-16 22:42发布

问题:

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?

回答1:

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>