前言

最近在做自定义几何体(平面)示例的时候,发现加上贴图后并没有得到想要的结果,代码是这样的:

 //创建几何体
      var geometry=new THREE.Geometry();
      var vertices=[
              new THREE.Vector3(0,0,0),
              new THREE.Vector3(100,0,0),
              new THREE.Vector3(100,100,0),
              new THREE.Vector3(0,100,0),
      ];
      var faces=[
        new THREE.Face3(0, 1, 2),
        new THREE.Face3(0, 2, 3)
      ];
      geometry.vertices = vertices;
      geometry.faces = faces;
      geometry.computeFaceNormals();//计算法向量 这决定了对光做出的反应


      material = new THREE.MeshLambertMaterial({
        map:new THREE.TextureLoader().load("./img/pic1.jpg"),
        side: THREE.DoubleSide,
        // color: "red",
        opacity: 1,
        depthWrite: false,
        transparent: true // 定义此材质是否透明
      });
      
      mesh = new THREE.Mesh(geometry, material);

      scene.add(mesh);

理想的结果:
在这里插入图片描述
实际的结果:
在这里插入图片描述
然后想到了,自定义的geometry是不会自动设置UV坐标的,那如何给它设置合适的UV坐标呢。这需要对UV坐标有一定的了解。

UV坐标

可以将需要贴图的图片,给它设定一个坐标系作为图片的定位,该坐标系就是UV坐标了。在贴图时,可以根据图片的UV坐标,对应到Three.js的每个三角面进行贴图。
在这里插入图片描述
在Three.js中可以定义好uv坐标,与每个三角面一一对应。
代码如下:

//创建几何体
      var geometry=new THREE.Geometry();
      var vertices=[
              new THREE.Vector3(0,0,0),
              new THREE.Vector3(100,0,0),
              new THREE.Vector3(100,100,0),
              new THREE.Vector3(0,100,0),
      ];
      var faces=[
        new THREE.Face3(0, 1, 2),
        new THREE.Face3(0, 2, 3)
      ];
      geometry.vertices = vertices;
      geometry.faces = faces;
      geometry.computeFaceNormals();//计算法向量 这决定了对光做出的反应
      
      material = new THREE.MeshLambertMaterial({
        map:new THREE.TextureLoader().load("./img/pic1.jpg"),
        side: THREE.DoubleSide,
        // color: "red",
        opacity: 1,
        depthWrite: false,
        transparent: true // 定义此材质是否透明
      });

      //几何体UV坐标定义
      var t0 = new THREE.Vector2(0, 0); //图片左下角
      var t1 = new THREE.Vector2(1, 0); //图片右下角
      var t2 = new THREE.Vector2(1, 1); //图片右上角
      var t3 = new THREE.Vector2(0, 1); //图片左上角
      var uv1 = [t0, t1, t2]; //选中图片一个三角区域像素——用于映射到一个三角面
      var uv2 = [t0, t2, t3]; //选中图片一个三角区域像素——用于映射到一个三角面
      geometry.faceVertexUvs[0][0] = uv1;
      geometry.faceVertexUvs[0][1] = uv2;

      mesh = new THREE.Mesh(geometry, material);

      scene.add(mesh);

效果:
在这里插入图片描述
如果想要重复多次贴图或者只贴一部分,只需要设计好贴图的UV坐标既可以了,如下修改UV部分代码:

//几何体UV坐标定义
	var t0 = new THREE.Vector2(0, 0); //图片左下角
      var t1 = new THREE.Vector2(0.5, 0); //图片右下角
      var t2 = new THREE.Vector2(0.5, 0.5); //图片右上角
      var t3 = new THREE.Vector2(0, 0.5); //图片左上角
      var uv1 = [t0, t1, t2]; //选中图片一个三角区域像素——用于映射到一个三角面
      var uv2 = [t0, t2, t3]; //选中图片一个三角区域像素——用于映射到一个三角面
      geometry.faceVertexUvs[0][0] = uv1;
      geometry.faceVertexUvs[0][1] = uv2;

效果:
在这里插入图片描述
实际上我们的几何体是有非常多的三角面组合而成的,为每个三角面设置好合适的uv坐标就可以实现多种多样的个性化贴图了。

完整示例代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>贴图与uv坐标</title>
  <style>
    body {
      font-family: Monospace;
      margin: 0px;
      overflow: hidden;
    }
  </style>
</head>

<body>
  <script type="text/javascript" src="../node_modules/three/build/three.js"></script>

  <script type="text/javascript" src="../node_modules/three/examples/js/controls/OrbitControls.js"></script>
  <script>
    let camera, light, scene, renderer, mesh, material, raycaster, group, texture;

    //初始化相机
    function initCamera() {
      camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
      camera.position.set(400, 400, 400);
    }

    //初始化灯光
    function initLight() {
      light = new THREE.AmbientLight(0xffffff);
    }

    //初始化场景
    function initScene() {
      scene = new THREE.Scene();
      //坐标辅助线
      let helper = new THREE.AxisHelper(1000);
      //网格辅助线
      let gridHelper = new THREE.GridHelper(1000, 50);

      scene.add(helper);
      scene.add(light);
      scene.add(gridHelper);
    }

    //初始化物体
    function initMesh() {

      //创建几何体
      var geometry=new THREE.Geometry();
      var vertices=[
              new THREE.Vector3(0,0,0),
              new THREE.Vector3(100,0,0),
              new THREE.Vector3(100,100,0),
              new THREE.Vector3(0,100,0),
      ];
      var faces=[
        new THREE.Face3(0, 1, 2),
        new THREE.Face3(0, 2, 3)
      ];
      geometry.vertices = vertices;
      geometry.faces = faces;
      geometry.computeFaceNormals();//计算法向量 这决定了对光做出的反应


      material = new THREE.MeshLambertMaterial({
        map:new THREE.TextureLoader().load("./img/pic1.jpg"),
        side: THREE.DoubleSide,
        // color: "red",
        opacity: 1,
        depthWrite: false,
        transparent: true // 定义此材质是否透明
      });

      //几何体UV坐标定义
      var t0 = new THREE.Vector2(0, 0); //图片左下角
      var t1 = new THREE.Vector2(0.5, 0); //图片右下角
      var t2 = new THREE.Vector2(0.5, 0.5); //图片右上角
      var t3 = new THREE.Vector2(0, 0.5); //图片左上角
      var uv1 = [t0, t1, t2]; //选中图片一个三角区域像素——用于映射到一个三角面
      var uv2 = [t0, t2, t3]; //选中图片一个三角区域像素——用于映射到一个三角面
      geometry.faceVertexUvs[0][0] = uv1;
      geometry.faceVertexUvs[0][1] = uv2;

      mesh = new THREE.Mesh(geometry, material);

      scene.add(mesh);
    }

  //初始化
    function initRender() {
      renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setClearColor(0xFFFFFF, 1.0);
      document.body.appendChild(renderer.domElement);

      //添加鼠标控制
      let controls = new THREE.OrbitControls(camera, renderer.domElement);//创建控件对象
      // controls.addEventListener('change', animate);//监听鼠标、键盘事件

      window.addEventListener('resize', onWindowResize, false);
    }

    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }


    function animate() {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
      //围绕Y轴旋转
      //group.rotateY(0.001 * Math.PI);
      if (texture) {
        // console.log("进来了");
        texture.offset.y += 0.01;
      }
    }

    function init() {
      initCamera();
      initLight();
      initScene()
      initMesh();
      initRender()
      animate();
    }

    init();

  </script>
</body>

</html>

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