three.js实战 -自定义剪切器

news2025/1/4 17:23:33

1. 前言

这是我在github上看到大佬的一个作品,当时感觉很有意思,决定分享出来,不知道取这个名字是否正确,废话不多说看下面效果。

2.demo效果在这里插入图片描述

3.需要掌握的知识

  1. 矩阵的基本运算,能够认是到一些基本变换用到的矩阵(modelMatrix)
  2. 掌握基本的向量运算
  3. 了解shader的基础语法

demo里的知识点,我之前的文章都有涉及,webgl与图形学的内容,代码里我已经加了注释方面各位阅读理解,代码逻辑实际并不难

4. demo代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body {
        margin: 0;
        overflow: hidden;
      }
      button {
        padding: 10px;
        border-radius: 5px;
        margin: 5px;
        font-size: 20px;
      }

      button:hover {
        cursor: pointer;
        color: #0ff;
      }
    </style>
    <script src="./three.js"></script>
    <script src="./OrbitControls.js"></script>
    <script src="./TransformControls.js"></script>
    <script src="./stats.min.js"></script>
  </head>
  <body>
    <div style="position: absolute; top: 10px; right: 10px">
      <button onclick="setTranslate()">translate</button>
      <br />
      <button onclick="setRotate()">rotate</button>
    </div>
  </body>
  <script type="x-shader/x-vertex" id="vertexShader">
    varying vec2 vUv;
    varying vec3 worldPos;
    void main() {
        vUv = uv;
        //求出顶点的世界坐标
        worldPos = (modelMatrix * vec4(position, 1.0)).xyz;
        //worldPos = position;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  </script>
  <script type="x-shader/x-vertex" id="fragmentShader">
    varying vec2 vUv;
    uniform vec3 color;
    varying vec3 worldPos;
    //剪切面的法线
    uniform vec3 clipNormal;
    //剪切面的中心点
    uniform vec3 origin;
    void main() {
      // 这里我们在图形学-向量这篇文章中说过,点积可以判断两个向量方向是否大致相同,如果在剪切面后方的片元那么点积一定是负数
      float d = dot(worldPos - origin, normalize(clipNormal));

      if(d > 0.0) {
        gl_FragColor = vec4(color, 1.0);
      }else {
        //直接跳出,舍去片元
        discard;
      }
           
      gl_FragColor = vec4(vec3(d), 1.0);
    }
  </script>

  <script>
    let scene, camera, renderer, clock, controller, stats;
    let shader_material, plane, normal, transformControl;
    let normalBall, cube;

    init();
    animate();

    // - Functions -
    function setTranslate() {
      transformControl.setMode("translate");
    }

    function setRotate() {
      transformControl.setMode("rotate");
    }

    function init() {
      scene = new THREE.Scene();
      clock = new THREE.Clock();
      camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      camera.position.set(10, 10, 10);
      renderer = new THREE.WebGLRenderer({
        antialias: true, // 开启抗锯齿处理
        alpha: true,
      });
      renderer.setClearColor(0xbfd1e5); // set sky color
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setPixelRatio(window.devicePixelRatio);
      // renderer.shadowMap.enabled = true
      
      //代表剪切面法向量的小球
      normalBall = new THREE.Mesh(
        new THREE.SphereBufferGeometry(0.2, 12, 12),
        new THREE.MeshBasicMaterial({
          color: "red",
          depthTest: false,
          transparent: true,
        })
      );
      normalBall.position.z = 1;
      normalBall.renderOrder = 1;
      scene.add(normalBall);
      
      //剪切面
      plane = new THREE.Mesh(
        new THREE.PlaneBufferGeometry(10, 10),
        new THREE.MeshBasicMaterial({
          color: "#0ff",
          side: THREE.DoubleSide,
          transparent: true,
          opacity: 0.3,
          depthWrite: false,
        })
      );

      scene.add(plane);
      
      //得到初始化的剪切面法向量
      normal = new THREE.Vector3(
        ...plane.geometry.attributes.normal.array.slice(
          0,
          plane.geometry.attributes.normal.itemSize
        )
      );
      shader_material = new THREE.ShaderMaterial({
        uniforms: {
          clipNormal: {
            // value: new THREE.Vector3(0, 0, 1)
            value: normal,
          },
          color: {
            value: new THREE.Color(1.0, 1.0, 0.0),
          },
          origin: {
            value: plane.position.clone(),
          },
        },
        vertexShader: document.getElementById("vertexShader").textContent,
        fragmentShader: document.getElementById("fragmentShader").textContent,
      });

      const axisHelper = new THREE.AxesHelper(10);
      scene.add(axisHelper);

      //环境光
      const ambient = new THREE.AmbientLight(0x404040, 0.6);
      scene.add(ambient);

      stats = new Stats();
      document.body.appendChild(stats.dom);

      // --------
      const geometry = new THREE.BoxBufferGeometry(2, 2, 2);
      const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
      cube = new THREE.Mesh(geometry, shader_material);
      scene.add(cube);

      // --------

      controller = new THREE.OrbitControls(camera, renderer.domElement);
      document.body.appendChild(renderer.domElement);
      window.onresize = onResize;

      transformControl = new THREE.TransformControls(
        camera,
        renderer.domElement
      );
      transformControl.attach(plane);
      transformControl.addEventListener("mouseDown", function () {
        controller.enabled = false;
      });
      transformControl.addEventListener("mouseUp", function () {
        controller.enabled = true;
      });
      transformControl.addEventListener("change", function () {
        // plane.updateMatrix();

        shader_material.uniforms.origin.value = plane.position.clone();

   
        //得到变换后的法向量,是逆矩阵再转置矩阵(当顶点发生改变时,顶点对应的法向量如何变化.
        //这里我们在WebGL编程指南-26文章中,旋转地物体在点光源的作用下每个片元呈现的效果中说过 
        shader_material.uniforms.clipNormal.value = normal
          .clone()
          .applyMatrix3(new THREE.Matrix3().getNormalMatrix(plane.matrix));

        // .getInverse - 求逆
        // .transpose - 转置

        normalBall.position.copy(normal.clone().applyMatrix4(plane.matrix));
      });
      scene.add(transformControl);
    }

    function animate() {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
      stats.update();
      controller.update(clock.getDelta());
      // cube.rotation.y += 0.01
    }

    function onResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }
  </script>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/71438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

