Use vue to learn advanced geometry of three.js-ParametricGeometry custom geometry

1. Introduction to ParametricGeometry custom geometry

ParametricGeometry can create geometry based on equations. That is, you can create custom geometry through custom functions. When creating a ParametricGeometry custom geometry, you can enter the following parameters:

Attributes have to description
function Yes This parameter is a function, taking u and v values ​​(0~1) as parameters, and the return value is an object of type Vector3 as the coordinates on the graph
slices Yes This attribute defines how many segments the u value should be divided into
stacks Yes This attribute defines how many segments the v value should be divided into
useTris no The default is false, if it is set to true, then the geometry will be created using a triangular patch, if it is false, a quadrilateral patch will be used

2. ParametricGeometry custom geometry usage

2.1 Define function

When creating a ParametricGeometry geometry, the first parameter is a function. Take the first function in the example to show it:

radialWave(u, v, position) {
    
    
  const r = 20

  const x = Math.sin(u) * r
  const z = Math.sin(v / 2) * 2 * r
  const y = (Math.sin(u * 4 * Math.PI) + Math.cos(v * 2 * Math.PI)) * 1.8
  position.set(x, y, z)
  return new THREE.Vector3(x, y, z)
}

2.2 Create ParametricGeometry geometry object

When creating the same as other geometry, use the new keyword to create

//const geom = new THREE.ParametricGeometry(function, slices, stacks[,useTris])
const geom = new THREE.ParametricGeometry(this.radialWave, 120, 160)

2.3 Create a Mesh object with material and add it to the scene

const geom = new THREE.ParametricGeometry(this.radialWave, 120, 160)
// 创建材质
const meshMaterial = new THREE.MeshPhongMaterial({
    
    
  color: 0x3399ff
})
meshMaterial.side = THREE.DoubleSide
// 创建网格对象
this.mesh = new THREE.Mesh(geom, meshMaterial)
this.mesh.position.set(0, 0, 0)
// 网格对象添加到场景中
this.scene.add(this.mesh)

3.demo description

Insert picture description here
Insert picture description here

Insert picture description here
Insert picture description here

As shown in the figure above, this example can create custom four different ParametricGeometry geometries through the drop-down selection on the right

4.demo code

<template>
  <div>
    <div id="container"></div>

    <div class="controls-box">
      <section>
        <el-row>
          <el-col :span="8" class="label-col"><label>drawFunc</label></el-col>
          <el-col :span="16">
            <el-select v-model="drawFunc" placeholder="请选择" @change="redraw">
              <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </el-col>
        </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 {
    
    
      options: [
        {
    
    
          value: 'radialWave',
          label: 'radialWave'
        },
        {
    
    
          value: 'klein',
          label: 'klein'
        },
        {
    
    
          value: 'mobius',
          label: 'mobius'
        },
        {
    
    
          value: 'mobius3d',
          label: 'mobius3d'
        }
      ],
      drawFunc: 'radialWave',
      step: 0.01,
      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()
    },
    radialWave(u, v, position) {
    
    
      const r = 20

      const x = Math.sin(u) * r
      const z = Math.sin(v / 2) * 2 * r
      const y = (Math.sin(u * 4 * Math.PI) + Math.cos(v * 2 * Math.PI)) * 1.8
      position.set(x, y, z)
      return new THREE.Vector3(x, y, z)
    },
    klein(v, u, position) {
    
    
      u *= Math.PI
      v *= 2 * Math.PI

      u = u * 2
      let x, y, z
      if (u < Math.PI) {
    
    
        x =
          3 * Math.cos(u) * (1 + Math.sin(u)) +
          2 * (1 - Math.cos(u) / 2) * Math.cos(u) * Math.cos(v)
        z =
          -8 * Math.sin(u) -
          2 * (1 - Math.cos(u) / 2) * Math.sin(u) * Math.cos(v)
      } else {
    
    
        x =
          3 * Math.cos(u) * (1 + Math.sin(u)) +
          2 * (1 - Math.cos(u) / 2) * Math.cos(v + Math.PI)
        z = -8 * Math.sin(u)
      }

      y = -2 * (1 - Math.cos(u) / 2) * Math.sin(v)

      position.set(x, y, z)
      return new THREE.Vector3(x, y, z)
    },
    mobius(u, t, position) {
    
    
      u = u - 0.5
      const v = 6 * Math.PI * t

      let x, y, z

      const a = 6
      x = Math.cos(v) * (a + u * Math.cos(v / 2))
      y = Math.sin(v) * (a + u * Math.cos(v / 2))
      z = u * Math.sin(v / 2)
      position.set(x, y, z)
      return new THREE.Vector3(x, y, z)
    },

    mobius3d(u, t, position) {
    
    
      u *= Math.PI
      t *= 10 * Math.PI

      u = u * 2
      const phi = u / 2
      const major = 8.25,
        a = 0.325,
        b = 0.65
      let x, y, z
      x = a * Math.cos(t) * Math.cos(phi) - b * Math.sin(t) * Math.sin(phi)
      z = a * Math.cos(t) * Math.sin(phi) + b * Math.sin(t) * Math.cos(phi)
      y = (major + x) * Math.sin(u)
      x = (major + x) * Math.cos(u)
      position.set(x, y, z)
      return new THREE.Vector3(x, y, z)
    },
    // 创建网格模型
    createMesh() {
    
    
      let geom = new THREE.ParametricGeometry(this.klein, 120, 120, false)
      switch (this.drawFunc) {
    
    
        case 'radialWave':
          geom = new THREE.ParametricGeometry(this.radialWave, 120, 160)
          break
        case 'klein':
          geom = new THREE.ParametricGeometry(this.klein, 120, 120, false)
          break

        case 'mobius':
          geom = new THREE.ParametricGeometry(this.mobius, 120, 100)
          break
        case 'mobius3d':
          geom = new THREE.ParametricGeometry(this.mobius3d, 120, 100)
          break
      }

      // 创建材质
      const meshMaterial = new THREE.MeshPhongMaterial({
    
    
        color: 0x3399ff
      })
      meshMaterial.side = THREE.DoubleSide
      // 创建网格对象
      this.mesh = new THREE.Mesh(geom, meshMaterial)
      this.mesh.position.set(0, 0, 0)
      // 网格对象添加到场景中
      this.scene.add(this.mesh)
    },

    // 创建光源
    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(-20, 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() {
    
    
      this.scene.remove(this.mesh)
      this.createMesh()
    },
    render() {
    
    
      //this.mesh.rotation.x = 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;
}
.label-col {
    
    
  padding: 8px 5px;
}
</style>

Guess you like

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