Three.js对模型进行多区域染色

Three.js对模型进行多区域染色

Three.js对模型进行多区域染色

在项目中遇到一个对地形图进行多区域染色的需求,即在地形图上选中一个区域,改变该区域的颜色。以下测试代码为学习痕迹。

测试程序:单机选取点,四个点自动停止选点。双击对区域染色。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>第一个three.js文件_WebGL三维场景</title>
    <style>
      body {
        margin: 0;
        overflow: hidden;
        /* 隐藏body窗口区域滚动条 */
      }
    </style>
    <!--引入three.js三维引擎-->
    <!-- <script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script> -->
    <script src="../../three.js-master/build/three.js"></script>
    <script src="../../three.js-master/examples/js/controls/OrbitControls.js"></script>
    <!-- <script src="http://www.yanhuangxueyuan.com/threejs/build/three.js"></script> -->
  </head>

  <body>
    <script>
      /**
       * 创建场景对象Scene
       */
      var scene = new THREE.Scene();
      var posArr = [];
      var geometry = new THREE.PlaneGeometry(100, 100, 50, 50);
      const arr1 = new Array(geometry.attributes.position.count);
      const arr = arr1.fill(1);
      arr.forEach((a, index) => {
        if (index % 3 === 0) {
          geometry.attributes.position.setZ(index, 5 * -1);
        } else if (index % 23 === 0) {
          geometry.attributes.position.setZ(index, 10 * -1);
        }
      });
      const vertexShader = `
precision highp float;
varying vec3 fPosition;

void main()
{
  vec4 pos = modelViewMatrix * vec4(position, 1.0);
  gl_Position = projectionMatrix * pos;
  fPosition = (modelMatrix * vec4(position, 1.0)).xyz;
}
`;

      const fragmentShader = `
precision highp float;
uniform float time;
varying vec3 fPosition;
uniform int areaNum;
struct Area {
  vec3 posList[16];
  int countNum;
  vec4 color;
};
uniform Area area[32];

bool pointInPolygon(vec3 p, vec3 points[16], int pCount){
  if (pCount < 3 || pCount > 16) {
    return false;
  }
  bool inside = false;
  for (int i = 0; i < pCount; i++) {
    float xi = points[i].x;
    float yi = points[i].y;
    float xj;
    float yj;
    if (i == 0) {
      xj = points[pCount - 1].x;
      yj = points[pCount - 1].y;
    } else {
      xj = points[i - 1].x;
      yj = points[i - 1].y; 
    }
      bool intersect = ((yi > p.z) != (yj > p.z)) && (p.x < (xj - xi) * (p.z - yi) / (yj - yi) + xi);
      if (intersect) {
        inside = !inside;
      }
    }
  return inside;
}

void d_color() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

void main(){
  if (areaNum > 0) {
    bool f = false;
    for (int i = 0; i < areaNum; i++) {
      int countNum = area[i].countNum;
      vec3 posList[16] = area[i].posList;
      vec4 color = area[i].color;
      bool isInside=pointInPolygon(fPosition, posList, countNum);
      if (isInside) {
        gl_FragColor = color;
        f = true;
      }
      if (!f) {
        d_color();
      }
    }
  } else {
    d_color();
  }
}`;
      let posArr1 = new Array(16);
      posArr1.fill(new THREE.Vector3(0.0, 0.0, 0.0));
      let area = new Array(32);
      area.fill({
        posList: posArr1,
        countNum: 0,
        color: new THREE.Vector4(0.2, 0.4, 0.3, 1.0),
      });

      let uniforms = {
        areaNum: {
          value: 0,
        }, // 染色区域的个数
        area: { value: area }, // 染色区域
      };

      var material = new THREE.ShaderMaterial({
        // wireframe: true,
        side: THREE.DoubleSide,
        uniforms: uniforms,
        vertexShader: vertexShader,
        fragmentShader: fragmentShader,
      });

      var mesh = new THREE.Mesh(geometry, material);
      mesh.rotation.x = Math.PI / 2;
      scene.add(mesh);

      // 辅助坐标系  参数250表示坐标系大小,可以根据场景大小去设置
      var axisHelper = new THREE.AxesHelper(250);
      scene.add(axisHelper);
      // console.log(scene)
      // console.log(scene.children)
      /**
       * 相机设置
       */
      var width = window.innerWidth; //窗口宽度
      var height = window.innerHeight; //窗口高度
      var k = width / height; //窗口宽高比
      var s = 200; //三维场景显示范围控制系数,系数越大,显示的范围越大
      //创建相机对象
      var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
      camera.position.set(200, 300, 200); //设置相机位置
      camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
      /**
       * 创建渲染器对象
       */
      var renderer = new THREE.WebGLRenderer();
      renderer.setSize(width, height); //设置渲染区域尺寸
      renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
      document.body.appendChild(renderer.domElement); //body元素中插入canvas对象
      //执行渲染操作   指定场景、相机作为参数
      // renderer.render(scene, camera);
      function render() {
        renderer.render(scene, camera); //执行渲染操作
        // mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
        requestAnimationFrame(render); //请求再次执行渲染函数render
      }
      render();
      var controls = new THREE.OrbitControls(camera, renderer.domElement); //创建控件对象

      // 单击事件,选取点
      renderer.domElement.addEventListener("click", (event) =>
        _onMouseClick(event)
      );
      // 双击事件,对选中范围进行渲染
      renderer.domElement.addEventListener("dblclick", (event) =>
        _onMouseDbClick(event)
      );
      function _onMouseClick(event) {
        _updateRaycaster(event);
      }
      function _onMouseDbClick(event) {
        var posVecArr = new Array(16);
        posVecArr.fill(new THREE.Vector3(0.0, 0.0, 0.0));
        for (let i = 0; i < 4; i++) {
          // x->x y->z z->y
          posVecArr[i] = new THREE.Vector3(
            posArr[i].x,
            posArr[i].z,
            posArr[i].y
          );
        }
        setupShaderUniforms(
          4,
          posVecArr,
          new THREE.Vector4(0.8, 0.8, 0.5, 1.0)
        );
      }

      function _updateRaycaster(event) {
        //更新拾取器
        raycaster = new THREE.Raycaster();
        const rect = renderer.domElement.getBoundingClientRect();
        let mouse = new THREE.Vector2();
        mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
        mouse.y = (-(event.clientY - rect.top) / rect.height) * 2 + 1;
        raycaster.setFromCamera(mouse, camera);
        //确定所点击位置上的物体数量
        var intersects = raycaster.intersectObjects(scene.children);

        const material = new THREE.MeshBasicMaterial({
          color: "#FF00FF",
          transparent: true,
          opacity: 0.5,
        });

        //选中后进行的操作
        if (intersects.length) {
          var selected = intersects[0]; //取第一个物体

          let pos = {
            x: selected.point.x,
            y: selected.point.y,
            z: selected.point.z,
          };
          console.log(this.posArr);
          posLength = this.posArr.length;
          if (posLength < 4) {
            this.posArr.push(pos);
            _point(pos);
            posLength = this.posArr.length;
            if (posLength > 1) {
              _line(this.posArr[posLength - 1], this.posArr[posLength - 2]);
              if (posLength === 4) {
                console.log("点够了");
                _line(this.posArr[0], this.posArr[posLength - 1]);
              }
            }
          }
        }
      }

      // 更新shader的uniform
      function setupShaderUniforms(posCount, posVecArr, color) {
        let num = this.mesh.material.uniforms.areaNum.value;
        let area = this.mesh.material.uniforms.area.value;

        for (let i = 0; i < area.length; i++) {
          if (area[i].countNum === 0) {
            area[i] = {
              posList: posVecArr,
              countNum: posCount,
              color: color,
            };
            break;
          }
        }

        this.mesh.material.uniforms.areaNum.value = num + 1;
        this.mesh.material.uniforms.area.value = area;

        this.posArr = [];
      }

      function _point(pos) {
        var geometry = new THREE.BufferGeometry();
        var vertices = new Float32Array([pos.x, pos.y, pos.z]);
        geometry.setAttribute(
          "position",
          new THREE.Float32BufferAttribute(vertices, 3)
        );

        var material = new THREE.PointsMaterial({ color: 0xff0000, size: 20 });
        var point = new THREE.Points(geometry, material);
        this.scene.add(point);
      }

      function _line(pos1, pos2) {
        var geometry = new THREE.BufferGeometry();
        var vertices = new Float32Array([
          pos1.x,
          pos1.y,
          pos1.z,
          pos2.x,
          pos2.y,
          pos2.z,
        ]);

        // 创建属性缓冲区对象
        var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,表示一个顶点的xyz坐标
        // 设置几何体attributes属性的位置属性
        geometry.attributes.position = attribue;

        var material = new THREE.LineBasicMaterial({
          color: 0xff0000,
        });

        var line = new THREE.Line(geometry, material);
        this.scene.add(line);
      }
    </script>
  </body>