晶圆级倒装装备及控制系统

晶圆级倒装装备主要由晶圆盘进料模块、晶圆盘工作台模块、覆晶模块、焊头模块、基板工作台模块、点胶模块、视觉模块和基板进出料模块组成&#xff0c;如图 2-2 所示。 晶圆级倒装装备控制系统结构晶圆级倒装装备的运控系统主要由工控机、运动控制卡、驱动器、反馈装置和直线电…

QA:observable and Subject

概念区别和常见的错误理解辩证&#xff1a; 通俗理解一下 1. Observable 是一条 "水管蓝图" ,每次打开水龙头&#xff0c;水流会按照设计好的路线流向终点。起点和终点一一对应。每次打开水流&#xff0c;都是新的流&#xff0c;水流之间互不影响。一次一管。 2. …

VMWare虚拟机设置CentOS7共享文件夹

1. 目录 系统版本&#xff1a;CentOS 7.9 文章目录1. 目录2. VMWare&#xff1a;虚拟机设置&#xff0c;设置共享文件夹3. 虚拟机设置&#xff1a;手动挂载共享文件夹4. 检查是否挂载成功5. (可选)创建共享文件夹的软链接(快捷方式)6. (可选)定时任务开机自动挂载2. VMWare&am…

博客文章分类导引(持续更新)

摘要&#xff1a;本文提供一篇博客目录&#xff0c;有物联网、安卓编程、硬件设计等若干主题&#xff0c;这些主题一般都是成系统的&#xff0c;可以实现从零开始做出自己的物联网系统。 文章结构如下&#xff1a; 1.物联网专栏 使用arduino编写mqtt客户端连接emqx服务器 VSC…

vue.js:组件化的实现和使用过程

什么是组件化&#xff1f; 当我们遇到复杂问题的时候&#xff1a; 任何一个人处理信息的逻辑能力都是有限的所以&#xff0c;当我们面对一个复杂的问题的时候&#xff0c;我们不可能一次性搞定处理掉一大堆内容但是我们都会有问题拆解的能力将一个复杂的问题拆解成很多小的问…

Springcloud笔记之Ribbon

Ribbon&#xff1a;负载均衡&#xff08;基于客户端&#xff09;1. 负载均衡以及Ribbon1.1 Ribbon 的概念1.2 Ribbon 的作用2. 集成Ribbon3. 使用Ribbon实现负载均衡3.1 步骤3.2 自定义规则1. 负载均衡以及Ribbon 1.1 Ribbon 的概念 Spring Cloud Ribbon 是基于Netflix Ribbo…

[附源码]计算机毕业设计JAVA智能超市导购系统

[附源码]计算机毕业设计JAVA智能超市导购系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

WebRTC Native M96 回调音频裸数据IAudioFrameObserver--采集的音频(onRecordAudioFrame)

