当前位置: 首页 > article >正文

使用three.js 实现 自定义绘制平面的效果

使用three.js 实现 自定义绘制平面的效果 预览
在这里插入图片描述

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

const box = document.getElementById('box')

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(75, box.clientWidth / box.clientHeight, 0.1, 1000)

camera.position.set(0, 3, 3)

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true })

renderer.setSize(box.clientWidth, box.clientHeight)

box.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)

controls.enableDamping = true

const directionalLight = new THREE.DirectionalLight(0xffffff, 1)

directionalLight.position.set(0, 20, 0)

scene.add(directionalLight, new THREE.AmbientLight(0xffffff, 1))

/* 增加一个面 */
const plane = new THREE.PlaneGeometry(5, 5)

const material = new THREE.MeshStandardMaterial({ color: 0xffffff })

const planeMesh = new THREE.Mesh(plane, material)

planeMesh.rotation.x -= Math.PI / 2

scene.add(planeMesh)

animate()

function animate() {

  requestAnimationFrame(animate)

  controls.update()

  renderer.render(scene, camera)

}

window.onresize = () => {

  renderer.setSize(box.clientWidth, box.clientHeight)

  camera.aspect = box.clientWidth / box.clientHeight

  camera.updateProjectionMatrix()

}

// 事件
const raycaster = new THREE.Raycaster()

const getPoint = event => {

  const mouse = new THREE.Vector2(

    (event.offsetX / event.target.clientWidth) * 2 - 1,

    -(event.offsetY / event.target.clientHeight) * 2 + 1

  )

  raycaster.setFromCamera(mouse, camera)

  const intersects = raycaster.intersectObjects(scene.children)

  if (intersects.length > 0) return intersects[0].point

}

const setPointBox = point => {

  const box = new THREE.BoxGeometry(0.04, 0.04, 0.04)

  const material = new THREE.MeshStandardMaterial({ color: 0xff0000 })

  const boxMesh = new THREE.Mesh(box, material)

  boxMesh.position.copy(point)

  scene.add(boxMesh)

}

/* 开始绘制 */
const pointList = []; let drawMesh = null; let stop = false

box.addEventListener('contextmenu', () => {

  stop = true

  const { indexGroup, faceGroup, uvGroup } = multShapeGroup(pointList)

  if (drawMesh) updateMultShapePlaneGeometry(drawMesh.geometry, faceGroup, indexGroup, uvGroup)

})

// 移动
box.addEventListener('mousemove', (event) => {

  if (stop) return

  const point = getPoint(event)

  if (!point || !drawMesh || pointList.length < 2) return

  const { indexGroup, faceGroup, uvGroup } = multShapeGroup([...pointList, point])

  updateMultShapePlaneGeometry(drawMesh.geometry, faceGroup, indexGroup, uvGroup)

})

box.addEventListener('click', (event) => {

  const point = getPoint(event)

  if (!point || stop) return

  setPointBox(point)

  point.y += 0.001

  pointList.push(point)

  const { indexGroup, faceGroup, uvGroup } = multShapeGroup(pointList)

  if (pointList.length < 3) return

  if (!drawMesh) {

    const geometry = multShapePlaneGeometry(faceGroup, indexGroup, uvGroup)

    const material = new THREE.MeshStandardMaterial({ color: 0x00ff00, side: THREE.DoubleSide })

    drawMesh = new THREE.Mesh(geometry, material)

    scene.add(drawMesh)

  }

  else updateMultShapePlaneGeometry(drawMesh.geometry, faceGroup, indexGroup, uvGroup)

})

