三.js getTangent on 曲线应该在曲线上移动时正确旋转我的对象



它确实旋转,使用:

t = 0
animate = ->
  requestAnimationFrame animate
  t += 0.001
  # ...
  position3 = curveSeaRoute.getPoint(t+.3)
  tangent3 = curveSeaRoute.getTangent(t)
  ship.position.copy(position3)
  ship.lookAt(tangent3)
  # console.log tangent3
  renderer.render scene, camera
animate()

我的船+"路线"定义如下:

# Ship
centerLength = 2
bodyLength = 3
bodyWidth = 2
deckHeight = 0.1
geometry = new THREE.Geometry() 
v1 = new THREE.Vector3(1, deckHeight,0)
v2 = new THREE.Vector3(-2, deckHeight,2)
v3 = new THREE.Vector3(1, deckHeight,2)
v4 = new THREE.Vector3(-2, deckHeight,0)
v5 = new THREE.Vector3(-3, deckHeight, 1)
geometry.vertices.push(v1)
geometry.vertices.push(v2)
geometry.vertices.push(v3)
geometry.vertices.push(v4)
geometry.vertices.push(v5)
geometry.faces.push( new THREE.Face3( 0, 1, 2 ) )
geometry.faces.push( new THREE.Face3( 3, 1, 0 ) )
geometry.faces.push( new THREE.Face3( 3, 4, 1 ) )
material = new THREE.MeshBasicMaterial( { color: 0x005b1e } )
ship = new THREE.Mesh( geometry, material )
ship.translateZ 20 
ship.translateX 14.5
scene.add( ship )
# Sea route
curveSeaRoute = new THREE.CatmullRomCurve3([
  new THREE.Vector3( 18, roadHeight, 31 ),
  new THREE.Vector3( 24, roadHeight, 23 ),
  new THREE.Vector3( 22, roadHeight, 20 ),
  new THREE.Vector3( 14, roadHeight, 19 ),
  new THREE.Vector3( 10, roadHeight, 20 ),
  new THREE.Vector3( 8, roadHeight, 23 ),
  new THREE.Vector3( 10, roadHeight, 31 ),
] )
geometry = new THREE.Geometry()
geometry.vertices = curveSeaRoute.getPoints( 200 )
material = new THREE.LineBasicMaterial( { color : 0x45607c } )
curveObject = new THREE.Line( geometry, material )
scene.add( curveObject )

不过,轮换的问题不是很顺利:

http://jsfiddle.net/CoderX99/66b3j9wa/6/

getTangent 确实为我提供了有价值的数据,这些数据看起来像曲线上的方向矢量(切线)。现在我当然可以使用三角函数将 y 轴旋转角度从 x 和 z 值中取出,但我希望能有一种更简单的 - 三.js - 方法。

我也希望旋转中心在船的中间。看起来它现在在后面的某个地方。

来自囚犯849。再加上整理我自己的代码。

setOutPath = (path, color, segments ) ->
    geometry = new THREE.Geometry()
    geometry.vertices = path.getPoints( segments )
    material = new THREE.LineBasicMaterial( { color : color } )
    curveObject = new THREE.Line( geometry, material )
    scene.add( curveObject )
setOutPath(curveRoadNo1, 0xa9c41e, 500)
setOutPath(curveRoadNo2, 0xa9c41e, 200)
setOutPath(curveSeaRoute, 0x45607c, 200)
# Moving objects
# Vehicle
geometry = new THREE.SphereBufferGeometry( 0.1, 32, 32 )
material = new THREE.MeshBasicMaterial( {color: 0xffff00} )
vehicle1 = new THREE.Mesh( geometry, material )
scene.add( vehicle1 )
vehicle2 = vehicle1.clone()
scene.add( vehicle2 )
vehicle3 = vehicle1.clone()
scene.add( vehicle3 )
# The ship
centerLength = 2
bodyLength = 3
bodyWidth = 2
deckHeight = 0.1
geometry = new THREE.PlaneGeometry(2,2,2)
geometry.vertices[4].y = -3
geometry.rotateX(-Math.PI * 0.5)
geometry.translate(0, deckHeight, 0)
material = new THREE.MeshBasicMaterial({
    color: 0x005b1e
  })
