Use vue to learn advanced geometry of three.js-tube geometry TubeGeometry

Introduction and use of TubeGeometry

1. Introduction to TubeGeometry

TubeGeometry can stretch a tube along a three-dimensional spline curve. The path can be defined by specifying the vertices. When creating a TubeGeometry pipe geometry, you can enter the following parameters:

Attributes have to description
path Yes This attribute uses a THREE.SplineCurve object to specify the path that the pipeline follows
segments no This attribute specifies the number of segments of the pipeline. The longer the path, the greater the number of segments specified. The default value is 64
radius no This attribute specifies the radius of the pipe, the default value is 1
radiusSegments no This attribute specifies the number of sections of the pipe section. The more the number of sections, the smoother the pipe will look. The default value is 8.
closed no This attribute is used to set whether the pipeline is connected at the end, the default value is false

The way to create the TubeGeometry is as follows:

const points = []
//随机生成点
for (let i = 0; i < this.properties.numberOfPoints.value; i++) {
    
    
  const randomX = -20 + Math.round(Math.random() * 50)
  const randomY = -15 + Math.round(Math.random() * 40)
  const randomZ = -20 + Math.round(Math.random() * 40)

  points.push(new THREE.Vector3(randomX, randomY, randomZ))
}
// 创建管状几何体
const geom = new THREE.TubeGeometry(
  new THREE.SplineCurve3(points), //创建样条曲线
  segments,//管道的分段数
  radius,//管道半径
  radiusSegments,//管道截面圆的分段数
  closed//是否头尾连接
)

2.demo description

Insert picture description here

As shown above, this example supports the following functions

  1. Use the numOfPoints property to adjust the number of points needed to create the path of the tubular geometry
  2. Specify the number of segments of the pipeline through the segments attribute
  3. Specify the radius of the pipe through the radius attribute
  4. Specify the number of segments of the pipe section circle through the radiusSegments property
  5. Set whether the tubular geometry is connected to the end through the closed attribute

3.demo code

<template>
  <div>
    <div id="container"></div>
    <div class="controls-box">
      <section>
        <el-row>
          <div v-for="(item,key) in properties" :key="key">
            <div v-if="item&&item.name!=undefined">
              <el-col :span="8">
                <span class="vertice-span">{
    
    {
    
    item.name}}</span>
              </el-col>
              <el-col :span="13">
                <el-slider v-model="item.value" :min="item.min" :max="item.max" :step="item.step" :format-tooltip="formatTooltip" @change="redraw(item.name)"></el-slider>
              </el-col>
              <el-col :span="3">
                <span class="vertice-span">{
    
    {
    
    item.value}}</span>
              </el-col>
            </div>
          </div>
        </el-row>
        <el-row>
          <el-checkbox v-model="properties.closed" @change="redraw">closed</el-checkbox>
        </el-row>
      </section>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three'
import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import {
    
     SceneUtils } from 'three/examples/jsm/utils/SceneUtils.js'
