搓绳子(直)

缘起Blender

在练习Blender的时候,某个案例里面用到了绳子。我就在想,three里面要怎么做一根绳子呢。

用Blender搓绳子

three里面怎么搓,暂且还不知道。 先来仔细研究Blender里面是怎么搓的。

  1. 新建一个圆环
  2. 使用螺旋修改器,轴向为圆环的某直径,
  3. 修改角度,螺旋长度,和迭代次数。

可以看到,Blender里面就是用螺旋修改器(车削),车这个圆环180度, 就出来一个球,然后,这个球沿刚才的轴向拉伸,出来一个油条麻花一样的东西,最后就是不断地迭代(重复)了。

螺旋修改器与车削几何体

threejs里面有没有类似的几何体呢?当然是有的,那就是车削几何体。 只不过,three的车削轴向是固定为Y轴而已。

不考虑轴向和螺旋(距离)、迭代次数,three的车削几何体和Blender的螺旋修改器可以说是相差无几了。

虽然有限制,前两步还是可以做到的,我拿了一个圆环车了90度,效果如下。

js 复制代码
const circleCurve = new CircleCurve(1);// 简单自定义了一个圆环曲线
const circlePoints  = circleCurve.getPoints(32);
const rope  = new LatheGeometry(circlePoints,32 * 2,0,Math.PI *4); // x y  二维路径点 切面分段数 起始 总弧度) ;

但是,这个螺旋(长度)参数three上没有。仔细观察,不难看出。这个螺旋的意思就是,每车一定的角度,在轴向上移动一段距离。

大概试了一下,可以是可以,但是,这里还要处理三角形的绕匝,虽然也可以用双面来解决就是了,又去看了一下Blender,一样,也是有背面的问题。

再优化一下迭代,重复车削这里,理论上是可以减少计算的,直接使用第一次车削的,在轴向上偏移和旋转即可。

但是,我觉得有点儿麻烦,这个车削几何体改螺旋修改器有点儿不好搞。

算了,我决定用更简单的法子,不需要修改three的几何体。

那就是管道几何体。刚好,最近由于某些原因,对管道几何体有了一点儿深入的研究,所以也更加熟悉一些。

使用管道几何体

管道几何体可以说是特殊的挤出几何体,截面是圆。管道几何体就是让圆环在给定的路径上运动一下,线扫成面,形成的几何体。

管道几何体最重要的就是路径曲线 。 就是下面的参数1path