ship = new THREE.Mesh(geometry, material)
ship.translateZ(15)
ship.translateX(15)
scene.add(ship)
# vehicle1, vehicle2, vehicle3, ship
t1 = { value: 0 } # for RoadNo1
t2 = { value: 0 } # for RoadNo2
t3 = { value: 0 } # for SeaRoute
# Tween updates
updateT = -> 
    vehicle1.position.copy(curveRoadNo1.getPointAt(t1.value))
    vehicle2.position.copy(curveRoadNo1.getPointAt(1 - t1.value)) 
updateT2 = ->
    vehicle3.position.copy(curveRoadNo2.getPointAt(t2.value))
updateT3 = ->
    lookAt = (t3.value + 0.0001) % 1
    ship.position.copy(curveSeaRoute.getPointAt(t3.value))
    ship.lookAt(curveSeaRoute.getPointAt(lookAt))
# Tweens - for Inbetweens
tween11 = new TWEEN.Tween(t1).to({ value: .3 }, 7000).delay(500).onUpdate(updateT)
tween12 = new TWEEN.Tween(t1).to({ value: 1 }, 3000).delay(1000).onUpdate(updateT).onComplete( () ->
        t1.value = 0
    )
tween11.chain(tween12)
tween12.chain(tween11)
tween11.start()
tween21 = new TWEEN.Tween(t2).to({ value: 1 }, 10000).onUpdate(updateT2)
tween22 = new TWEEN.Tween(t2).to({ value: 0 }, 10000).onUpdate(updateT2)
tween21.chain(tween22)
tween22.chain(tween21)
tween21.start()
tween3 = new TWEEN.Tween(t3).to({ value: 1 }, 17000).delay(1500).onUpdate(updateT3).onComplete( () ->
        t3.value = 0
    )
tween3.chain(tween3)
tween3.start()
controls = new THREE.OrbitControls(camera, renderer.domElement)
camera.position.x = colms/2
camera.position.y = 13
camera.position.z = 25
camera.rotation.x = -40 * Math.PI / 180
controls.target = new THREE.Box3().setFromObject(scene).getCenter()
controls.update()
t = 0
animate = ->
    requestAnimationFrame animate
    TWEEN.update()
    renderer.render scene, camera
animate()
您可以使用

ship .lookAt()方法。看看updateT3功能。

我重新设计了你的jsfiddle(一点):简化了飞船的创建;使用Tween.js制作动画;添加了THREE.OrbitControls()以获得更好的视图。

请参阅代码片段。

