threejs基于视觉空间的选中效果

效果如下所示

请添加图片描述

说明

仿照 光辉城市 软件中材质修改面板选中模型的效果,基于视觉空间,不受模型本身大小旋转的影。代码包含两个函数(一个选中替换shader,一个取消选中还原shader)

代码如下

  /**
   * 取消选中mesh
   */
  cancleSelect() {
    if (this.selectMat) {
      //   去掉材质的选中效果(阵列黑点)
      this.selectMat.onBeforeCompile = (shader) => {
        // 去掉自定义uniforms
        if (shader.uniforms.uResolution) delete shader.uniforms.uResolution;
        if (shader.uniforms.uSpacing) delete shader.uniforms.uSpacing;
        if (shader.uniforms.uRadius) delete shader.uniforms.uRadius;

        // 在着色器中去掉uniform声明
        shader.fragmentShader = shader.fragmentShader.replace(
          this.argunmentChunk,
          "#include <common>"
        );

        // 替换片元着色器中的dithering部分,去掉黑点逻辑
        shader.fragmentShader = shader.fragmentShader.replace(
          this.fragmentChunk,
          "#include <dithering_fragment>"
        );
      };
      this.selectMat.needsUpdate = true;
    }
    this.selectMat = null;
  }

  /**
   * 选中mesh
   * @param {*} mat
   * @returns
   */
  selectMat(mat) {
    // 选中同一个材质就取消
    if (mat && this.selectMat && this.selectMat === mat) {
      this.cancleSelect();
      return;
    }

    this.cancleSelect();
    if (!mat) return;

    this.selectMat = mat;

    if (!this.argunmentChunk) {
      this.argunmentChunk = `
       #include <common>
        uniform vec2 uResolution;
        uniform float uSpacing;
        uniform float uRadius;
    `;
    }

    if (!this.fragmentChunk) {
      this.fragmentChunk = `
        #include <dithering_fragment>

        // 替换原片元着色器中的黑点生成逻辑
        vec2 uv = gl_FragCoord.xy / uResolution;

        // 1. 坐标系修正:将Y轴翻转(适配左上角原点)
        uv.y = 1.0 - uv.y;

        // 2. 宽高比补偿(保持正圆)
        float aspect = uResolution.x / uResolution.y;
        uv.x *= aspect;

        // 3. 网格映射(强制正方形单元格)
        float spacing = uSpacing;
        vec2 gridPos = uv * (1.0 / spacing);
        vec2 cell = fract(gridPos);

        // 4. 圆形计算(反向补偿宽高比)
        vec2 offset = cell - vec2(0.5);
        //offset.x /= aspect; // 关键修正点
        float dist = length(offset);

        // 5. 抗锯齿 + 颜色混合(保留原材质)
        float smoothEdge = fwidth(dist) * 2.0; // 自动边缘平滑
        float mask = smoothstep(uRadius + smoothEdge, uRadius - smoothEdge, dist);
        gl_FragColor = mix(gl_FragColor, vec4(0.0, 0.0, 0.0, 1.0), mask);
        `;
    }

    // 给材质添加选中效果(阵列黑点)
    mat.onBeforeCompile = (shader) => {
      // 添加自定义uniforms
      shader.uniforms.uResolution = {
        value: new Vector2(window.innerWidth, window.innerHeight),
      };
      shader.uniforms.uSpacing = { value: 0.04 };
      shader.uniforms.uRadius = { value: 0.07 };

      // 在着色器中插入uniform声明
      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <common>",
        this.argunmentChunk
      );

      // 替换片元着色器中的dithering部分,添加黑点逻辑
      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <dithering_fragment>",
        this.fragmentChunk
      );
    };
    mat.needsUpdate = true;
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值