使用vue学习three.js之粒子和粒子系统-从高级几何体中创建粒子系统

1.创建粒子材质

1.1创建材质贴图

创建粒子材质时需要一个纹理贴图,用来给材质的map属性赋值,该贴图的创建过程如下:

// 创建材质贴图
generateSprite () {
    
    
  const canvas = document.createElement('canvas')
  canvas.width = 16
  canvas.height = 16

  const context = canvas.getContext('2d')
  const gradient = context.createRadialGradient(
    canvas.width / 2,
    canvas.height / 2,
    0,
    canvas.width / 2,
    canvas.height / 2,
    canvas.width / 2
  )
  gradient.addColorStop(0, 'rgba(255,255,255,1)')
  gradient.addColorStop(0.2, 'rgba(0,255,255,1)')
  gradient.addColorStop(0.4, 'rgba(0,0,64,1)')
  gradient.addColorStop(1, 'rgba(0,0,0,1)')

  context.fillStyle = gradient
  context.fillRect(0, 0, canvas.width, canvas.height)

  const texture = new THREE.Texture(canvas)
  texture.needsUpdate = true
  return texture
}

1.2使用贴图创建粒子材质

上一步的创建贴图的函数返回一个贴图对象,我们使用它创建THREE.PointCloudMaterial材质

const material = new THREE.PointCloudMaterial({
    
    
  color: 0xffffff,
  size: 3,
  transparent: true,
  blending: THREE.AdditiveBlending,
  map: this.generateSprite()
})

2.创建粒子系统

2.1 创建环面扭结几何体的粒子系统

// 创建环面扭结几何体的粒子系统
createPointCloud () {
    
    
  const geom = this.createGeom()
  const material = new THREE.PointCloudMaterial({
    
    
    color: 0xffffff,
    size: 3,
    transparent: true,
    blending: THREE.AdditiveBlending,
    map: this.generateSprite()
  })

  this.knotMesh = new THREE.PointCloud(geom, material)
  this.knotMesh.sortParticles = true
  this.scene.add(this.knotMesh)
}

2.2 创建粒子系统旋转动画

render () {
    
    
  if (this.properties.rotateSystem) {
    
    
    this.knotMesh.rotation.y += 0.01
  }
  this.renderer.render(this.scene, this.camera)
  requestAnimationFrame(this.render)
}

3.demo效果

在这里插入图片描述

4.demo代码

<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"></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.asParticles" @change="redraw">asParticles</el-checkbox>
        </el-row>
        <el-row>
          <el-checkbox v-model="properties.rotateSystem" @change="redraw">rotateSystem</el-checkbox>
        </el-row>

      </section>

    </div>
  </div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export default {
  components: {},
  data () {
    return {
      properties: {
        radius: {
          name: 'radius',
          value: 13,
          min: 0,
          max: 50,
          step: 0.1
        },
        tube: {
          name: 'tube',
          value: 1.7,
          min: 0,
          max: 8,
          step: 0.1
        },
        radialSegments: {
          name: 'radialSegments',
          value: 156,
          min: 0,
          max: 400,
          step: 1
        },
        tubularSegments: {
          name: 'tubularSegments',
          value: 12,
          min: 0,
          max: 50,
          step: 1
        },
        p: {
          name: 'p',
          value: 5,
          min: 0,
          max: 10,
          step: 1
        },
        q: {
          name: 'q',
          value: 4,
          min: 0,
          max: 10,
          step: 1
        },
        heightScale: {
          name: 'heightScale',
          value: 3.5,
          min: 0,
          max: 10,
          step: 0.1
        },
        asParticles: false,
        rotateSystem: false
      },
      knotMesh: null,
      camera: null,
      scene: null,
      renderer: null,
      controls: null
    }
  },
  mounted () {
    this.init()
  },
  methods: {
    formatTooltip (val) {
      return val
    },
    // 初始化
    init () {
      this.createScene() // 创建场景
      this.createMesh() // 创建网格对象
      this.createCamera() // 创建相机
      this.createRender() // 创建渲染器
      this.createControls() // 创建控件对象
      this.render() // 渲染
    },
    // 创建场景
    createScene () {
      this.scene = new THREE.Scene()
    },
    // 创建材质贴图
    generateSprite () {
      const canvas = document.createElement('canvas')
      canvas.width = 16
      canvas.height = 16

      const context = canvas.getContext('2d')
      const gradient = context.createRadialGradient(
        canvas.width / 2,
        canvas.height / 2,
        0,
        canvas.width / 2,
        canvas.height / 2,
        canvas.width / 2
      )
      gradient.addColorStop(0, 'rgba(255,255,255,1)')
      gradient.addColorStop(0.2, 'rgba(0,255,255,1)')
      gradient.addColorStop(0.4, 'rgba(0,0,64,1)')
      gradient.addColorStop(1, 'rgba(0,0,0,1)')

      context.fillStyle = gradient
      context.fillRect(0, 0, canvas.width, canvas.height)

      const texture = new THREE.Texture(canvas)
      texture.needsUpdate = true
      return texture
    },
    // 创建环面扭结几何体
    createGeom () {
      const geom = new THREE.TorusKnotGeometry(
        this.properties.radius.value,
        this.properties.tube.value,
        Math.round(this.properties.radialSegments.value),
        Math.round(this.properties.tubularSegments.value),
        Math.round(this.properties.p.value),
        Math.round(this.properties.q.value),
        this.properties.heightScale.value
      )
      return geom
    },
    // 创建环面扭结几何体的粒子系统
    createPointCloud () {
      const geom = this.createGeom()
      const material = new THREE.PointCloudMaterial({
        color: 0xffffff,
        size: 3,
        transparent: true,
        blending: THREE.AdditiveBlending,
        map: this.generateSprite()
      })

      this.knotMesh = new THREE.PointCloud(geom, material)
      this.knotMesh.sortParticles = true
      this.scene.add(this.knotMesh)
    },
    // 创建网格对象
    createMesh () {
      const geom = this.createGeom()
      const material = new THREE.MeshNormalMaterial({})
      material.side = THREE.DoubleSide

      this.knotMesh = new THREE.Mesh(geom, material)
      this.scene.add(this.knotMesh)
    },

    // 创建相机
    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()
      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 () {
      this.scene.remove(this.knotMesh)
      if (this.properties.asParticles) {
        this.createPointCloud()
      } else {
        this.createMesh()
      }
    },

    render () {
      if (this.properties.rotateSystem) {
        this.knotMesh.rotation.y += 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>

猜你喜欢

转载自blog.csdn.net/qw8704149/article/details/112254248