/* 处理顶点算法 */
function multShapeGroup(pList) {

  const indexGroup = pList.map((_, k) => (k >= 2) ? [0, k - 1, k] : false).filter((i) => i).reduce((i, j) => [...i, ...j], [])

  const faceGroup = pList.reduce((j, i) => [...j, i.x, i.y, i.z], [])

  const uvMaxMin = pList.reduce((p, i) => ({ x: [...p['x'], i['x']], y: [...p['y'], i['y']], z: [...p['z'], i['z']] }), { x: [], y: [], z: [] })

  // vu 点计算 二维面
  const Maxp = new THREE.Vector3(Math.max(...uvMaxMin.x), Math.max(...uvMaxMin.y), Math.max(...uvMaxMin.z))  // 最大点

  const Minp = new THREE.Vector3(Math.min(...uvMaxMin.x), Math.min(...uvMaxMin.y), Math.min(...uvMaxMin.z))  // 最小点

  const W = Maxp.x - Minp.x

  const H = Maxp.y - Minp.y

  const L = W > H ? W : H  // 以最大为基准

  // 顶点uv计算
  const uvGroup = pList.map(i => new THREE.Vector2((i.x - Minp.x) / L, (i.y - Minp.y) / L)).reduce((i, j) => [...i, ...j], [])

  return { indexGroup, faceGroup, uvGroup }

}

/* 根据顶点组生成物体 */
function multShapePlaneGeometry(faceGroup, indexGroup, uvGroup) {

  const geometry = new THREE.BufferGeometry()

  // 因为在两个三角面片里,这两个顶点都需要被用到。
  const vertices = new Float32Array(faceGroup)

  // itemSize = 3 因为每个顶点都是一个三元组。
  geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))

  // 索引组 面
  if (indexGroup) {

    // 格式化索引面组
    let indexs = new Uint16Array(indexGroup)

    // 添加索引组
    geometry.index = new THREE.BufferAttribute(indexs, 1)

  }

  // uv 是二维坐标相当于三维物体展开图
  if (uvGroup) geometry.attributes.uv = new THREE.Float32BufferAttribute(uvGroup, 2)

  geometry.computeVertexNormals()

  return geometry

}

/* 更新顶点 */
function updateMultShapePlaneGeometry(geometry, faceGroup, indexGroup, uvGroup) {

  geometry.setIndex(indexGroup)

  geometry.setAttribute('position', new THREE.Float32BufferAttribute(faceGroup, 3))

  geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvGroup, 2))

  delete geometry.attributes.normal

  geometry.computeVertexNormals()

}

/**
 * 名称: 绘制面
 * 作者: 优雅永不过时 https://github.com/z2586300277
*/


http://www.kler.cn/a/372706.html

相关文章:

  • 尽可能连续的基于挤压的表面模型制造
  • 从0到1构建 UniApp + Vue3 + TypeScript 移动端跨平台开源脚手架
  • 二百七十、Kettle——ClickHouse中增量导入清洗数据错误表
  • 日志库Serilog 笔记
  • Ubuntu下安装和配置MySQL5.7教程
  • [JAVAEE] 面试题(二) - CAS 和 原子类
  • Golang 并发编程入门:Goroutine 简介与基础用法
  • 适配器模式:连接不兼容接口的桥梁
  • NVR小程序接入平台/设备EasyNVR多个NVR同时管理多平台级联与上下级对接的高效应用
  • Nginx 的讲解和案例示范
  • SpringBoot+VUE2完成WebSocket聊天(数据入库)
  • ‌MySQL中‌between and的基本用法‌
  • macOS 15 Sequoia dmg格式转用于虚拟机的iso格式教程
  • 【Mac】安装 F5-TTS
  • 阿里云ECS访问GitHub解决方案
  • 公共命名空间,2024年10月的笔记
  • 6款IntelliJ IDEA插件,让Spring和Java开发如虎添翼
  • 微信小程序生成二维码
  • Vue中如何解析docx文件(word文档)
  • 零跑汽车嵌入式面试题汇总及参考答案
  • 如何理解Js中闭包
  • LeetCode 242 - 有效的字母异位词
  • (done) 什么 RPC 协议? remote procedure call 远程调用协议
  • Comsol基于亥姆霍兹声学超材料的通风式低频吸声器
  • 【Linux】文件切割排序 cut sort
  • 微信小程序元素水平居中或垂直居中