Three.js。从 BufferGeometry 顶点获取 localToWorld

Three.js. Get localToWorld from BufferGeometry vertices

  1. 我从几何属性中获取人脸
  2. 获取面部每个点的坐标
  3. 为脸部的每条边画线

如果对象没有移动,一切都很好: without moving

但是如果你移动,那么线条会保持原样: after moving

如果我尝试在移动后获取点的世界坐标,那么我会看到相同的图片: after moving + localToWorld

如果我更新立方体的矩阵世界,那么我会看到: after moving + localToWorld + updateMatrixWorld

var scene = new THREE.Scene();
scene.background = new THREE.Color().setStyle('#e0e0e0');
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.set(0, 500, 3000);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

///////////////////////CREATE CUBE////////////////////////////////

const cube = new THREE.Mesh(
  new THREE.BoxBufferGeometry(1000, 1000, 1000),
  new THREE.MeshBasicMaterial({
    color: 'red',
    transparent: true,
    opacity: 0.4
  })
);
scene.add(cube);
//MOVE
cube.position.set(100, 100, 100);
//UPDATE
cube.updateMatrixWorld(true);

//GET EDGE LINES(THREE.Line3) FOR EDGES OF FACES 
const lines = getLinesFromFaces(cube);

//CONVERT FROM LOCAL TO WORLD
 lines.map(l => {
        //l.applyMatrix4(cube.matrixWorld);
        //l.start.applyMatrix4(cube.matrixWorld);
    //l.end.applyMatrix4(cube.matrixWorld);
    cube.localToWorld(l.start);
    cube.localToWorld(l.end);
  });

//DRAW
drawLines(lines);

function drawLines(lines) {
  for (let i = 0; i < lines.length; i += 1) {
    addLine(lines[i].start, lines[i].end);
  }
}

function addLine(p1, p2) {
  const material = new THREE.LineBasicMaterial({
    color: 0x0000ff
  });
  const points = [p1, p2];
  const geometry = new THREE.BufferGeometry().setFromPoints(points);

  const line = new THREE.Line(geometry, material);
  scene.add(line);
}

function getLinesFromFaces(object) {
  const facesWithPos = getFacesWithPos(object.geometry);
  const lines = [];
  for (let i = 0; i < facesWithPos.length; i += 1) {
    const f = facesWithPos[i];

    const lineAB = new THREE.Line3(f.a, f.b);
    let isExist = false;
    isExist = lines.some(l => {
      return (l.start.equals(lineAB.start) && l.end.equals(lineAB.end)) ||
        (l.start.equals(lineAB.end) && l.end.equals(lineAB.start));
    });
    if (!isExist) lines.push(lineAB);

    const lineBC = new THREE.Line3(f.b, f.c);
    isExist = false;
    isExist = lines.some(l => {
      return (l.start.equals(lineBC.start) && l.end.equals(lineBC.end)) ||
        (l.start.equals(lineBC.end) && l.end.equals(lineBC.start));
    });
    if (!isExist) lines.push(lineBC);

    const lineCA = new THREE.Line3(f.c, f.a);
    isExist = false;
    isExist = lines.some(l => {
      return (l.start.equals(lineCA.start) && l.end.equals(lineCA.end)) ||
        (l.start.equals(lineCA.end) && l.end.equals(lineCA.start));
    });
    if (!isExist) lines.push(lineCA);
  }

  return lines;
}

function getFacesWithPos(geometry) {
  const faces = getFaces(geometry);
  const facesWithPos = [];
  const position = geometry.getAttribute('position');
  for (let i = 0; i < faces.length; i += 1) {
    const f = faces[i];

    facesWithPos.push({
      a: new THREE.Vector3(position.array[f.a * 3], position.array[f.a * 3 + 1], position.array[f.a * 3 + 2]),
      b: new THREE.Vector3(position.array[f.b * 3], position.array[f.b * 3 + 1], position.array[f.b * 3 + 2]),
      c: new THREE.Vector3(position.array[f.c * 3], position.array[f.c * 3 + 1], position.array[f.c * 3 + 2])
    });
  }
  return facesWithPos;
}

function getFaces(geometry) {
  const faces = [];
  const index = geometry.getIndex();
  for (let i = 0; i < index.count; i += 3) {
    faces.push({
      a: index.getX(i),
      b: index.getX(i + 1),
      c: index.getX(i + 2)
    });
  }
  return faces;
}


