首页 > 解决方案 > Three.js 从 3 个坐标创建人脸

问题描述

Vue.js我有代码可以使用and来创建加载、渲染和显示 STL 对象Three.js。我想渲染一张新面孔来代替我目前正在寻找的飞机。我已经找到了一种方法来获取鼠标悬停(单击)面(、、、)的 3aVertexbVertex顶点cVertex

现在我想在这个位置渲染一个三角形(用不同的颜色),但老实说我不知道​​怎么做。我试过用谷歌搜索它,但还没有运气(我对 3d 和一般渲染非常不熟悉)。有人可以推动我朝着正确的方向前进吗?

<template>
  <div id="scene-container" ref="sceneContainer" class="scene-container"></div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { STLLoader } from "three/examples/jsm/loaders/STLLoader";

export default {
  name: "HelloWorld",
  data() {
    return {
      container: null,
      scene: null,
      camera: null,
      controls: null,
      renderer: null,
      stats: null,
      mouse: null,
      raycaster: null,
      objName: "testobj",
    };
  },
  methods: {
    init() {
      // set container
      this.container = this.$refs.sceneContainer;

      // add raycaster
      this.raycaster = new THREE.Raycaster();
      this.mouse = new THREE.Vector2();
      const onMouseClick = (event) => {
        event.preventDefault();
        // calculate mouse position in normalized device coordinates
        // (-1 to +1) for both components
        this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        // update the picking ray with the camera and mouse position
      };

      window.addEventListener("mousedown", onMouseClick, false);

      // add camera
      const fov = 60; // Field of view
      const aspect = this.container.clientWidth / this.container.clientHeight;
      // const near = 0.1; // the near clipping plane
      // const far = 3000; // the far clipping plane
      const camera = new THREE.PerspectiveCamera(fov, aspect);
      camera.position.set(0, 20, 75);

      this.camera = camera;

      // create scene
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0xdddddd);

      // add lights
      const ambientLight = new THREE.HemisphereLight(
        0xffffff, // bright sky color
        0x222222, // dim ground color
        1 // intensity
      );
      const mainLight = new THREE.DirectionalLight(0xffffff, 4.0);
      mainLight.position.set(10, 10, 10);
      this.scene.add(ambientLight, mainLight);

      let hlight = new THREE.AmbientLight(0xffffff, 1.3);
      this.scene.add(hlight);

      //Add some point lights to simulate real lights
      let light = new THREE.PointLight(0xffffff, 1, 1000);
      light.position.set(0, 300, 500);
      this.scene.add(light);

      // add controls
      this.controls = new OrbitControls(this.camera, this.container);

      // create renderer
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setSize(
        this.container.clientWidth,
        this.container.clientHeight
      );
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.gammaFactor = 2.2;
      this.renderer.outputEncoding = THREE.sRGBEncoding;
      this.renderer.physicallyCorrectLights = true;
      document
        .getElementById("scene-container")
        .appendChild(this.renderer.domElement);

      // set aspect ratio to match the new browser window aspect ratio
      this.camera.aspect =
        this.container.clientWidth / this.container.clientHeight;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(
        this.container.clientWidth,
        this.container.clientHeight
      );
      let me = this;
      let loader = new STLLoader();
      let mesh = new THREE.Object3D();
      loader.load("/three-assets/RobotExpressive.stl", function (geometry) {
        // console.log(geometry);
        let material = new THREE.MeshLambertMaterial({
          color: 0x1313,
          wireframe: false,
          transparent: false,
          vertexColors: false,
        });
        mesh = new THREE.Mesh(geometry, material);
        mesh.rotation.x = -0.5 * Math.PI;
        mesh.position.set(0, 0, 0);
        mesh.name = me.objName;
        me.scene.add(mesh);
      });

      window.addEventListener("resize", onWindowResize, false);

      function onWindowResize() {
        me.camera.aspect = window.innerWidth / window.innerHeight;
        me.camera.updateProjectionMatrix();

        me.renderer.setSize(window.innerWidth, window.innerHeight);
      }

      this.renderer.setAnimationLoop(() => {
        this.render();
      });
    },
    render() {
      this.raycaster.setFromCamera(this.mouse, this.camera);
      this.intersects = this.raycaster.intersectObjects(this.scene.children);

      // window.addEventListener( 'mousemove', onMouseMove, false );
      if (this.intersects.length > 1) {
        // this.intersects[0].object.material.color.set(0xff);
        for (let i = 0; i < this.intersects.length; i++) {
          if (this.intersects[i].object.name == "testobj") {
            let positionAttribute = this.intersects[i].object.geometry
              .attributes["position"];
            let intersection = this.intersects[i];
            let aVertex = new THREE.Vector3(
              positionAttribute.getX(intersection.face.a),
              positionAttribute.getY(intersection.face.a),
              positionAttribute.getZ(intersection.face.a)
            );
            let bVertex = new THREE.Vector3(
              positionAttribute.getX(intersection.face.b),
              positionAttribute.getY(intersection.face.b),
              positionAttribute.getZ(intersection.face.b)
            );
            let cVertex = new THREE.Vector3(
              positionAttribute.getX(intersection.face.c),
              positionAttribute.getY(intersection.face.c),
              positionAttribute.getZ(intersection.face.c)
            );

            console.log(aVertex, bVertex, cVertex);
          }
        }
      }
      this.renderer.render(this.scene, this.camera);
    },
  },
  mounted() {
    this.init();
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
#scene-container {
  height: 99.8%;
}
</style>

标签: javascriptvue.jsthree.js

解决方案


示例(用于使用 BufferGeometry 拾取网格):

// initital setup:
let lineGeometry = new THREE.BufferGeometry();
let linePositionAttribute = new THREE.BufferAttribute(new Float32Array(4 * 3), 3);
lineGeometry.addAttribute('position', linePositionAttribute);
let lineMaterial = new THREE.LineBasicMaterial(
{
    color: 0xff0000
});
var intersectionFaceEdge = new THREE.Line(lineGeometry, lineMaterial);      
scene.add(intersectionFaceEdge);    
        
// ... on each raycasting:

let face = intersection.face;
let obj = intersection.object;  

let positionAttribute = obj.geometry.attributes['position'];
        
linePositionAttribute.copyAt(0, positionAttribute, face.a);
linePositionAttribute.copyAt(1, positionAttribute, face.b);
linePositionAttribute.copyAt(2, positionAttribute, face.c);
linePositionAttribute.copyAt(3, positionAttribute, face.a); 

lineGeometry.applyMatrix(obj.matrixWorld);

我建议使用 GPU 拾取而不是简单的光线投射


推荐阅读