上篇已经说道,通过注册回调,给上层APP抛音频裸数据: 《WebRTC Native M96 SDK接口封装–注册语音观测器对象获取原始音频数据registerAudioFrameObserver》[https://dabaojian.blog.csdn.net/article/details/128218542] 此篇,就详细讲述一下,如果实现onRecordAudioFrame…

vue学习笔记(二)-vue生命周期

概念 a.又名生命周期回调函数、生命周期函数、生命周期钩子b.是什么&#xff1a;Vue在关键时刻帮我们调用的一些特殊名称的函数c.生命周期函数的名字不可更改&#xff0c;但函数的具体内容是程序员根据需求编写的d.生命周期函数中的 this 指向是vm或组件实例对象 示例代码&am…

来一场关于元宇宙可持续的灵魂辩论|BOOK DAO 内容共建 第6期 招募

小杜《元宇宙创意图谱》是 BOOK DAO 的共建书籍项目&#xff0c;12.03我们举行了虚拟时尚主题的第5次公开共建活动。本周六晚8点&#xff0c;我们将举办第6期 元宇宙可持续 专题的共建活动。BOOK DAO 以搭建产业界与用户之间的交流平台、挖掘业界最值得深入探讨研究的话题为目标…

【linux】ssh免密登录

概要 服务器免密登录实际上是基于公钥的认证&#xff0c;比如希望A服务器可以免密访问B服务器&#xff0c;则需要进行如下步骤 A服务器生成密钥对将A服务器生成的公钥分发到B服务器&#xff08;写入~/.ssh/authorized_keys&#xff09;A服务器即可免密登录B服务器 生成密钥对…

特殊类的设计(单类模式)

一.不能拷贝的类 首先要知道拷贝的场景&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;想要让一个类禁止拷贝&#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 方法1&#xff1a;将这两个函数只声明&#xff0c;不定义&#xff08;防止编译器默…

【愚公系列】2022年12月 Redis数据库-Cache和Redis缓存的无缝切换使用

文章目录前言一、Cache和Redis缓存的无缝切换使用1.安装包2.服务配置3.创建控制器4.启动程序前言 Redis是分布式缓存&#xff0c;是将数据随机分配到不同服务器的&#xff0c;catch属于单机缓存&#xff0c;只能本机访问。 Redis和Cache的区别吧 Redis和cache都是将数据存放…

Nginx rewrite 详解

Nginx rewrite 详解 本篇主要介绍 nginx 的 rewrite 重定向这个功能进行 详解介绍, 以及介绍它的使用场景 1. rewrite 基本介绍 rewrite是实现URL重写的关键指令&#xff0c;根据regex (正则表达式)部分内容,重定向到replacement&#xff0c;结尾是flag标记。 基本语法: re…

JAVA SCRIPT设计模式--结构型--设计模式之Decorator装饰模式(9)

JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能&#xff0c;所以不可能像C&#xff0c;JAVA等面向对象语言一样严谨&#xff0c;大部分程序都附上了JAVA SCRIPT代码&#xff0c;代码只是实现了设计模式的主体功能&#xff0c;不代…

JSP ssh房地产项目管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 JSP ssh房地产项目管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用 B/S模式开发。开发环境为TOMCAT7…

吉林省教育学院学报杂志社吉林省教育学院学报编辑部2022年第11期目录

特稿 高职院校思政课“一体两翼四融合”教学模式的构建探索——以吉林工业职业技术学院为例 杨宝忠;解静; 1-5 高校业财融合体系研究 朱延辉; 6-9 教育理论与管理《吉林教育学院学报》投稿&#xff1a;cn7kantougao163.com 新教育评价改革背景下幼儿园教师评价能力…

pikachu-xss部分速通

pikachu-xss部分速通 &#x1f349; &#x1f349;目录pikachu-xss部分速通反射型xss(get)反射性xss(post)存储型xssDOM型xss、DOM型xss-xxss盲打xss之过滤xss之htmlspecialcharsxss之href输出xss之js输出反射型xss(get) payload: </p><script>alert(1)</scr…

AI绘画绘图流量主小程序开发

AI绘画绘图流量主小程序开发 响应式设计——响应式布局&#xff0c;手机、平板、PC自适应匹配。 自定义模型——自定义内容模型、自定义字段、自定义表单。 付费阅读——支持企业支付宝、企业微信支付、余额支付无缝整合。 微信小程序端——微信小程序CMS客户端和服务端全部源…

【Linux】Docker 搭建 Jenkins

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 目录一、Jenkins到底是什么&#xff1f;二、持…