【问题标题】:Set 3d cube rotation origin设置 3d 立方体旋转原点
【发布时间】:2020-12-28 18:23:12
【问题描述】:

我有一个简单的 3d 立方体(BoxGeometry 为 100、100、100),我正在尝试旋转它。如果我们将所有 100x100x100 称为图块 - 当我旋转它时,我可以看到它与下面的图块重叠。

(通过改变颜色,现在我完全理解了这种行为)。

tl.to(this.cube4.rotation, 0.5, {z: -45* Math.PI/180});

[

如果我想根据右下角的锚点旋转它怎么办?因此,它不会溢出下面的瓷砖,而是将该部分溢出到上面的瓷砖。

所以它看起来像绿色示例而不是红色示例:

这里红色的例子是由

实现的
tl.to(this.cube4.rotation, 0.5, {z: -45* Math.PI/180});
tl.to(this.cube4.position, 0.5, {x: 50 }, 0.5);

我对 three.js 很陌生,所以如果有任何术语错误,请警告我

【问题讨论】:

    标签: javascript three.js rotation transformation gsap


    【解决方案1】:

    将(“红色”)立方体添加到THREE.Group,这样旋转轴(边缘)位于组的原点。这意味着立方体必须移动一半边长。
    如果您旋转组对象,那么立方体(在组内)将围绕边缘而不是围绕其中心旋转。

    例如

    var bbox = new THREE.Box3().setFromObject(cube);
    cube.position.set(bbox.min.x, bbox.max.y, 0);
    
    var pivot = new THREE.Group();
    pivot.add(cube);
    
    scene.add(pivot);
    

    另请参阅How to center a group of objects? 的答案,它使用此解决方案来旋转一组对象。


    (function onLoad() {
      var camera, scene, renderer, orbitControls, pivot;
      var rot = 0.02;
      
      init();
      animate();
    
      function init() {
        container = document.getElementById('container');
        
        renderer = new THREE.WebGLRenderer({
          antialias: true,
          alpha: true
        });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;
        container.appendChild(renderer.domElement);
    
        camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
        camera.position.set(4, 1, 2);
        //camera.lookAt( -1, 0, 0 );
    
        loader = new THREE.TextureLoader();
        loader.setCrossOrigin("");
    
        scene = new THREE.Scene();
        scene.background = new THREE.Color(0xffffff);
        scene.add(camera);
        
        window.onresize = function() {
          renderer.setSize(window.innerWidth, window.innerHeight);
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();
        }
        
        orbitControls = new THREE.OrbitControls(camera, container);
    
        var ambientLight = new THREE.AmbientLight(0x404040);
        scene.add(ambientLight);
    
        var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
        directionalLight.position.set(1,2,-1.5);
        scene.add( directionalLight );
        
        addGridHelper();
        createModel();
    
      }
    
      function createModel() {
    
        var material = new THREE.MeshPhongMaterial({color:'#80f080'});
        var geometry = new THREE.BoxGeometry( 1, 1, 1 );
        
        var cube1 = new THREE.Mesh(geometry, material);
        cube1.position.set(0,-0.5,-0.5);
    
        var cube2 = new THREE.Mesh(geometry, material);
        cube2.position.set(0,0.5,-0.5);
    
        var cube3 = new THREE.Mesh(geometry, material);
        cube3.position.set(0,-0.5,0.5);
    
        var material2 = new THREE.MeshPhongMaterial({color:'#f08080'});
        var cube4 = new THREE.Mesh(geometry, material2);
        
        var bbox = new THREE.Box3().setFromObject(cube4);
        cube4.position.set(bbox.min.x, bbox.max.y, 0);
    
        pivot = new THREE.Group();
        pivot.add(cube4);
        pivot.position.set(-bbox.min.x, 0.5-bbox.max.y, 0.5);
    
        scene.add(cube1);
        scene.add(cube2);
        scene.add(cube3);
        scene.add(pivot);
      }
    
      function addGridHelper() {
        
        var helper = new THREE.GridHelper(100, 100);
        helper.material.opacity = 0.25;
        helper.material.transparent = true;
        scene.add(helper);
    
        var axis = new THREE.AxesHelper(1000);
        scene.add(axis);
      }
    
      function animate() {
        requestAnimationFrame(animate);
        orbitControls.update();
        pivot.rotation.z += rot;
        if (pivot.rotation.z > 0.0 || pivot.rotation.z < -Math.PI/2) rot *= -1;
        render();
      }
    
      function render() {
        renderer.render(scene, camera);
      }
    })();
    <!--script src="https://threejs.org/build/three.js"></!--script-->
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r124/build/three.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r124/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.js"></script>
    <div id="container"></div>

    【讨论】:

    • 如果 Group 和 box 大小一样,Group 的 origin 会不会又是 center 了?所以就算我轮换组,不也是一样的效果吗?
    • @senty 是的,该组围绕其中心旋转。但是框中的立方体是相对于组的中心进行平移的。
    • 那么组需要比盒子大吗?我不太明白为什么它的行为会有所不同。 (对不起,如果这是一个愚蠢的问题,我是three.js的新手)
    • @senty 不,立方体的原点是立方体的中心。群的原点是群的中心。但是如果你把立方体放在一个翻译的组中,那么立方体的原点就不是组的原点。
    • 现在你的 sn-p 更清楚了。只有一个问题,cubeMatrix 是从哪里来的?是我在创建 Box 时使用的几何图形,即 var geometry = new THREE.BoxGeometry( 100, 100, 100 );
    【解决方案2】:

    从第一张图片来看,您的红色瓷砖的支点似乎位于其中心。

    对于您想要的旋转,您最好将轴心更改为立方体的右下方。如果不修改立方体的几何形状,这是不可能的。

    但一个简单的技巧是在该枢轴点创建一个空节点,将您的立方体作为该空的父级,然后将您的旋转应用于空。 (别忘了删除你的翻译,你不再需要它了)

    这是一些伪代码,假设您的红色框以 (0,0,0) 为中心,宽度和高度为 100:

    // create an empty node at desired rotation pivot
    var empty = new Object3D or group
    empty.position = (50, -50, 0)
    
    // parent your cube to the empty
    var cube = your box
    empty.add(cube)
    
    // you may need to change the local position of your cube to bring it back to its global position of (0,0,0)
    cube.position = (-50, 50, 0)
    
    rotate empty by 45°
    

    【讨论】:

    • 能否请您扩展简单的技巧部分或添加一些伪代码块来解释它?
    【解决方案3】:

    我认为您可以像这样获得旋转对象的边界:

    bounds = new THREE.Box3().setFromObject( theRedObject )
    

    然后根据它的bounds.min.y重新定位object.y

    let scene, camera, controls, ambient, point, loader, renderer, container, stats;
    
    const targetRotation = 0;
    const targetRotationOnMouseDown = 0;
    const mouseX = 0;
    const mouseXOnMouseDown = 0;
    const windowHalfX = window.innerWidth / 2;
    const windowHalfY = window.innerHeight / 2;
    
    
    init();
    animate();
    var box, b1, b2, b3;
    
    function init() {
      // Create a scene which will hold all our meshes to be rendered
      scene = new THREE.Scene();
    
      // Create and position a camera
      camera = new THREE.PerspectiveCamera(
         60, // Field of view
         window.innerWidth / window.innerHeight, // Aspect ratio
        /*window.innerWidth / -8,
        window.innerWidth / 8,
        window.innerHeight / 8,
        window.innerHeight / -8,
        */
        0.1, // Near clipping pane
        1000 // Far clipping pane
      );
    scene.add(camera)
      // Reposition the camera
      camera.position.set(0, 5, 10);
      // Point the camera at a given coordinate
      camera.lookAt(new THREE.Vector3(0, 0, 0));
      // Add orbit control
      controls = new THREE.OrbitControls(camera);
      controls.target.set(0, -0.5, 0);
      controls.update();
    
      // Add an ambient lights
      ambient = new THREE.AmbientLight(0xffffff, 0.2);
      scene.add(ambient);
    
      // Add a point light that will cast shadows
      point = new THREE.PointLight(0xffffff, 1);
      point.position.set(25, 50, 25);
      point.castShadow = true;
      point.shadow.mapSize.width = 1024;
      point.shadow.mapSize.height = 1024;
      scene.add(point);
    
      group = new THREE.Group();
      group.position.y = 0;
      scene.add(group);
      rotationAnchor = new THREE.Object3D()
      group.add(rotationAnchor);
    
      box = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshStandardMaterial({
        color: 'grey'
      }))
      b1 = box.clone();
      b2 = box.clone();
      b3 = box.clone();
      b3.material = b3.material.clone()
      b3.material.color.set('red')
      group.add(box);
      group.add(b1);
      b1.position.y += 1
      group.add(b2);
      b2.position.z += 1
      rotationAnchor.add(b3);
      rotationAnchor.position.set(0.5, 0.5, 1.5)
      b3.position.set(-.5, -.5, -.5)
      // Create a renderer
      renderer = new THREE.WebGLRenderer({
        antialias: true
      });
      // Set size
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      // Set color
      renderer.setClearColor(0xf8a5c2);
      renderer.gammaOutput = true;
      // Enable shadow mapping
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    
      // Append to the document
      container = document.createElement("div");
      document.body.appendChild(container);
      document.body.appendChild(renderer.domElement);
      // Add resize listener
      window.addEventListener("resize", onWindowResize, false);
    
      // Enable FPS stats
      stats = new Stats();
      container.appendChild(stats.dom);
    
      var gui = new dat.GUI({
        height: 5 * 32 - 1
      });
      let params = {
        'test': 4,
        'bevelThickness': 1,
        'bevelSize': 1.5,
        'bevelSegments': 3
      }
      gui.add(params, 'test', 0, 10).onChange(val => {
        test = val
      })
    }
    
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }
    
    function animate() {
      rotationAnchor.rotation.z = (Math.cos(performance.now() * 0.001) * Math.PI * 0.25) + (Math.PI * 1.25)
      requestAnimationFrame(animate);
      // Re-render scene
      renderer.render(scene, camera);
      // Update stats
      stats.update();
    }
    body {
      overflow: hidden;
      margin: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
    <script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.min.js"></script>

    【讨论】:

    • 我如何使用bounds 来旋转? (对不起这个愚蠢的问题,我真的是 3js 新手)
    • 哦,我误会了.. 我以为您只是想将对象分开,以免它们相互渗透。要围绕不同的点旋转对象...您可以创建一个空的 THREE.Object3D()。在那一点上,然后将您的红色框添加到该对象...然后旋转空对象。
    • 谢谢,我会调查这种方法。如果您可以在答案中添加伪代码,那也将非常有帮助
    • 如果您在下次遇到问题时复制该 sn-p 将非常有帮助,您可以包含示例设置。 :)
    猜你喜欢
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2021-08-04
    • 1970-01-01
    • 1970-01-01
    • 2012-05-13
    • 1970-01-01
    • 2014-04-28
    相关资源
    最近更新 更多