【问题标题】:THREE.js - moving a 3D ball with a rotationTHREE.js - 通过旋转移动 3D 球
【发布时间】:2018-09-02 13:13:36
【问题描述】:

我是 THREE.js 的新手,对物理学的了解很差 - 但我正在尝试构建一个足球游戏引擎(从顶部看),现在我正在努力应对球的运动。

当尝试将球从一侧移动到另一侧时,旋转始终面向一个方向,我不明白如何使其沿其移动方向旋转。

我添加了一个显示此问题的简单代码。非常感谢您的帮助。

     /*
            *
            * SET UP MOTION PARAMS
            * 
            */

            var degrees = 10;
            var power = 1;
            var angleRad = degrees * Math.PI / 120;

            var velocityX = Math.cos(angleRad) * power;
            var velocityY = Math.sin(angleRad) * power;
            var velocityZ = 1;

            var friction = 1;
            var gravity = 0.2;
            var bounciness = 0.9;



            window.onload = function (params) {

                /*
                *
                * SET UP THE WORLD
                * 
                */
                
                
                
                //set up the ratio
                var gWidth = window.innerWidth;
                var gHeight = window.innerHeight;
                var ratio = gWidth / gHeight;
                var borders = [40, 24] //indicate where the ball needs to move in mirror position


                //set the scene
                scene = new THREE.Scene();
                scene.background = new THREE.Color(0xeaeaea);

                //set the camera
                var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
                camera.position.z = 120;   

                //set the light
                var light = new THREE.SpotLight(0xffffff, 1);
                light.position.set(100, 1, 0); 		
                light.castShadow = true;         
                light.position.set(0, 0, 100);
                scene.add(light);

                //  set the renderer 
                var renderer = new THREE.WebGLRenderer();

                //properties for casting shadow
                renderer.shadowMap.enabled = true;
                renderer.shadowMap.type = THREE.PCFSoftShadowMap; 

                renderer.setSize(gWidth, gHeight);
                document.body.appendChild(renderer.domElement);



                
                /*
                *
                * ADD MESH TO SCENE
                * 
                */


                // create and add the ball
                var geometry = new THREE.SphereGeometry(5, 5, 5);
                var material = new THREE.MeshLambertMaterial({ color: 'gray' });
                var ball = new THREE.Mesh(geometry, material);

                ball.castShadow = true;
                ball.receiveShadow = false;
                scene.add(ball);


                
                // create and add the field
                var margin = 20;
                var fieldRatio = 105 / 68;

                var width = 90;
                var height = width / fieldRatio;

                var material = new THREE.MeshLambertMaterial({ color: 'green' });
                var geometry = new THREE.BoxGeometry(width, height, 1);
                var field = new THREE.Mesh(geometry, material);

                field.receiveShadow = true;
                field.position.z = -1;
                scene.add(field);




                /*
                * setting up rotation axis 
                */


                var rotation_matrix = null;

                var setQuaternions = function () {
                    setMatrix();
                    ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
                    ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
                }

                var setMatrix = function () {
                    rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
                }

                setQuaternions();


                /*
                *
                * ANIMATION STEP
                * 
                */

                var render = function (params) {

                    // add velocity to ball
                    ball.position.x += velocityX;
                    ball.position.z += velocityZ;
                    ball.position.y += velocityY;


                    //validate if ball is stop moving
                    if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
                        console.log("DONE!");
                        return;
                    }



                    // handle boucing effect
                    if (ball.position.z < 1) {
                        velocityZ *= -bounciness;
                        ball.position.z = 1
                    }


                    // Update the object's rotation & apply it
                    ball.matrix.multiply(rotation_matrix);
                    ball.rotation.setFromRotationMatrix(ball.matrix);


                    //reducing speed by friction
                    angleRad *= friction;
                    velocityX *= friction;
                    velocityY *= friction;
                    velocityZ *= friction;


                    //set up the matrix 
                    setMatrix();
                    


                    //validate ball is withing its borders otherwise go in the mirror direction
                    if (Math.abs(ball.position.x) > borders[0]) {
                        velocityX *= -1;
                        ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
                    }

                    if (Math.abs(ball.position.y) > borders[1]) {
                        velocityY *= -1;
                        ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
                    }


                    // reduce ball height with gravity
                    velocityZ -= gravity;

                    

                    //render the page
                    renderer.render(scene, camera);

                    requestAnimationFrame(render);
                }

                render();

            }
