【问题标题】:How to make the camera look at a specific child in THREE.js如何让相机看着 THREE.js 中的特定孩子
【发布时间】:2018-03-14 21:34:53
【问题描述】:

我将 THREE v0.86 与 React 一起使用,我试图获取父母的特定孩子的世界位置,所以当我点击一个按钮时,我可以更改它的材质颜色并使相机面向它。

首先,我在 onLoad 函数中为我感兴趣的每个孩子存储一个引用:

const onLoad = object => {

      object.name = "scene 1";
      object.position.set(0, 5, 0);
      obj3d.add(object);
      object.traverse(
        function(child) {
          if (condition === true) {
            let material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
            child.material = material;
            this[child.name] = child; // <-- Here
          }
        }.bind(this)
      );
    };

然后,我尝试了两种方法:第一种启用控件:TrackballControls

moveCamera(selectedChild) {

    this[selectedChild].material.color.setHex(0xff0000);
    const material = new THREE.MeshBasicMaterial({ color: "#FF0000" });
    this[selectedChild].material = material;

    const newPosition = new THREE.Vector3();
    newPosition.setFromMatrixPosition(this[selectedChild].matrixWorld);

// I've tried this:
    this.controls.target = this[selectedChild].getWorldPosition();

// OR this
    this.controls.target = newPosition;

    this.camera.lookAt(this[selectedChild]);
  }

另外,我已经禁用控件并尝试了这个:

moveCamera(selectedChild) {

    this[selectedChild].material.color.setHex(0xff0000);
    const material = new THREE.MeshBasicMaterial({ color: "#FF0000" });
    this[selectedChild].material = material;

    this.camera.lookAt(this[selectedChild].getWorldPosition());
  }

在 renderScene() 函数中使用这个:

renderScene() {
    this.renderer.autoClear = true;
    this.renderer.setClearColor(0xfff0f0);
    this.renderer.setClearAlpha(0.0);
    this.scene.updateMatrixWorld(); // <- THIS

    if (this.composer) {
      this.composer.render();
    } else {
      this.renderer.render(this.scene, this.camera);
    }

  }

moveCamera(childName) 确实改变了特定的子颜色,但问题是它总是看起来在同一个方向,所以我安慰记录了父对象并看了一眼孩子,他们都它们之间有相同的matrixmatrixAutoUpdate: truematrixWorldmatrixWorldNeedsUpdate: false 属性值,它们也是父级的值,所以当然newPosition.setFromMatrixPosition(this[selectedChild].matrixWorld); 向量总是相同的。我错过了什么?为什么不同的子对象相对于世界的位置不不同?

这是我的场景设置

componentDidMount() {
    const { THREE, innerWidth, innerHeight } = window;

    OBJLoader(THREE);
    MTLLoader(THREE);

    this.setScene();
    this.setCamera();
    this.setRenderer();

    this.group = new THREE.Group();
    this.selectedObjects = [];
    this.mouse = new THREE.Vector2();
    this.raycaster = new THREE.Raycaster();

    this.enableControls();

    this.start();
  }

地点:

this.setScene()

setScene() {
    const { THREE } = window;
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xcccccc);
    scene.fog = new THREE.FogExp2(0xcccccc, 0.002);

    // LIGHTS
    scene.add(new THREE.AmbientLight(0xaaaaaa, 0.2));
    const light = new THREE.DirectionalLight(0xddffdd, 0.6);
    light.position.set(1, 1, 1);
    light.castShadow = true;
    light.shadow.mapSize.width = 1024;
    light.shadow.mapSize.height = 1024;
    const d = 10;
    light.shadow.camera.left = -d;
    light.shadow.camera.right = d;
    light.shadow.camera.top = d;
    light.shadow.camera.bottom = -d;
    light.shadow.camera.far = 1000;
    scene.add(light);

    const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.5);
    hemiLight.color.setHSL(0.6, 1, 0.6);
    hemiLight.groundColor.setHSL(0.095, 1, 0.75);
    hemiLight.position.set(0, 500, 0);
    scene.add(hemiLight);

    // GROUND
    const groundGeo = new THREE.PlaneBufferGeometry(1000, 1000);
    const groundMat = new THREE.MeshPhongMaterial({
      color: 0xffffff,
      specular: 0x050505
    });
    groundMat.color.setHSL(0.095, 1, 0.75);

    const ground = new THREE.Mesh(groundGeo, groundMat);
    ground.rotation.x = -Math.PI / 2;
    ground.position.y = -0.5;
    scene.add(ground);

    // SKYDOME
    const vertexShader =
      "varying vec3 vWorldPosition; void main() { vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); vWorldPosition = worldPosition.xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }";
    const fragmentShader =
      "uniform vec3 topColor; uniform vec3 bottomColor; uniform float offset; uniform float exponent; varying vec3 vWorldPosition; void main() { float h = normalize( vWorldPosition + offset ).y; gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( max( h , 0.0), exponent ), 0.0 ) ), 1.0 ); }";
    const uniforms = {
      topColor: { value: new THREE.Color(0x0077ff) },
      bottomColor: { value: new THREE.Color(0xffffff) },
      offset: { value: 0 },
      exponent: { value: 0.6 }
    };
    uniforms.topColor.value.copy(hemiLight.color);

    scene.fog.color.copy(uniforms.bottomColor.value);

    const skyGeo = new THREE.SphereGeometry(300, 32, 15);
    const skyMat = new THREE.ShaderMaterial({
      vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      uniforms: uniforms,
      side: THREE.BackSide
    });

    const sky = new THREE.Mesh(skyGeo, skyMat);
    scene.add(sky);

    this.scene = scene;
  }

