Appearance
相机跟随、一个 geom 组合多个 mat
通过group.add把一个geo融合多个MeshBasicMaterial、相机跟随
点击运行
<template>
<div>
<div>通过group.add把一个geo融合多个MeshBasicMaterial、相机跟随</div>
<div @click="onTrigger" class="pointer">点击{{ !isRunning ? '运行' : '关闭' }}</div>
<div v-if="isRunning" id="oneGeoMoreMat" class="stage"></div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, nextTick, onUnmounted } from 'vue'
import {
Scene,
PerspectiveCamera,
WebGLRenderer,
Group,
Color,
AmbientLight,
Mesh,
TubeGeometry,
MeshBasicMaterial,
CatmullRomCurve3,
Vector3,
BufferGeometry,
LineBasicMaterial,
Line,
Texture
} from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
const createMultiMaterialObject = (geometry, materials)=> {
const group = new Group()
for (let i = 0, l = materials.length; i < l; i++) {
group.add(new Mesh(geometry, materials[i]))
}
return group
}
const curve = ref<any>()
const requestID = ref<any>()
const isRunning = ref(false)
let sceneResources
const onTrigger = async () => {
if (!isRunning.value) {
isRunning.value = true
await nextTick()
sceneResources = await initScene()
} else {
isRunning.value = false
destroy()
}
}
const initScene = () => {
const ele = document.getElementById('oneGeoMoreMat') as HTMLElement
const width = Number(window.getComputedStyle(ele).width.split('px')[0])
const height = Number(window.getComputedStyle(ele).height.split('px')[0])
const segments = 64
const radius = 10
const radiusSegments = 10
const closed = false
const step = 0.002
let progress = 0
const scene = new Scene()
const camera: any = new PerspectiveCamera(45, width / height, 1, 10000)
camera.position.set(0, 0, 1000)
camera.rotation.set(0, 0, 1)
scene.add(camera)
const renderer: any = new WebGLRenderer({
antialias: true,
powerPreference: 'high-performance',
alpha: true
})
renderer.setSize(width, height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.setClearColor(new Color('#32373E'), 1)
ele.appendChild(renderer.domElement)
// 添加 OrbitControls
const createOrbitControls = () => {
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.25
controls.enableZoom = true
}
// 光
const createLight = () => {
const ambient = new AmbientLight(0x444444)
scene.add(ambient)
}
const createCurve = () => {
const curve = new CatmullRomCurve3([
new Vector3(160, 160, 150),
new Vector3(130, 130, 130),
new Vector3(100, 100, 120),
new Vector3(50, 50, 50),
new Vector3(0, 0, 0),
])
return curve
}
const createTube = () => {
const tubeGeometry = new TubeGeometry(
curve?.value, //创建样条曲线
segments, //管道的分段数
radius, //管道半径
radiusSegments, //管道截面圆的分段数
closed //是否头尾连接
)
const tubeMat = new MeshBasicMaterial({
color: 0x00f00,
transparent: true,
opacity: 0.3
})
const tubeFrame = new MeshBasicMaterial()
tubeFrame.wireframe = true
const tube = createMultiMaterialObject(tubeGeometry, [tubeMat, tubeFrame])
scene.add(tube)
}
const createLine = () => {
const lineGeometry = new BufferGeometry().setFromPoints(curve?.value?.getPoints(5000))
const lineMaterial = new LineBasicMaterial({
color: 'orange'
})
const line = new Line(lineGeometry, lineMaterial)
scene.add(line)
}
const moveCamera = () => {
if (progress < 1 - step) {
const point = curve?.value?.getPoint(progress)
const pointBox = curve?.value?.getPoint(progress + step)
camera.position.set(point.x, point.y + 5, point.z)
camera.lookAt(pointBox.x, pointBox.y + 5, pointBox.z)
progress += step
} else {
progress = 0
}
}
const runAnimate = () => {
moveCamera()
requestID.value = requestAnimationFrame(runAnimate)
renderer.render(scene, camera)
}
createLight()
curve.value = createCurve()
createTube()
createLine()
runAnimate()
const controls = createOrbitControls()
return {
renderer,
scene,
controls,
}
}
const destroy = () => {
if (sceneResources) {
sceneResources.scene.clear()
sceneResources.scene.traverse((child) => {
if (child.geometry) child.geometry?.dispose()
if (child.material) {
if (child.material.map) child.material.map?.dispose()
child.material?.dispose()
}
})
if (sceneResources.scene.background) {
if (sceneResources.scene.background instanceof Texture) {
sceneResources.scene.background?.dispose()
}
}
sceneResources.renderer?.dispose()
sceneResources.renderer.forceContextLoss()
sceneResources.controls?.dispose()
cancelAnimationFrame(requestID.value)
sceneResources = null
}
curve.value = null
}
onMounted(async() => {
await nextTick()
})
onUnmounted(() => {
destroy()
})
</script>