threejs-生成带宽度的线-MeshLine的使用

4,678 阅读3分钟

线材质设置linewidth无效的原因

由于OpenGL Core Profile与 大多数平台上WebGL渲染器的限制,无论如何设置该值,线宽始终为1。 解决方法:线用面来代替,首选使用现成的库,今天就使用MeshLine.js

MeshLine.js介绍

引入

原版github.com/spite/THREE…
es版代码地址,使用这一版 github.com/utsuboco/TH…

npm i three.meshline 
import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'meshline';

介绍

1.MeshLine主要方法

setPoints

setPoints(points: Float32Array | Array<number>, wcb?: (p: number) => any): void;

根据传入的顶点数据生成几何体

setGeometry

setGeometry(g: THREE.BufferGeometry, c: (p: number) => any): void;

根据一个THREE.BufferGeometry来获取顶点数据生成几何体

2.MeshLineMaterial主要属性介绍

MeshLineMaterial继承自ShaderMaterial,所以ShaderMateria上的有些属性也是可以用的(比如 wireframe: true)

export class MeshLineMaterial extends THREE.ShaderMaterial 
  • map - 贴图 THREE.Texture
  • useMap - 是否使用贴图 (0 - 不使用,用color中的颜色, 1 - 使用)
  • alphaMap - 一张灰度纹理,用于控制整个表面的不透明度 THREE.Texture
  • useAlphaMap - 是否使用灰度贴图 (0-不使用, 1-使用)
  • repeat - 设置纹理的重复 THREE.Vector2
  • color - 线的颜色
  • opacity - 透明度 (transparent 需要透明设置为 true`)
  • alphaTest - 透明度测试,设置一个0-1的值,透明度小于这值的就不会显示
  • dashArray - 通俗解释为 一组虚线(有颜色部分跟间隔部分加起来)占总长度的比例,决定绘制多少组虚线(详见后面示例)
  • dashOffset - 一组虚线中间隔开始的位置,改变其位置用来实现动画 (详见后面示例)
  • dashRatio - 定义一组虚线中无颜色的间隔部分占比为多少 0-1(详见后面示例)
  • resolution - 指定画布大小THREE.Vector2 
  • sizeAttenuation - 线的大小是否会受相机影响。(0 - 不会, 1 - 会)
  • lineWidth - 线宽

3.MeshLineRaycast使线可以被选中

mesh.raycast = MeshLineRaycast

把线模型这样设置后,raycaster.intersectObjects([线模型])的时候,就能捕捉到线模型了

简单使用

开启wireframe: true,看出生成的几何体,是跟着相机在不断调整朝向,才看起来不是一个片 GIF.gif

....省去基本场景的搭建
// 生成一段贝赛尔曲线
const curve = new THREE.CubicBezierCurve3(
  new THREE.Vector3(-100, 0, 0),
  new THREE.Vector3(-50, 150, 0),
  new THREE.Vector3(200, 150, 0),
  new THREE.Vector3(100, 0, 0)
)
// 获取这段线上51个坐标点
const points = curve.getPoints(50)
// 将坐标数据存入positions中
const positions = []
for (let j = 0; j < points.length; j++) {
  positions.push(points[j].x, points[j].y, points[j].z)
}
// 初始化MeshLine
const line = new MeshLine()
// 传入顶点坐标数据
line.setPoints(positions)
// 获取纹理,官方案例中的纹理
this.texture = new THREE.TextureLoader().load('static/material/stroke.png')
this.texture.wrapS = this.texture.wrapT = THREE.RepeatWrapping
// 生成线材质
this.material = new MeshLineMaterial({
  useMap: 0,
  color: new THREE.Color(0x006666),
  opacity: 1,
  resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
  sizeAttenuation: 1,
  lineWidth: 20,
  transparent: true,
  wireframe: true,
  map: this.texture
})
// 生成模型
const mesh = new THREE.Mesh(line.geometry, this.material)
this.scene.add(mesh)

参数调整

1.使用纹理

this.material = new MeshLineMaterial({
  useMap: 1,   //修改
  color: new THREE.Color(0x006666),
  opacity: 1,
  resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
  sizeAttenuation: 1,
  lineWidth: 20,
  transparent: true,
  // wireframe: true, //修改
  map: this.texture 
})

image.png

2.调整repeat跟间隔相关

为了直观注释掉transparent: true

this.material = new MeshLineMaterial({
  useMap: 1,
  color: new THREE.Color(0x006666),
  opacity: 1,
  resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
  sizeAttenuation: 1,
  lineWidth: 20,
  // transparent: true, // 修改
  // wireframe: true,
  dashArray: 0.5,  // 修改
  dashRatio: 0.1,  // 新增
  repeat: new THREE.Vector2(2, 1), // 新增
  map: this.texture
})

dashArray: 0.5,就是每组占0.5,所以分成了两组

image.png

repeat: new THREE.Vector2(2, 1),纹理x重复为2,就平铺为两个

image.png

dashRatio: 0.1,每组中,间隔部分占比为0.1

image.png

3.修改dashOffset动起来

this.material = new MeshLineMaterial({
  useMap: 0,  // 修改
  color: new THREE.Color(0x006666),
  opacity: 1,
  resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
  sizeAttenuation: 1,
  lineWidth: 20,
  transparent: true,
  // useDash: 1,
  // wireframe: true,
  dashArray: 0.5,
  dashRatio: 0.1,
  dashOffset: 0, // 新增
  repeat: new THREE.Vector2(2, 1),
  map: this.texture
})

动态改变dashOffset

// 渲染
_animate() {
  requestAnimationFrame(this._animate.bind(this))
  if (this.material) {
    this.material.uniforms.dashOffset.value -= 0.005
  }
}

不使用纹理时 GIF.gif useMap改为1,使用纹理时 GIF.gif

问题来了来,纯色时看着正常,换为纹理时,看得出dashOffset修改的是间隔的位置,纹理的uv并没有动,要想uv动,需要修改源码

4.uv动起来,修改源码,增加参数

参考大佬的修改方式 github.com/maptalks/ma… 下载meshline源码,在源码src中找到material.js,学习上面的方式修改,加入offset参数相关代码,使纹理位置可以改变
( 修改完后可以build出meshline.es.js来使用,或者不压缩直接把meshline文件夹下的文件都放入项目中使用)

image.png

image.png 同步改变间隔跟新增的offset参数,问题解决

// 渲染
_animate() {
  requestAnimationFrame(this._animate.bind(this))
  if (this.material) {
  this.material.uniforms.offset.value.x -= 0.005
  this.material.uniforms.dashOffset.value -= 0.005
  }
}

GIF.gif

5.用几何模型来设置顶点

// 用一个正方体来生成线
const geometry = new THREE.BoxGeometry(100, 100, 100)
line.setGeometry(geometry)

image.png