body {
    padding: 0;
    margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>

<head>

</head>

<body>
</body>

</html>

【问题讨论】:

    标签: javascript three.js 3d physics


    【解决方案1】:

    如果我正确理解您的情况,那么您将希望对球应用旋转,即基于球局部空间的“右轴”。

    THREE.js 提供了许多帮助方法来简化此数学运算,namely the makeRotationAxis() methodTHREE.Matrix4 类上。

    从概念上和实践上来说,对你的 ball.rotation 数学进行一些小的调整应该可以达到你想要的效果。请查看以下代码 sn-p 了解如何执行此操作(或查看this working jsFiddle):

       /*
                *
                * SET UP MOTION PARAMS
                * 
                */
                
                var rotationAngle = 0;
    
                var degrees = 10;
                var power = 1;
                var angleRad = degrees * Math.PI / 120;
    
                var velocityX = Math.cos(angleRad) * power;
                var velocityY = Math.sin(angleRad) * power;
                var velocityZ = 1;
    
                var friction = 1;
                var gravity = 0.2;
                var bounciness = 0.9;
    
    
    
                window.onload = function (params) {
    
                    /*
                    *
                    * SET UP THE WORLD
                    * 
                    */
                    
                    
                    
                    //set up the ratio
                    var gWidth = window.innerWidth;
                    var gHeight = window.innerHeight;
                    var ratio = gWidth / gHeight;
                    var borders = [40, 24] //indicate where the ball needs to move in mirror position
    
    
                    //set the scene
                    scene = new THREE.Scene();
                    scene.background = new THREE.Color(0xeaeaea);
    
                    //set the camera
                    var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
                    camera.position.z = 120;   
    
                    //set the light
                    var light = new THREE.SpotLight(0xffffff, 1);
                    light.position.set(100, 1, 0); 		
                    light.castShadow = true;         
                    light.position.set(0, 0, 100);
                    scene.add(light);
    
                    //  set the renderer 
                    var renderer = new THREE.WebGLRenderer();
    
                    //properties for casting shadow
                    renderer.shadowMap.enabled = true;
                    renderer.shadowMap.type = THREE.PCFSoftShadowMap; 
    
                    renderer.setSize(gWidth, gHeight);
                    document.body.appendChild(renderer.domElement);
    
    
    
                    
                    /*
                    *
                    * ADD MESH TO SCENE
                    * 
                    */
    
    
                    // create and add the ball
                    var geometry = new THREE.SphereGeometry(5, 5, 5);
                    var material = new THREE.MeshLambertMaterial({ color: 'gray' });
                    var ball = new THREE.Mesh(geometry, material);
    
                    ball.castShadow = true;
                    ball.receiveShadow = false;
                    scene.add(ball);
    
    
                    
                    // create and add the field
                    var margin = 20;
                    var fieldRatio = 105 / 68;
    
                    var width = 90;
                    var height = width / fieldRatio;
    
                    var material = new THREE.MeshLambertMaterial({ color: 'green' });
                    var geometry = new THREE.BoxGeometry(width, height, 1);
                    var field = new THREE.Mesh(geometry, material);
    
                    field.receiveShadow = true;
                    field.position.z = -1;
                    scene.add(field);
    
    
    
    
                    /*
                    * setting up rotation axis 
                    */
    
    
                    var rotation_matrix = null;
    
                    var setQuaternions = function () {
                        setMatrix();
                        ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
                        ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
                    }
    
                    var setMatrix = function () {
                        rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
                    }
    
                    setQuaternions();
    
    
                    /*
                    *
                    * ANIMATION STEP
                    * 
                    */
    
                    var render = function (params) {
    
                        // add velocity to ball
                        ball.position.x += velocityX;
                        ball.position.z += velocityZ;
                        ball.position.y += velocityY;
    
    
                        //validate if ball is stop moving
                        if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
                            console.log("DONE!");
                            return;
                        }
    
    
    
                        // handle boucing effect
                        if (ball.position.z < 1) {
                            velocityZ *= -bounciness;
                            ball.position.z = 1
                        }
    
    
                        // Update the object's rotation & apply it
                      //                    ball.matrix.multiply(rotation_matrix);
                      // Compute the direction vector of the balls current forward direction of motion
                      var vectorDirection = new THREE.Vector3(velocityX, velocityY, velocityZ);
    
                      // Compute the vector about which the balls rotation is calculated. This is at a 
                      // right angle to the vectorDirection, and so we use the cross product to 
                      // calculate this
                      var axisOfRotation = new THREE.Vector3().crossVectors(vectorDirection, new THREE.Vector3(0,0,1) );
    
                      // Normalise the rotation axis to unit length
                      axisOfRotation.normalize();
    
                      // Build a rotation matrix around the rotation axis.
                      var rotation = new THREE.Matrix4();
                      rotation .makeRotationAxis(axisOfRotation, rotationAngle)
                      ball.rotation.setFromRotationMatrix(rotation );
    
                      // Decrement the rotation angle to achieve the rolling effect
                      rotationAngle -= 0.1;
                        
                       // ball.rotation.setFromRotationMatrix(ball.matrix);
    
    
                        //reducing speed by friction
                        angleRad *= friction;
                        velocityX *= friction;
                        velocityY *= friction;
                        velocityZ *= friction;
    
    
                        //set up the matrix 
                        setMatrix();
                        
    
    
                        //validate ball is withing its borders otherwise go in the mirror direction
                        if (Math.abs(ball.position.x) > borders[0]) {
                            velocityX *= -1;
                            ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
                        }
    
                        if (Math.abs(ball.position.y) > borders[1]) {
                            velocityY *= -1;
                            ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
                        }
    
    
                        // reduce ball height with gravity
                        velocityZ -= gravity;
    
                        
    
                        //render the page
                        renderer.render(scene, camera);
    
                        requestAnimationFrame(render);
                    }
    
                    render();
    
                }
    body {
        padding: 0;
        margin: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
    <html>
    
    <head>
    
    </head>
    
    <body>
    </body>
    
    </html>

    【讨论】:

    • 只需使用Object3D.rotateOnWorldAxis( axis, angle )。另外,避免在渲染循环中调用new。这会产生很多不必要的垃圾收集。相反,创建单个实例并重用它们。
    • 呵呵.. 在发布我的@Dacre Denny 之前,我没有看到您的答案。我觉得我的比较简单。你在他原来的例子中留下了一些死代码..比如 setMatrix 方法等。看看我下面的答案......
    • @manthrax 不错的答案!我只是尝试对原始代码进行最少的修改以实现所需的内容,以避免对原始代码结构造成太多破坏等:-)
    • 酷.. 是的,你的回答也快了 20 分钟。. 我花了很多时间在球上添加纹理并调整灯光,所以我可以正确地看到它。 :P
    • 对于你们两个来说,这是一个完美的答案。这正是我所需要的(在两种情况下),感谢@manthrax 向我展示了如何在球上放置跳棋纹理(;
    【解决方案2】:

    如果你想包括摩擦和惯性等,这实际上是一个非常高级的物理学,以一种超级逼真的方式来做。但是你可以采取一些捷径来获得一个像样的视觉滚动效果......

    如果你在球的运动方向上取向量,你可以得到一个垂直向量。通过取运动向量的 .cross 乘积,与世界向上的向量。

    如果球与地面完全摩擦,该矢量是球旋转的轴。一旦你有了那个轴,你就可以将 .rotateOnWorldAxis (axis : Vector3, angle : Float) 与对象一起使用..

    然后您必须根据球的半径和行进的距离计算出要旋转多少。所以它是运动矢量 * (PI*2) 的长度(在我的代码中称为幅度) / 球的周长。

    如果这有帮助,请告诉我...

    p.s - 你的“angleRad”计算除以 120 而不是 180.. 我修复了这个问题。

    /*
     *
     * SET UP MOTION PARAMS
     * 
     */
    
    var degrees = 35;
    var power = 0.45;
    var angleRad = degrees * Math.PI / 180;
    
    var velocityX = Math.cos(angleRad) * power;
    var velocityY = Math.sin(angleRad) * power;
    var velocityZ = 1;
    
    var friction = 1;
    var gravity = 0.2;
    var bounciness = 0.9;
    
    var ballRadius = 5;
    var ballCircumference = Math.PI * ballRadius * 2;
    var ballVelocity = new THREE.Vector3();
    var ballRotationAxis = new THREE.Vector3(0, 1, 0);
    
    
    window.onload = function(params) {
    
      /*
       *
       * SET UP THE WORLD
       * 
       */
    
    
    
      //set up the ratio
      var gWidth = window.innerWidth;
      var gHeight = window.innerHeight;
      var ratio = gWidth / gHeight;
      var borders = [40, 24] //indicate where the ball needs to move in mirror position
    
    
      //set the scene
      scene = new THREE.Scene();
      scene.background = new THREE.Color(0xeaeaea);
    
      //set the camera
      var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
      camera.position.z = 120;
    
      //set the light
      var light = new THREE.SpotLight(0xffffff, 1);
      light.position.set(100, 1, 0);
      light.castShadow = true;
      light.position.set(0, 0, 35);
      scene.add(light);
    
      //  set the renderer 
      var renderer = new THREE.WebGLRenderer();
    
      //properties for casting shadow
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    
      renderer.setSize(gWidth, gHeight);
      document.body.appendChild(renderer.domElement);
    
    
    
    
      /*
       *
       * ADD MESH TO SCENE
       * 
       */
    
    
      // create and add the ball
      var geometry = new THREE.SphereGeometry(ballRadius, 8, 8);
    
      //make a checkerboard texture for the ball...
      var canv = document.createElement('canvas')
      canv.width = canv.height = 256;
      var ctx = canv.getContext('2d')
      ctx.fillStyle = 'white';
      ctx.fillRect(0, 0, 256, 256);
      ctx.fillStyle = 'black';
      
      for (var y = 0; y < 16; y++)
        for (var x = 0; x < 16; x++)
          if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
      var ballTex = new THREE.Texture(canv);
      ballTex.needsUpdate = true;
    
    
      var material = new THREE.MeshLambertMaterial({
        map: ballTex
      });
      var ball = new THREE.Mesh(geometry, material);
    
      ball.castShadow = true;
      ball.receiveShadow = false;
      scene.add(ball);
    
    
    
      // create and add the field
      var margin = 20;
      var fieldRatio = 105 / 68;
    
      var width = 90;
      var height = width / fieldRatio;
    
      var material = new THREE.MeshLambertMaterial({
        color: 'green'
      });
      var geometry = new THREE.BoxGeometry(width, height, 1);
      var field = new THREE.Mesh(geometry, material);
    
      field.receiveShadow = true;
      field.position.z = -1;
      scene.add(field);
    
    
    
    
      /*
       * setting up rotation axis 
       */
    
    
      var rotation_matrix = null;
    
      var setQuaternions = function() {
        setMatrix();
        ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
        ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
      }
    
      var setMatrix = function() {
        rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
      }
    
      setQuaternions();
    
    
      /*
       *
       * ANIMATION STEP
       * 
       */
    
      var render = function(params) {
    
        // add velocity to ball
        ball.position.x += velocityX;
        ball.position.z += velocityZ;
        ball.position.y += velocityY;
    
    
        //validate if ball is stop moving
        if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
          console.log("DONE!");
          return;
        }
    
    
    
        // handle boucing effect
        if (ball.position.z < 1) {
          velocityZ *= -bounciness;
          ball.position.z = 1
        }
    
    
        // Update the object's rotation & apply it
        /*
                        ball.matrix.multiply(rotation_matrix);   ball.rotation.setFromRotationMatrix(ball.matrix);
        //set up the matrix 
        setMatrix();
    */
    
        // Figure out the rotation based on the velocity and radius of the ball...
        ballVelocity.set(velocityX, velocityY, velocityZ);
        ballRotationAxis.set(0, 0, 1).cross(ballVelocity).normalize();
        var velocityMag = ballVelocity.length();
        var rotationAmount = velocityMag * (Math.PI * 2) / ballCircumference;
        ball.rotateOnWorldAxis(ballRotationAxis, rotationAmount)
    
    
        //reducing speed by friction
        angleRad *= friction;
        velocityX *= friction;
        velocityY *= friction;
        velocityZ *= friction;
    
    
    
    
    
        //validate ball is withing its borders otherwise go in the mirror direction
        if (Math.abs(ball.position.x) > borders[0]) {
          velocityX *= -1;
          ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
        }
    
        if (Math.abs(ball.position.y) > borders[1]) {
          velocityY *= -1;
          ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
        }
    
    
        // reduce ball height with gravity
        velocityZ -= gravity;
    
    
    
        //render the page
        renderer.render(scene, camera);
    
        requestAnimationFrame(render);
      }
    
      render();
    
    }
    body {
      padding: 0;
      margin: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
    <html>
    
    <head>
    
    </head>
    
    <body>
    </body>
    
    </html>

    【讨论】:

    • 你太棒了 - 谢谢你和@Dacre Denny 提供了这个很好的答案。像魅力一样工作
    猜你喜欢
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    相关资源
    最近更新 更多