绕任意轴旋转模型

更新时间: 2024-04-28 08:37:12

公司要做桥梁转体动画,难点就在于要让桥梁的模型绕着任意旋转轴去旋转,这个旋转轴可能不是xyz之一的坐标轴,也不是模型的中心点。

这要使用旋转矩阵去实现,具体原理在这里 绕任意不穿过原点轴的四维旋转矩阵

效果如下:绿色的方块会绕着亮蓝色的轴线为中心旋转。

代码:

let container,scene,perpCamera,renderer,cube,line

let rotateMatrix = new THREE.Matrix4()
rotateMatrix.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)

let params = {
  angle: 0,
  start:{
    x:1,
    y:2,
    z:1
  },
  end:{
    x:2,
    y:1,
    z:1
  }
}

let gui

init()
function init() {
    container = document.getElementById('myTest')

    scene = new THREE.Scene()
    scene.background = new THREE.Color(0xffffff)

    const width =  container.clientWidth
    const height = container.clientHeight

    perpCamera = new THREE.PerspectiveCamera( 60, width/height, 1, 1000)
    perpCamera.position.set(0,0,10)
    scene.add(perpCamera)

    const directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
    directionalLight.position.set(1,1,1)
    scene.add( directionalLight );

    const directionalLight1 = new THREE.DirectionalLight( 0xffffff, 0.5 );
    directionalLight1.position.set(-1,-1,-1)
    scene.add( directionalLight1 );

    renderer = new THREE.WebGLRenderer({antialias: true})
    renderer.autoClear = false
    renderer.setPixelRatio(window.devicePixelRatio)
    renderer.setSize(width, height)
    container.appendChild(renderer.domElement)

    const controls = new OrbitControls(perpCamera, renderer.domElement)
    controls.addEventListener('change', render)

    var gui = new dat.GUI({},container)
    gui.add(params,'angle', 0, 360)
    .onChange(function() {
        rotate()
        render()
    })
    gui.add(params.start,'x',-10,10).name('start-x')
    .onChange(function() {
      changeAxis()
      rotate()
      render()
    })
    gui.add(params.start,'y',-10,10).name('start-y')
    .onChange(function() {
      changeAxis()
      rotate()
      render()
    })
    gui.add(params.start,'y',-10,10).name('start-y')
    .onChange(function() {
      changeAxis()
      rotate()
      render()
    })
    gui.add(params.end,'x',-10,10).name('end-x')
    .onChange(function() {
      changeAxis()
      rotate()
      render()
    })
    gui.add(params.end,'y',-10,10).name('end-y')
    .onChange(function() {
      changeAxis()
      rotate()
      render()
    })
    gui.add(params.end,'z',-10,10).name('end-z')
    .onChange(function() {
      changeAxis()
      rotate()
      render()
    })

    const axesHelper = new THREE.AxesHelper( 5 );
    scene.add( axesHelper );

    // 添加测试模型进去
    addModel()

    render()
}

function render() {
  renderer.clear()
  renderer.render(scene,perpCamera)
}

function addModel() {
  const geometry = new THREE.BoxGeometry( 1, 1, 1 ); 
  const material = new THREE.MeshLambertMaterial( {color: 0x00ff00} ); 
  cube = new THREE.Mesh( geometry, material ); 
  scene.add( cube );

  // 添加旋转轴
  const material1 = new THREE.LineBasicMaterial({
    color: 0x00ffff
  });

  const points = [];
  points.push( new THREE.Vector3( params.start.x, params.start.y, params.start.z ) );
  points.push( new THREE.Vector3( params.end.x, params.end.y, params.end.z ) );

  const geometry1 = new THREE.BufferGeometry().setFromPoints( points );

  line = new THREE.Line( geometry1, material1 );
  scene.add( line );

  rotate()
}

function changeAxis() {
  const geometry = line.geometry
  geometry.attributes.position.setXYZ(0, params.start.x, params.start.y, params.start.z);
  geometry.attributes.position.setXYZ(1, params.end.x, params.end.y, params.end.z);
  geometry.attributes.position.needsUpdate = true;
}

function rotate() {
  // 平移[-start[0], -start[1], -start[2]]
  // 然后绕 [end[0]-start[0], end[1]-start[1], end[2]-start[2]]旋转angle角度
  // 平移[start[0], start[1], start[2]]
  // 注意threejs中的matrix4是右乘
  const iMatrix = cube.matrix.clone().invert()
  cube.applyMatrix4(iMatrix)

  const px = params.start.x
  const py = params.start.y
  const pz = params.start.z

  const ex = params.end.x
  const ey = params.end.y
  const ez = params.end.z
  // 注意这里要归一化
  const vecn = new THREE.Vector3(ex-px, ey-py, ez-pz).normalize()
  const nx = vecn.x
  const ny = vecn.y
  const nz = vecn.z

  // 平移
  const matrix1 = new THREE.Matrix4()
  matrix1.makeTranslation(-px, -py, -pz)

  // 旋转
  const matrix3 = new THREE.Matrix4()
  matrix3.makeRotationAxis(vecn, params.angle * Math.PI / 180)

  // 平移回来
  const matrix2 = new THREE.Matrix4()
  matrix2.makeTranslation(px, py, pz)

  // 右乘,所以顺序是反的
  const newMatrix = matrix2.clone().multiply(matrix3).multiply(matrix1)
  rotateMatrix = newMatrix

  cube.applyMatrix4(newMatrix)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178