Introduction and use of 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
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>