export default {
    
    
  data() {
    
    
    return {
    
    
      properties: {
    
    
        numberOfPoints: {
    
    
          name: 'numberOfPoints',
          value: 7,
          min: 2,
          max: 15,
          step: 1
        },
        segments: {
    
    
          name: 'segments',
          value: 120,
          min: 0,
          max: 200,
          step: 1
        },
        radius: {
    
    
          name: 'radius',
          value: 2,
          min: 0,
          max: 20,
          step: 0.1
        },
        radiusSegments: {
    
    
          name: 'radiusSegments',
          value: 22,
          min: 0,
          max: 100,
          step: 1
        },
        closed: false
      },
      points: [],
      isNeedChangePoints: true,
      step: 1,
      spGroup: null,
      mesh: null,
      camera: null,
      scene: null,
      renderer: null,
      controls: null
    }
  },
  mounted() {
    
    
    this.init()
  },
  methods: {
    
    
    formatTooltip(val) {
    
    
      return val
    },
    // 初始化
    init() {
    
    
      this.createScene() // 创建场景
      this.createMesh() // 创建网格模型
      this.createLight() // 创建光源
      this.createCamera() // 创建相机
      this.createRender() // 创建渲染器
      this.createControls() // 创建控件对象
      this.render() // 渲染
    },
    // 创建场景
    createScene() {
    
    
      this.scene = new THREE.Scene()
    },

    // 创建网格模型
    createMesh() {
    
    
      if (this.isNeedChangePoints) {
    
    
        this.generatePoints() //初始化点集
      }
      this.isNeedChangePoints = false
      // 创建管状几何体
      const geom = new THREE.TubeGeometry(
        new THREE.SplineCurve3(this.points), //创建样条曲线
        this.properties.segments.value,
        this.properties.radius.value,
        this.properties.radiusSegments.value,
        this.properties.closed
      )
      // 创建材质
      const meshMaterial = new THREE.MeshBasicMaterial({
    
    
        color: 0x00ff00,
        transparent: true,
        opacity: 0.2
      })
      const wireFrameMat = new THREE.MeshBasicMaterial({
    
     wireframe: true })

      // 创建网格对象
      this.mesh = SceneUtils.createMultiMaterialObject(geom, [
        meshMaterial,
        wireFrameMat
      ])
      // 网格对象添加到场景中
      this.scene.add(this.mesh)
    },
    generatePoints() {
    
    
      this.points = []
      //生成指定数量的点
      for (let i = 0; i < this.properties.numberOfPoints.value; i++) {
    
    
        const randomX = -20 + Math.round(Math.random() * 50)
        const randomY = -15 + Math.round(Math.random() * 40)
        const randomZ = -20 + Math.round(Math.random() * 40)

        this.points.push(new THREE.Vector3(randomX, randomY, randomZ))
      }
      this.spGroup = new THREE.Group()
      const material = new THREE.MeshBasicMaterial({
    
    
        color: 0xff0000,
        transparent: false
      })
      // 根据每个点创建mesh对象并添加到场景
      this.points.forEach(point => {
    
    
        const spGeom = new THREE.SphereGeometry(0.2)
        const spMesh = new THREE.Mesh(spGeom, material)
        spMesh.position.copy(point)
        this.spGroup.add(spMesh)
      })
      // 点集添加到场景
      this.scene.add(this.spGroup)
      return this.points
    },

    // 创建光源
    createLight() {
    
    
      // 环境光
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.1) // 创建环境光
      this.scene.add(ambientLight) // 将环境光添加到场景

      const spotLight = new THREE.SpotLight(0xffffff) // 创建聚光灯
      spotLight.position.set(-40, 60, -10)
      spotLight.castShadow = true
      this.scene.add(spotLight)
    },
    // 创建相机
    createCamera() {
    
    
      const element = document.getElementById('container')
      const width = element.clientWidth // 窗口宽度
      const height = element.clientHeight // 窗口高度
      const k = width / height // 窗口宽高比
      // PerspectiveCamera( fov, aspect, near, far )
      this.camera = new THREE.PerspectiveCamera(35, k, 0.1, 1000)
      this.camera.position.set(-80, 60, 40) // 设置相机位置

      this.camera.lookAt(new THREE.Vector3(10, 0, 0)) // 设置相机方向
      this.scene.add(this.camera)
    },
    // 创建渲染器
    createRender() {
    
    
      const element = document.getElementById('container')
      this.renderer = new THREE.WebGLRenderer({
    
     antialias: true, alpha: true })
      this.renderer.setSize(element.clientWidth, element.clientHeight) // 设置渲染区域尺寸
      this.renderer.shadowMap.enabled = true // 显示阴影
      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
      this.renderer.setClearColor(0x3f3f3f, 1) // 设置背景颜色
      element.appendChild(this.renderer.domElement)
    },

    // 重新绘制
    redraw(attributeName) {
    
    
      debugger
      if (attributeName === 'numberOfPoints') {
    
    
        this.scene.remove(this.spGroup)
        this.generatePoints()
      }

      this.scene.remove(this.mesh)
      this.createMesh()
    },
    render() {
    
    
      this.spGroup.rotation.y = this.step
      this.mesh.rotation.y = this.step += 0.01
      this.renderer.render(this.scene, this.camera)
      requestAnimationFrame(this.render)
    },
    // 创建控件对象
    createControls() {
    
    
      this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    }
  }
}
</script>
<style>
#container {
    
    
  position: absolute;
  width: 100%;
  height: 100%;
}
.controls-box {
    
    
  position: absolute;
  right: 5px;
  top: 5px;
  width: 300px;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #c3c3c3;
}
.vertice-span {
    
    
  line-height: 38px;
  padding: 0 2px 0 10px;
}
</style>

Guess you like

Origin blog.csdn.net/qw8704149/article/details/110293461