You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js OrbitControls设置目标后模型位移问题求助

问题描述

场景中有两个立方体:

const material1 = new THREE.MeshBasicMaterial({color: '#00ff00'});
const cube1 = new THREE.Mesh(geometry, material1);
cube1.position.set(0, 0, 0);

const material2 = new THREE.MeshBasicMaterial({color: '#ffff00'});
const cube2 = new THREE.Mesh(geometry, material1);
cube1.position.set(-3, 0, 0);

Cube1(绿色)位于原点(屏幕中心位置),Cube2(黄色)沿X轴偏移-3。按住鼠标左键拖动可看到Cube2(黄色)绕Cube1(绿色)旋转,这是OrbitControls的默认效果。

需求是实现Cube1(绿色)绕Cube2(黄色)旋转,使用语句control.target = cube2.position;虽达成旋转效果,但Cube2(黄色)被拉回原点,不符合预期,求解决方法。

完整代码如下:

<template>
  <div ref="container" style="width: 100%; height: 100%"></div>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";

import * as THREE from "three";
// 轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

const container = ref(null);

// 场景   相机  渲染  轨道控制器   模型
let scene, camera, renderer, control, cube1, cube2;
let axesHelper1, axesHelper2, gridHelper;

onMounted(() => {
  init();
  animate();

  // 监听窗口变化
  window.addEventListener("resize", () => {
    // 重置渲染器宽高比
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 重置相机宽高比
    camera.aspect = window.innerWidth / window.innerHeight;
    // 更新相机投影矩阵
    camera.updateProjectionMatrix();
  });
});

// 场景
function initScene() {
  scene = new THREE.Scene();
}
// 相机
function initCamera() {
  camera = new THREE.PerspectiveCamera(
    30,
    window.innerWidth / window.innerHeight,
    1,
    5000,
  );
  camera.position.z = 20;
  scene.add(camera);
}
// 轨道控制器
function initControl() {
  control = new OrbitControls(camera, renderer.domElement);
  // 设置阻尼
  control.enableDamping = true;
  control.dampingFactor = 0.05;
}
// 渲染
function initRenderer() {
  // 创建渲染器 antialias 抗锯齿
  renderer = new THREE.WebGLRenderer({ antialias: true });
  // 尺寸
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 设置像素比
  renderer.setPixelRatio(window.devicePixelRatio);
  container.value.appendChild(renderer.domElement);
}

// 模型
function initModel() {
  const geometry = new THREE.BoxGeometry(1, 1, 1);

  // region 原点
  const material1 = new THREE.MeshBasicMaterial({ color: "#00ff00" });
  cube1 = new THREE.Mesh(geometry, material1);
  cube1.position.set(0, 0, 0);
  axesHelper1 = new THREE.AxesHelper(2);
  cube1.add(axesHelper1);
  scene.add(cube1);
  // endregion 原点

  // region 模型2
  const material2 = new THREE.MeshBasicMaterial({ color: "#ffff00" });
  cube2 = new THREE.Mesh(geometry, material2);
  cube2.position.set(-5, 0, 0);
  axesHelper2 = new THREE.AxesHelper(2);
  cube2.add(axesHelper2);
  scene.add(cube2);
  // endregion 模型2

  // 网格辅助器
  gridHelper = new THREE.GridHelper(10, 10);
  scene.add(gridHelper);
}

function init() {
  // 场景
  initScene();
  // 相机
  initCamera();
  // 渲染器
  initRenderer();
  // 轨道控制器
  initControl();
  // 模型
  initModel();
}
function animate() {
  requestAnimationFrame(() => {
    animate();
  });

  control.update();

  // 重新渲染
  renderer.render(scene, camera);
}
</script>
<style scoped></style>
解决方案

问题出在直接将control.target赋值为cube2.position——OrbitControls的target属性是Vector3对象的引用,当你直接赋值cube2.position后,控制器会修改这个向量来控制相机环绕,而cube2.position是立方体本身的位置属性,这就导致立方体被强制拉到控制器的目标点。

方法1:通过相机环绕实现视觉上的Cube1绕Cube2旋转

创建独立的Vector3对象复制cube2的位置作为控制器目标,避免影响立方体本身的位置:

  1. 调整init函数的执行顺序,先初始化模型再初始化控制器,确保cube2已设置位置:
function init() {
  initScene();
  initCamera();
  initRenderer();
  // 先初始化模型,再初始化控制器
  initModel();
  initControl();
}
  1. 在initControl中设置独立的目标向量:
function initControl() {
  control = new OrbitControls(camera, renderer.domElement);
  control.enableDamping = true;
  control.dampingFactor = 0.05;
  // 复制cube2的位置作为控制器目标
  control.target.copy(cube2.position);
}

此时相机将环绕cube2的位置旋转,从视觉上呈现Cube1绕Cube2旋转的效果,同时Cube2会保持初始位置。

方法2:让Cube1主动绕Cube2旋转

如果需要Cube1真正主动绕Cube2做圆周运动,可在animate函数中添加旋转逻辑:

function animate() {
  requestAnimationFrame(animate);

  // 让cube1以cube2为中心做圆周运动
  const angle = Date.now() * 0.001;
  const radius = 5; // 旋转半径
  cube1.position.x = cube2.position.x + Math.cos(angle) * radius;
  cube1.position.z = cube2.position.z + Math.sin(angle) * radius;

  control.update();
  renderer.render(scene, camera);
}

这种方式下,Cube1会持续绕Cube2旋转,配合OrbitControls可从任意角度观察运动过程。


内容的提问来源于stack exchange,提问作者nieyuan1980

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 18:46:21