render();

function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.117.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.117.0/examples/js/controls/OrbitControls.js"></script>

如何获取世界坐标?我做错了什么?

一些线点是常见的对象。如果您将 localToWorld 应用于它们,那么该方法将多次应用于它们并且结果不正确。下面是解决方案

var scene = new THREE.Scene();
scene.background = new THREE.Color().setStyle('#e0e0e0');
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.set(0, 500, 3000);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

///////////////////////CREATE CUBE////////////////////////////////

const cube = new THREE.Mesh(
  new THREE.BoxBufferGeometry(1000, 1000, 1000),
  new THREE.MeshBasicMaterial({
    color: 'red',
    transparent: true,
    opacity: 0.4
  })
);
scene.add(cube);
//MOVE
cube.position.set(100, 100, 100);
//UPDATE
cube.updateMatrixWorld(true);

//GET EDGE LINES(THREE.Line3) FOR EDGES OF FACES 
const lines = getLinesFromFaces(cube);

//CONVERT FROM LOCAL TO WORLD
 lines.map(l => {
        //l.applyMatrix4(cube.matrixWorld);
        //l.start.applyMatrix4(cube.matrixWorld);
    //l.end.applyMatrix4(cube.matrixWorld);
    cube.localToWorld(l.start);
    cube.localToWorld(l.end);
  });

//DRAW
drawLines(lines);

function drawLines(lines) {
  for (let i = 0; i < lines.length; i += 1) {
    addLine(lines[i].start, lines[i].end);
  }
}

function addLine(p1, p2) {
  const material = new THREE.LineBasicMaterial({
    color: 0x0000ff
  });
  const points = [p1, p2];
  const geometry = new THREE.BufferGeometry().setFromPoints(points);

  const line = new THREE.Line(geometry, material);
  scene.add(line);
}

function getLinesFromFaces(object) {
  const facesWithPos = getFacesWithPos(object.geometry);
  const lines = [];
  for (let i = 0; i < facesWithPos.length; i += 1) {
    const f = facesWithPos[i];

    const lineAB = new THREE.Line3(f.a, f.b);
    let isExist = false;
    isExist = lines.some(l => {
      return (l.start.equals(lineAB.start) && l.end.equals(lineAB.end)) ||
        (l.start.equals(lineAB.end) && l.end.equals(lineAB.start));
    });
    if (!isExist) lines.push(lineAB.clone());

    const lineBC = new THREE.Line3(f.b, f.c);
    isExist = false;
    isExist = lines.some(l => {
      return (l.start.equals(lineBC.start) && l.end.equals(lineBC.end)) ||
        (l.start.equals(lineBC.end) && l.end.equals(lineBC.start));
    });
    if (!isExist) lines.push(lineBC.clone());

    const lineCA = new THREE.Line3(f.c, f.a);
    isExist = false;
    isExist = lines.some(l => {
      return (l.start.equals(lineCA.start) && l.end.equals(lineCA.end)) ||
        (l.start.equals(lineCA.end) && l.end.equals(lineCA.start));
    });
    if (!isExist) lines.push(lineCA.clone());
  }

  return lines;
}

function getFacesWithPos(geometry) {
  const faces = getFaces(geometry);
  const facesWithPos = [];
  const position = geometry.getAttribute('position');
  for (let i = 0; i < faces.length; i += 1) {
    const f = faces[i];

    facesWithPos.push({
      a: new THREE.Vector3(position.array[f.a * 3], position.array[f.a * 3 + 1], position.array[f.a * 3 + 2]),
      b: new THREE.Vector3(position.array[f.b * 3], position.array[f.b * 3 + 1], position.array[f.b * 3 + 2]),
      c: new THREE.Vector3(position.array[f.c * 3], position.array[f.c * 3 + 1], position.array[f.c * 3 + 2])
    });
  }
  return facesWithPos;
}

function getFaces(geometry) {
  const faces = [];
  const index = geometry.getIndex();
  for (let i = 0; i < index.count; i += 3) {
    faces.push({
      a: index.getX(i),
      b: index.getX(i + 1),
      c: index.getX(i + 2)
    });
  }
  return faces;
}


render();

function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.117.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.117.0/examples/js/controls/OrbitControls.js"></script>