this.setCamera

setCamera() {
    const { THREE, innerWidth, innerHeight } = window;
    const camera = new THREE.PerspectiveCamera(
      45,
      innerWidth / innerHeight,
      0.1,
      100
    );
    camera.position.set(-4.1, 7.2, 4.2);
    this.camera = camera;
  }

this.setRenderer

setRenderer() {
    const { THREE, innerWidth, innerHeight } = window;
    const renderer = new THREE.WebGLRenderer({ antialias: false });
    renderer.setSize(innerWidth, innerHeight * this.windowMultiplier);
    this.renderer = renderer;
  }

this.start this.stop this.animate

start() {
    if (!this.frameId) {
      this.frameId = requestAnimationFrame(this.animate);
    }
  }

  stop() {
    cancelAnimationFrame(this.frameId);
  }

  animate() {
    this.renderScene();
    this.frameId = window.requestAnimationFrame(this.animate);
    if (!!this.controls) {
      this.controls.update();
    }
  }

【问题讨论】:

    标签: javascript reactjs three.js


    【解决方案1】:

    Object3D .lookAt 方法需要在世界空间中的位置:

    var position = new THREE.Vector3().copy( child.position );
    child.localToWorld( position );
    camera.lookAt( position );
    

    使用 TrackballControls 或 OrbitControls 会使这件事复杂化,我不确定 lookAt 是否能与其中任何一个相关人员正常工作。

    三个.js r89

    【讨论】:

    • 这和我写的一样,因为它使用一个孩子的矩阵作为位置(我猜matrixWorld),主要问题是所有孩子和父母都有相同的值对于他们的矩阵,我不知道为什么。另外,不,lookAt 无法与这些控件一起使用,这就是为什么我在使用它们时禁用它们的原因。
    • 你能创建一个 JSFiddle 来重现这个问题吗?这是 a live example 显示 .lookAt 与转换的父级,它确实按预期工作。
    • 问题是,.lookAt 工作得很好,它正在做它应该做的事情。看,如果我 console.log(parent.getWorldPosition())console.log(child.getWorldPosition()) 他们是一样的!并且发生在所有孩子身上,这就是为什么无论我传递给 moveCamera() 函数什么,它总是会看到同一个位置。
    • 问题是你的问题没有显示你的场景结构是什么,或者如何重现它。所以我无法猜测应该是什么父/子位置。另外 — 看起来您将 object 传递给 lookAt,而不是位置?
    • 哦,对不起,我忘了.getWorldPosition().lookAt 中的对象,添加它。我正在加载一个 Unity 制作的场景,然后我正在设置对某些孩子的引用,但是,无论如何,.getWorldPosition() 不应该总是不同吗?父对象作为对象在 3D 世界中占有一个位置,但子对象各占有一个位置,.getWorldPosition() 如何为所有对象返回相同的向量(父对象的初始位置)? (我会添加场景设置)
    【解决方案2】:

    我明白了!我的所有代码都运行良好,问题出在 .obj 本身,所有子节点的枢轴点都与父节点相同,它们不在对象的中心(人们会怎么想):

    【讨论】:

      猜你喜欢
      • 2012-11-19
      • 2016-11-30
      • 1970-01-01
      • 2018-08-06
      • 2012-05-14
      • 2017-04-21
      • 1970-01-01
      • 2021-08-11
      • 2023-02-25
      相关资源
      最近更新 更多