// Generated by CoffeeScript 2.0.1
(function() {
  var animate, bodyLength, bodyWidth, build_city, camera, centerLength, city_map, colms, curveObject, curveRoadNo1, curveRoadNo2, deckHeight, flora_density_map, geometry, height, heightBuilding, height_map, i, j, k, len, len1, material, plant_tree, raise_land, ref, renderer, roadHeight, rows, scene, ship, t, v1, v2, v3, v4, v5, vehicle;
  scene = new THREE.Scene;
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio((ref = window.devicePixelRatio) != null ? ref : window.devicePixelRatio || 1);
  document.body.appendChild(renderer.domElement);
  var controls = new THREE.OrbitControls(camera, renderer.domElement);
  //##########################################################################################
  colms = 30;
  rows = 14;
  height_map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 4, 5, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 2, 1, 3, 4, 7, 5, 5, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 2, 3, 3, 4, 5, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 4, 5, 6, 6];
  flora_density_map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0];
  city_map = [9, 9, 9, 9, 9, 0, 0, 9, 9, 0, 0, 9];
  plant_tree = function(x, y, z) {
    var cone, geometry, material;
    geometry = new THREE.ConeGeometry(0.2, 0.5);
    material = new THREE.MeshBasicMaterial({
      color: 0x20281e
    });
    cone = new THREE.Mesh(geometry, material);
    cone.translateX(x);
    cone.translateY(y + .25);
    cone.translateZ(z);
    return scene.add(cone);
  };
  build_city = function(start_c, start_r, colms, rows, i, heightBuilding) {
    var geometry, height, land, material, row, width;
    if (heightBuilding !== 0) {
      height = heightBuilding * 0.035;
      width = 0.5;
      geometry = new THREE.BoxGeometry(width, height, width);
      material = new THREE.MeshBasicMaterial({
        color: 0x8b0000
      });
      land = new THREE.Mesh(geometry, material);
      // Set box base equal to overall baselevel.
      land.translateY(height / 2);
      // Normal animation translation.
      row = (Math.floor(i / colms)) + 1;
      land.translateZ(row + start_r - 1);
      land.translateX(i - (colms * (row - 1)) + start_c);
      return scene.add(land);
    }
  };
  raise_land = function(colms, rows, i, height) {
    var color, geometry, land, material, row;
    color = new THREE.Color(`rgb(${0 + height * 12}, 153, ${51 + height * 30})`);
    height = height * 0.22;
    geometry = new THREE.BoxGeometry(1, height, 1);
    material = new THREE.MeshBasicMaterial({
      color: color
    });
    land = new THREE.Mesh(geometry, material);
    // Set box base equal to overall baselevel.
    land.translateY(height / 2);
    // Normal animation translation.
    row = (Math.floor(i / colms)) + 1;
    land.translateZ(row);
    land.translateX(i - (colms * (row - 1)));
    scene.add(land);
    if (flora_density_map[i] !== 0) {
      return plant_tree(i - (colms * (row - 1)), height, row);
    }
  };
  centerLength = 2;
  bodyLength = 3;
  bodyWidth = 2;
  deckHeight = 0.1;
  geometry = new THREE.PlaneGeometry(2, 2, 2);
  geometry.vertices[4].y = -3;
  geometry.rotateX(-Math.PI * .5);
  geometry.translate(0, .1, 0);
  material = new THREE.MeshBasicMaterial({
    color: 0x005b1e
  });
  ship = new THREE.Mesh(geometry, material);
  ship.translateZ(15);
  ship.translateX(15);
  scene.add(ship);
  // Roads around the main settlement.
  roadHeight = 0.1;
  curveRoadNo1 = new THREE.CatmullRomCurve3([
    new THREE.Vector3(13,
      roadHeight,
      13),
    new THREE.Vector3(12,
      roadHeight,
      14),
    new THREE.Vector3(10,
      roadHeight,
      14),
    new THREE.Vector3(10,
      roadHeight,
      13),
    new THREE.Vector3(11,
      roadHeight,
      12),
    new THREE.Vector3(11,
      roadHeight,
      11),
    // new THREE.Vector3( 9, roadHeight, 10),
    // new THREE.Vector3( 7, roadHeight, 9),
    new THREE.Vector3(4,
      roadHeight,
      10),
    new THREE.Vector3(4,
      roadHeight,
      7),
    new THREE.Vector3(1,
      roadHeight,
      3),
    new THREE.Vector3(5,
      roadHeight,
      2),
    new THREE.Vector3(10,
      roadHeight,
      4),
    new THREE.Vector3(15,
      roadHeight,
      3),
    new THREE.Vector3(18,
      roadHeight,
      6),
    new THREE.Vector3(22,
      roadHeight,
      2),
    new THREE.Vector3(25,
      roadHeight,
      5),
    new THREE.Vector3(25,
      roadHeight,
      7),
    new THREE.Vector3(21,
      roadHeight,
      11),
    new THREE.Vector3(19,
      roadHeight,
      9),
    new THREE.Vector3(17,
      roadHeight,
      11),
    new THREE.Vector3(19,
      roadHeight,
      12),
    new THREE.Vector3(18,
      roadHeight,
      13)
  ]);
  curveRoadNo2 = new THREE.CatmullRomCurve3([new THREE.Vector3(15, roadHeight, 11), new THREE.Vector3(12, roadHeight, 12), new THREE.Vector3(11, roadHeight, 11), new THREE.Vector3(11, roadHeight, 9), new THREE.Vector3(12, roadHeight, 8), new THREE.Vector3(12, roadHeight, 5), new THREE.Vector3(15, roadHeight, 3), new THREE.Vector3(17, roadHeight, 7), new THREE.Vector3(16, roadHeight, 8), new THREE.Vector3(16, roadHeight, 9), new THREE.Vector3(17, roadHeight, 11)]);
  curveRoadNo2.closed = true;
  geometry = new THREE.Geometry();
  geometry.vertices = curveRoadNo1.getPoints(300);
  material = new THREE.LineBasicMaterial({
    color: 0xa9c41e
  });
  curveObject = new THREE.Line(geometry, material);
  scene.add(curveObject);
  geometry = new THREE.Geometry();
  geometry.vertices = curveRoadNo2.getPoints(200);
  material = new THREE.LineBasicMaterial({
    color: 0xa9c41e
  });
  curveObject = new THREE.Line(geometry, material);
  scene.add(curveObject);
  geometry = new THREE.SphereBufferGeometry(0.2, 32, 32);
  material = new THREE.MeshBasicMaterial({
    color: 0xffff00
  });
  vehicle = new THREE.Mesh(geometry, material);
  scene.add(vehicle);
  for (i = j = 0, len = height_map.length; j < len; i = ++j) {
    height = height_map[i];
    // vehicle.position.x = 15
    // vehicle.position.z = 3
    // Fundaments of scene.
    raise_land(30, 14, i, height);
  }
  for (i = k = 0, len1 = city_map.length; k < len1; i = ++k) {
    heightBuilding = city_map[i];
    build_city(14, 12, 4, 3, i, heightBuilding);
  }
  camera.position.x = colms / 2;
  camera.position.y = 15;
  camera.position.z = 30;
  controls.target = new THREE.Box3().setFromObject(scene).getCenter();
  controls.update();
  t = {
    value: 0
  };
  vehicle2 = new THREE.Mesh(vehicle.geometry, vehicle.material);
  scene.add(vehicle2);
  vehicle3 = new THREE.Mesh(vehicle.geometry, vehicle.material);
  scene.add(vehicle3);
  var tween11 = new TWEEN.Tween(t).to({
    value: .3
  }, 7000).delay(500).onUpdate(updateT);
  var tween12 = new TWEEN.Tween(t).to({
    value: 1
  }, 3000).delay(1000).onUpdate(updateT).onComplete(function() {
    t.value = 0
  });
  tween11.chain(tween12);
  tween12.chain(tween11);
  tween11.start();
  function updateT() {
    vehicle.position.copy(curveRoadNo2.getPointAt(t.value));
    vehicle2.position.copy(curveRoadNo2.getPointAt(1 - t.value));
  }
  let t2 = {
    value: 0
  };
  var tween21 = new TWEEN.Tween(t2).to({
    value: 1
  }, 10000).onUpdate(updateT2);
  var tween22 = new TWEEN.Tween(t2).to({
    value: 0
  }, 10000).onUpdate(updateT2);
  tween21.chain(tween22);
  tween22.chain(tween21);
  tween21.start();
  function updateT2() {
    vehicle3.position.copy(curveRoadNo1.getPointAt(t2.value));
  }
  var shipCurve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(15, 0, 15),
    new THREE.Vector3(0, 0, 25),
    new THREE.Vector3(30, 0, 25)
  ]);
  shipCurve.closed = true;
  var shipLineGeometry = new THREE.Geometry();
  shipLineGeometry.vertices = shipCurve.getPoints(200);
  var shipLine = new THREE.Line(shipLineGeometry, new THREE.LineBasicMaterial({
    color: "aqua"
  }));
  scene.add(shipLine);
  let t3 = {
    value: 0
  };
  var tween3 = new TWEEN.Tween(t3).to({
    value: 1
  }, 17000).delay(1500).onUpdate(updateT3).onComplete(function() {
    t3.value = 0;
  });
  tween3.chain(tween3);
  tween3.start();
  function updateT3() {
    let lookAt = (t3.value + 0.0001) % 1;
    ship.position.copy(shipCurve.getPointAt(t3.value));
    ship.lookAt(shipCurve.getPointAt(lookAt));
  }
  animate = function() {
    requestAnimationFrame(animate);
    TWEEN.update();
    renderer.render(scene, camera);
  };
  animate();
}).call(this);
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/libs/tween.min.js"></script>

作为参考,请查看此示例的源代码。

最新更新