Three.js旋转一个球体,然后是相机

时间:2018-01-03 02:39:26

标签: javascript three.js

我正在玩一个玩具Three.js的场景,我想跟随一个带有相机 [demo]的球体。但是现在,我无法弄清楚如何制作球体" roll"没有旋转相机。

这是我用来更新每个帧的球体位置的代码:

function moveSphere() {
  var delta = clock.getDelta(); // seconds
  var moveDistance = 200 * delta; // 200 pixels per second
  var rotateAngle = Math.PI / 2 * delta; // pi/2 radians (90 deg) per sec

  // move forwards/backwards/left/right
  if ( pressed['W'] ) {
    sphere.translateZ( -moveDistance );
  }
  if ( pressed['S'] ) 
    sphere.translateZ(  moveDistance );
  if ( pressed['Q'] )
    sphere.translateX( -moveDistance );
  if ( pressed['E'] )
    sphere.translateX(  moveDistance ); 

  // rotate left/right/up/down
  var rotation_matrix = new THREE.Matrix4().identity();
  if ( pressed['A'] )
    sphere.rotateOnAxis(new THREE.Vector3(0,1,0), rotateAngle);
  if ( pressed['D'] )
    sphere.rotateOnAxis(new THREE.Vector3(0,1,0), -rotateAngle);
  if ( pressed['R'] )
    sphere.rotateOnAxis(new THREE.Vector3(1,0,0), rotateAngle);
  if ( pressed['F'] )
    sphere.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle);
}

每个时间刻度跟随球体的代码:

function moveCamera() {
  var relativeCameraOffset = new THREE.Vector3(0,50,200);
  var cameraOffset = relativeCameraOffset.applyMatrix4(sphere.matrixWorld);
  camera.position.x = cameraOffset.x;
  camera.position.y = cameraOffset.y;
  camera.position.z = cameraOffset.z;
  camera.lookAt(sphere.position);
}

是否有一种简单的方法可以使球滚动而不会使相机螺旋到处?在if (pressed['W'])区块的内部,我尝试了sphere.rotateOnAxis(new THREE.Vector3(0,0,1), rotateAngle);的各种排列,但是没有找到一种让球滚动的自然方式。我非常感谢其他人可以提供的任何建议!

2 个答案:

答案 0 :(得分:0)

您的问题是这一行:

var cameraOffset = relativeCameraOffset.applyMatrix4(sphere.matrixWorld);

这将获取您指定的偏移量,不仅适用于球体位置,还适用于旋转。例如,如果球体在Y轴上旋转180度,则生成的矢量为(0,50,200)+(球体位置)。

您需要从球体矩阵中提取平移分量,并将其应用于偏移量。下面的代码使用中间矩阵来存储球体的位置。

    /**
     * Follow the sphere
     **/
    var sphereTranslation = new THREE.Matrix4(); // only make it once to reduce overhead

    function moveCamera() {
        var relativeCameraOffset = new THREE.Vector3(0,50,200);
        sphereTranslation.copyPosition(sphere.matrixWorld); // get sphere position only
        var cameraOffset = relativeCameraOffset.applyMatrix4(sphereTranslation);
        camera.position.x = cameraOffset.x;
        camera.position.y = cameraOffset.y;
        camera.position.z = cameraOffset.z;
        camera.lookAt(sphere.position);
    }

答案 1 :(得分:0)