</html>

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
three.js是一个功能强大的JavaScript库,用于创建和渲染3D图形。它提供了各种方法可以对模型进行图片贴图,从而为模型增加纹理。 首先,我们需要为模型加载一个纹理图片。有两种常用的方法来加载纹理图片。一种是使用`THREE.TextureLoader`,它可以从指定的URL加载纹理图片。例如,可以使用以下代码加载一张名为`texture.png`的纹理图片: ```javascript var loader = new THREE.TextureLoader(); loader.load('texture.png', function (texture) { // 在此处使用纹理 }); ``` 另一种方法是直接创建一个纹理对象。我们可以先将图片加载为`Image`对象,然后将其传递给`THREE.Texture`的构造函数。例如,可以使用以下代码创建一个纹理对象: ```javascript var image = new Image(); image.src = 'texture.png'; var texture = new THREE.Texture(image); ``` 无论使用哪种方法,我们都可以将纹理应用于模型的材质。对于模型的每个面,我们可以将纹理设置为材质的`map`属性。例如,可以使用以下代码为一个方块模型贴上纹理: ```javascript var geometry = new THREE.BoxGeometry(); // 创建一个基本的材质,并将纹理应用于材质的map属性 var material = new THREE.MeshBasicMaterial({ map: texture }); var cube = new THREE.Mesh(geometry, material); ``` 当我们将纹理应用于模型后,模型的表面将使用纹理图片进行渲染。我们可以根据需要对纹理进行更多的调整,如旋转、重复等。可以通过设置纹理的`rotation`和`repeat`属性来实现。例如,可以使用以下代码旋转纹理: ```javascript texture.rotation = Math.PI / 4; ``` 总之,通过使用`THREE.TextureLoader`或直接创建纹理对象,我们可以将纹理图片应用于three.js模型。然后,我们可以根据需要对纹理进行进一步的调整,以实现更加逼真和个性化的渲染效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值