THREE.js - 旋转移动3D球

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了THREE.js - 旋转移动3D球相关的知识,希望对你有一定的参考价值。

我是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>
答案

如果你想要包含摩擦力和惯性等,这实际上是一个非常先进的物理学方法。但你可以采取一些快捷方式来获得一个体面的视觉滚动效果......

如果你在球的运动方向上取向量,你可以得到一个垂直向量..通过运动向量的.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>
另一答案

如果我正确理解你的情况,那么你将需要对球进行旋转,这是基于球的局部空间的“右轴”。

THREE.js提供了许多辅助方法来简化namely the makeRotationAxis() method类的THREE.Matrix4数学运算。

在概念上和实际上,对你的ball.rotation数学进行一些小的调整应该达到你想要的。请参阅以下代码段以了解如何完成此操作(或参见this working jsFiddle):

   /*
            *
            * SET UP MOTION PARAMS
            * 
            */
            
            var rotationAngle = 0;

   

以上是关于THREE.js - 旋转移动3D球的主要内容,如果未能解决你的问题,请参考以下文章

Three.js源码解读一:Object3D

three.js 在它的中心围绕 Y 轴旋转 Object3d

如何在轴 three.js 上旋转 3D 对象?

Three.js 飞机的 3D 旋转

使用 Three.js 将 3D 对象旋转到鼠标

在three.js中获取鼠标点击点的3D坐标