这里的关键是创建一个球体,然后将该球体添加到一个组中,这样我就可以平移和旋转组(控制球的位置),同时还可以在组内旋转球体(允许球到“滚”)。将这些实体分开可以让相机像以前一样跟随球体组,同时允许球独立于球体组的移动[updated demo]旋转:

  /**
  * Generate a scene object with a background color
  **/

  function getScene() {
    var scene = new THREE.Scene();
    scene.background = new THREE.Color(0x111111);
    return scene;
  }

  /**
  * Generate the camera to be used in the scene. Camera args:
  *   [0] field of view: identifies the portion of the scene
  *     visible at any time (in degrees)
  *   [1] aspect ratio: identifies the aspect ratio of the
  *     scene in width/height
  *   [2] near clipping plane: objects closer than the near
  *     clipping plane are culled from the scene
  *   [3] far clipping plane: objects farther than the far
  *     clipping plane are culled from the scene
  **/

  function getCamera() {
    var aspectRatio = window.innerWidth / window.innerHeight;
    var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
    camera.position.set(0,150,400);
    camera.lookAt(scene.position);  
    return camera;
  }

  /**
  * Generate the light to be used in the scene. Light args:
  *   [0]: Hexadecimal color of the light
  *   [1]: Numeric value of the light's strength/intensity
  *   [2]: The distance from the light where the intensity is 0
  * @param {obj} scene: the current scene object
  **/

  function getLight(scene) {
    var lights = [];
    lights[0] = new THREE.PointLight( 0xffffff, 0.6, 0 );
    lights[0].position.set( 100, 200, 100 );
    scene.add( lights[0] );

    var ambientLight = new THREE.AmbientLight(0x111111);
    scene.add(ambientLight);
    return light;
  }

  /**
  * Generate the renderer to be used in the scene
  **/

  function getRenderer() {
    // Create the canvas with a renderer
    var renderer = new THREE.WebGLRenderer({antialias: true});
    // Add support for retina displays
    renderer.setPixelRatio(window.devicePixelRatio);
    // Specify the size of the canvas
    renderer.setSize(window.innerWidth, window.innerHeight);
    // Add the canvas to the DOM
    document.body.appendChild(renderer.domElement);
    return renderer;
  }

  /**
  * Generate the controls to be used in the scene
  * @param {obj} camera: the three.js camera for the scene
  * @param {obj} renderer: the three.js renderer for the scene
  **/

  function getControls(camera, renderer) {
    var controls = new THREE.TrackballControls(camera, renderer.domElement);
    controls.zoomSpeed = 0.4;
    controls.panSpeed = 0.4;
    return controls;
  }

  /**
  * Get grass
  **/

  function getPlane(scene, loader) {
    var texture = loader.load('grass.jpg');
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
    texture.repeat.set( 10, 10 );
    var material = new THREE.MeshBasicMaterial({
      map: texture, side: THREE.DoubleSide
    });
    var geometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
    var plane = new THREE.Mesh(geometry, material);
    plane.position.y = -0.5;
    plane.rotation.x = Math.PI / 2;
    scene.add(plane);
    return plane;
  }

  /**
  * Add background
  **/

  function getBackground(scene, loader) {
    var imagePrefix = 'sky-parts/';
    var directions  = ['right', 'left', 'top', 'bottom', 'front', 'back'];
    var imageSuffix = '.bmp';
    var geometry = new THREE.BoxGeometry( 1000, 1000, 1000 ); 
    
    var materialArray = [];
    for (var i = 0; i < 6; i++)
      materialArray.push( new THREE.MeshBasicMaterial({
        map: loader.load(imagePrefix + directions[i] + imageSuffix),
        side: THREE.BackSide
      }));
    var sky = new THREE.Mesh( geometry, materialArray );
    scene.add(sky);
  }

  /**
  * Add a character
  **/

  function getSphere(scene) {
    var geometry = new THREE.SphereGeometry( 30, 12, 9 );
    var material = new THREE.MeshPhongMaterial({
      color: 0xd0901d,
      emissive: 0xaf752a,
      side: THREE.DoubleSide,
      flatShading: true
    });
    var sphere = new THREE.Mesh( geometry, material );

    // create a group for translations and rotations
    var sphereGroup = new THREE.Group();
    sphereGroup.add(sphere)

    sphereGroup.position.set(0, 24, 100);
    scene.add(sphereGroup);
    return [sphere, sphereGroup];
  }

  /**
  * Store all currently pressed keys
  **/

  function addListeners() {
    window.addEventListener('keydown', function(e) {
      pressed[e.key.toUpperCase()] = true;
    })
    window.addEventListener('keyup', function(e) {
      pressed[e.key.toUpperCase()] = false;
    })
  }

  /**
  * Update the sphere's position
  **/

  function moveSphere() {
    var delta = clock.getDelta(); // seconds
    var moveDistance = 200 * delta; // 200 pixels per second
    var rotateAngle = Math.PI / 2 * delta; // pi/2 radians (90 deg) per sec

    // move forwards/backwards/left/right
    if ( pressed['W'] ) {
      sphere.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle)
      sphereGroup.translateZ( -moveDistance );
    }
    if ( pressed['S'] ) 
      sphereGroup.translateZ(  moveDistance );
    if ( pressed['Q'] )
      sphereGroup.translateX( -moveDistance );
    if ( pressed['E'] )
      sphereGroup.translateX(  moveDistance ); 

    // rotate left/right/up/down
    var rotation_matrix = new THREE.Matrix4().identity();
    if ( pressed['A'] )
      sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), rotateAngle);
    if ( pressed['D'] )
      sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), -rotateAngle);
    if ( pressed['R'] )
      sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), rotateAngle);
    if ( pressed['F'] )
      sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle);
  }

  /**
  * Follow the sphere
  **/

  function moveCamera() {
    var relativeCameraOffset = new THREE.Vector3(0,50,200);
    var cameraOffset = relativeCameraOffset.applyMatrix4(sphereGroup.matrixWorld);
    camera.position.x = cameraOffset.x;
    camera.position.y = cameraOffset.y;
    camera.position.z = cameraOffset.z;
    camera.lookAt(sphereGroup.position);
  }

  // Render loop
  function render() {
    requestAnimationFrame(render);
    renderer.render(scene, camera);
    moveSphere();
    moveCamera();
  };

  // state
  var pressed = {};
  var clock = new THREE.Clock();

  // globals
  var scene = getScene();
  var camera = getCamera();
  var light = getLight(scene);
  var renderer = getRenderer();

  // add meshes
  var loader = new THREE.TextureLoader();
  var floor = getPlane(scene, loader);
  var background = getBackground(scene, loader);
  var sphereData = getSphere(scene);
  var sphere = sphereData[0];
  var sphereGroup = sphereData[1];

  addListeners();
  render();
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>