搓绳子(直)

缘起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几何体两端是没有封口的,可以加一下,不然这个绳子就只能空心的了。

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

相关推荐
Gungnirss9 分钟前
前后端分离,后端拦截器无法获得前端请求的token
java·前端·token
五秒法则23 分钟前
从搭建uni-app+vue3工程开始
前端·vue.js·uni-app
风之舞_yjf29 分钟前
css基础(27)_行内、行内块元素之间的空白问题
前端·css
Kika写代码1 小时前
【基于轻量型架构的WEB开发】课程 第14章 SSM框架整合 Java EE企业级应用开发教程 Spring+SpringMVC+MyBatis
前端·架构·java-ee
WuMingf_1 小时前
CSS基础
前端·css
白水4651 小时前
基于官网的Vue-router安装(2024/11)
前端·vue.js·vue
白墨阳1 小时前
vue3:scss引用
前端·css·scss
小小优化师 anny1 小时前
纯CSS 写的一个树状触摸菜单
前端·css·css3
努力小贼1 小时前
Vue小项目(开发一个购物车)
前端·javascript·vue.js
xcLeigh1 小时前
HTML5好看的音乐播放器多种风格(附源码)
前端·html·html5