js 复制代码
TubeGeometry(path: QuadraticBezierCurve3 , tubularSegments: number , radius: number , radialSegments: number , closed: boolean , cover: boolean

其实,这个麻绳,可以拆解为两个细的绳子扭在一起,就是两个管道。管道的路径曲线,就是一个螺旋曲线。我们只需要造出一条螺旋曲线,再绕Y轴旋转180度,就可以拿到另一条曲线。

自定义螺旋曲线

一条曲线,最重要的就是取点规则。这里的取点,用的是曲线的起点到终点的路程百分比。只要输入百分比,就输出一个曲线上的点。

自定义曲线只需要继承原本的Curve,并自定义getPoint方法即可。

我们先来一个简单的圆形曲线热热身。 其中getPoint方法的参数1就是路程百分比,参数2是用来接输出的点位的。

js 复制代码
export class CircleCurve extends Curve {
  /**
 * 构造函数
 *
 * @param {number} r - 圆的半径,默认为1
 * @param {Vector3} center - 圆心位置,默认为(0,0,0)
 * @param {number} startAngle - 起始角度,默认为0
 */
  constructor(r = 1, center = new Vector3(0,0,0), startAngle = 0){
    super();
    this.r = r;
    this.center = center;
    this.startAngle = startAngle;
  }
  getPoint(t, target  = new Vector3()){
    t *= 2*PI + this.startAngle; 
    let x = cos(t)*this.r, y = sin(t) *this.r;
    return target.set(x,y,0).add(this.center);
  }
}

螺旋曲线

什么是螺旋?就是一边儿打转儿一边儿上天,螺旋升天。 也就是说,我们只要在刚才的圆形曲线的基础上加上z轴的偏移,它就是一个螺旋曲线了。

对了,这种单函数的曲线路线,可以用这个网站直接显示出来。

js 复制代码
 getPoint(t, target  = new Vector3()){
    t *= 2*PI + this.startAngle; 
    let x = cos(t)*this.r, y = sin(t) *this.r;
    return target.set(x,y,t).add(this.center);
  }

显然,我们可以看到这个曲线初步效果出来了,但是有些参数需要调整。

  1. 原本的圆只有[0,2π],而螺旋应该放开这个限制
  2. 现在有了z方向的偏移,我们应该加一个参数控制这个偏移量

调整后如下。 depth为总拉伸量。

js 复制代码
class SpiralPath extends Curve {
  constructor(r = 1, theta = PI * 2, depth = 1, thetaOffset = 0) {
    super();
    this.r = r;
    this.theta = theta;
    this.thetaOffset = thetaOffset;
    this.depth = depth;
  }

  getPoint(t) {
    const z = this.depth * t;
    const theta = this.theta * t + this.thetaOffset;
    return new Vector3(this.r * Math.cos(theta),  this.r * -Math.sin(theta),z);
  }
   
}

这个曲线还有可以优化的地方,一个是距离的计算,这里是一个非常简单的函数曲线,其总路程我们是可以直接算出来的,路程的计算会影响到一些东西比如uv。 然后就是切线的计算,按理说是可以通过数学公式计算出来,不过我目前还没写出正确的来。

螺旋管道

螺旋曲线有了,管道也就水到渠成。 刚才的曲线命名为 SpiralCurve

js 复制代码
  const spiralCurve = new SpiralCurve(
    spiralRadius,
    spiralTheta,
    spiralDepth,
    thetaOffset
  );
  const spiralGeo = new TubeGeometry2(
    spiralCurve,
    tubeSegments,
    tubeRadius,
    tubeRadiusSegments,
    close,
    cover
  );
const spiralMat = new MeshPhysicalMaterial({
  color: 0xfff10f,
});
const tube1 = new Mesh(spiralGeo, spiralMat);

双螺旋

只要把上面的Mesh浅克隆一个,绕Z轴旋转180度,即可实现双螺旋,还能复用几何体,真是一举多得。

js 复制代码
const tube2 = new Mesh(spiralGeo, spiralMat);
tube2.rotateZ(Math.PI);

要搓成一根绳子,这个螺旋的半径,刚好就是管道的半径。。

下面demo里,我已经把曲线的长度计算的优化做了,法线那里其实有一个不算优化的优化,就是切线,three本身是直接用两点连线作为切线的,而我们这种丝滑的函数曲线,完全可以用三个点计算切线。

上贴图

麻绳的形状有了,但是还缺少一些细节。 用Blenderkit随便找了一个绳子的材质,把材质的纹理贴图分离出来了,用在three里面,用上去有效果的就三张贴图,所以也只用了三张贴图。

js 复制代码
const spiralMat = new MeshPhysicalMaterial({
  color: 0xfff10f,
    map: ropeMap,
  normalMap: ropeNormalMap,
  roughnessMap: ropeRoughMap,
});

用了贴图之后,绳子的质感一下子就出来了。

小结

搓绳子就是把两条细的绳子搓成一根不那么细的。

两条细的,就是两条管道,其路径形状是一个螺旋曲线。

螺旋就是一边转圈一边上升。

两条螺旋曲线刚好是绕轴旋转180度的对称关系。

这个绳子是搓出来了,但是还有一些地方需要优化的。 比如说,three的Tube几何体两端是没有封口的,可以加一下,不然这个绳子就只能空心的了。

至于弯曲的绳子,下一篇再说。

相关推荐
木子七21 分钟前
Js Dom
前端·javascript
谢小飞1 小时前
我做了三把椅子原来纹理这样加载切换
前端·three.js
圈圈的熊1 小时前
HTTP 和 HTTPS 的区别
前端·网络协议·http·https
GIS程序媛—椰子1 小时前
【Vue 全家桶】2、Vue 组件化编程
前端·javascript·vue.js
Beamon__1 小时前
element-plus按需引入报错IconsResolver is not a function
前端
努力奔波的程序猿1 小时前
HBuilderx修改主题色-改变编辑器背景颜色等
前端
正小安1 小时前
Vue 3 性能提升与 Vue 2 的比较 - 2024最新版前端秋招面试短期突击面试题【100道】
前端·vue.js·面试
yqcoder1 小时前
electron 中 ipcRenderer 的常用方法有哪些?
前端·javascript·electron
T0uken2 小时前
【Python】Bottle:轻量Web框架
开发语言·前端·python