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

threejs创建自定义多段柱

最近在研究自定义建模,有一个多断柱模型比较有意思,分享下,就是利用几组点串,比如上中下,然后每组点又不一样多,点续还不一样,(比如第一个环的第一个点在左边,第二个环在右边),然后进行插值,连接三角网

主函数:

export function createMultiRingPrismMesh(rings: Vector3[][],material?: Material,
) {if (!material) {const texture = new TextureLoader().load(green)material = new MeshStandardMaterial({map:texture,color: 0x3399ff,side: DoubleSide,wireframe: false,})}if (rings.length < 2) {throw new Error('至少需要两个环来构建几何体')}// 将所有环插值为统一点数const targetCount = Math.max(...rings.map((r) => r.length))const processedRings = rings.map((ring) => {const center = computeCenter(ring)const angles = computeAngles(ring, center)const { ring: sortedRing, angles: sortedAngles } = sortRingByAngle(ring,angles,)return interpolateRingByAngles(sortedRing, sortedAngles, targetCount)})const vertices = []const uvs = []const indices = []const ringCount = processedRings.lengthconst count = targetCount// 顶点和UVprocessedRings.forEach((ring, ringIndex) => {ring.forEach((p, i) => {vertices.push(p.x, p.y, p.z)// UV 映射:U 是角度,V 是高度归一化// const angle = (i / count) * Math.PI * 2uvs.push(i / count, ringIndex / (ringCount - 1)) // U: 角度比例, V: 层级比例})})// 侧面三角形索引for (let i = 0; i < ringCount - 1; i++) {const offset = i * countfor (let j = 0; j < count; j++) {const curr = offset + jconst next = offset + ((j + 1) % count)const currTop = curr + countconst nextTop = next + countindices.push(curr, next, currTop)indices.push(next, nextTop, currTop)}}// 封底(第一个环)const bottomCenter = computeCenter(processedRings[0])const bottomCenterIndex = vertices.length / 3vertices.push(bottomCenter.x, bottomCenter.y, bottomCenter.z)uvs.push(0.5, 0.5)for (let i = 0; i < count; i++) {const next = (i + 1) % countindices.push(bottomCenterIndex, next, i)}// 封顶(最后一个环)const topCenter = computeCenter(processedRings[ringCount - 1])const topCenterIndex = vertices.length / 3const topOffset = (ringCount - 1) * countvertices.push(topCenter.x, topCenter.y, topCenter.z)uvs.push(0.5, 0.5)for (let i = 0; i < count; i++) {const next = (i + 1) % countindices.push(topCenterIndex, topOffset + i, topOffset + next)}const geometry = new BufferGeometry()geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3))geometry.setAttribute('uv', new Float32BufferAttribute(uvs, 2))geometry.setIndex(indices)geometry.computeVertexNormals()return new Mesh(geometry, material)
}

 辅助函数

/*** 计算环中心点(平均点)* @param {Vector3[]} ring* @returns {Vector3}*/
function computeCenter(ring: Vector3[]) {const center = new Vector3(0, 0, 0)ring.forEach((p) => center.add(p))center.multiplyScalar(1 / ring.length)return center
}
/*** 计算环中每点相对于中心的角度(0~2π)* 这里按 XZ 平面计算角度* @param {Vector3[]} ring* @param {Vector3} center* @returns {number[]} 角度数组,与ring点一一对应*/
function computeAngles(ring: Vector3[], center: Vector3) {return ring.map((p) => {const dx = p.x - center.xconst dz = p.z - center.zlet angle = Math.atan2(dz, dx)if (angle < 0) angle += Math.PI * 2return angle})
}function interpolateRingByAngles(ring: Vector3[],angles: number[],targetCount: number,
) {const result = []const len = ring.length// 角度归一化到 [0, 2PI)const TWO_PI = Math.PI * 2for (let i = 0; i < targetCount; i++) {const t = (i / targetCount) * TWO_PI// 找到 t 在 angles 中所在区间 [angles[j], angles[j+1]]// 注意最后一个区间是 [angles[len-1], angles[0] + 2PI]let j = 0for (; j < len; j++) {const a0 = angles[j]const a1 = angles[(j + 1) % len] + (j === len - 1 ? TWO_PI : 0)if (t >= a0 && t <= a1) {// 找到区间const p0 = ring[j]const p1 = ring[(j + 1) % len]const localT = (t - a0) / (a1 - a0)const interpolated = new Vector3().lerpVectors(p0, p1, localT)result.push(interpolated)break}}// 保险 fallback,如果没找到区间,返回第一个点if (j === len) {result.push(ring[0].clone())}}return result
}// 对角度和点排序(保证升序)
function sortRingByAngle(ring: Vector3[], angles: number[]) {const pairs = ring.map((p, i) => ({ p, angle: angles[i] }))pairs.sort((a, b) => a.angle - b.angle)return {ring: pairs.map((pair) => pair.p),angles: pairs.map((pair) => pair.angle),}
}

使用

createMultiRingPrismMesh([points,points2,points3])

http://www.dtcms.com/a/307581.html

相关文章:

  • 浅谈物联网嵌入式程序开发源码技术方案
  • STORM代码阅读笔记
  • 邢台市某区人民医院智慧康养平台建设项目案例研究
  • Mac安装Navicat教程Navicat Premium for Mac v17.1.9 Mac安装navicat【亲测】
  • 【ARM】PK51关于内存模式的解析与选择
  • c++:设计模式训练
  • 两款免费数据恢复软件介绍,Win/Mac均可用
  • 【javascript】new.target 学习笔记
  • 揭秘动态测试:软件质量的实战防线
  • List和 ObservableCollection 的区别
  • 【worklist】worklist的hl7、dicom是什么关系
  • 原生安卓与flutter混编的实现
  • 如何使用一台电脑adb调试多个Android设备
  • AI 如何评价股票:三七互娱(SZ:002555),巨人网络(SZ:002558)
  • 解决:MATLAB 已经画好了Figure,想在不重新绘图的情况下去掉坐标轴刻度线
  • Java 大视界 -- Java 大数据在智能医疗远程健康监测与疾病预防预警中的应用(374)
  • 《以终为始,不辩过程》
  • cartographer 概率栅格地图
  • JVM面试通关指南:内存区域、类加载器、双亲委派与GC算法全解析
  • 一万字讲解Java中的IO流——包含底层原理
  • GCC/G++ + Makefile/make 使用
  • Visual Studio调试技巧与函数递归详解
  • “0 成本开跨境店” 噱头下的优哩哩:商业模式深度剖析
  • 5G 单兵终端 + 无人机:消防应急场景的 “空 - 地” 救援协同体系
  • 【可用有效】Axure RP 9 授权码
  • imx6ull-驱动开发篇5——新字符设备驱动实验
  • springcloud04——网关gateway、熔断器 sentinel
  • cas自定义返回信息和自定义认证
  • 考研408_数据结构笔记(第三章栈、队列和数组)
  • 解构衡石嵌入式BI:统一语义层与API网关的原子化封装架构