javascript
<template>
<div ref="container" class="three-container"></div>
</template>
<script setup>
import {ref, onMounted, onBeforeUnmount} from 'vue'
import * as THREE from 'three'
import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
import {GUI} from 'three/addons/libs/lil-gui.module.min.js'
// 着色器代码保持原样
const vertexShader = `
varying vec2 vUv;
void main(){
vUv = vec2(uv.x,uv.y);
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
`
const fragmentShader = `
varying vec2 vUv;
uniform float vTime;
uniform float vPow;
uniform vec3 vColor;
void main(){
float vy = 1.0 - vUv.y;
// 添加呼吸波动计算
float breath = ( sin(vTime * 2.0) * 0.5 + sin(vTime * 3.0) * 0.3) * 0.5 + 0.5; // 生成0-1的波动值
float alpha = pow(vy, vPow) * breath; // 叠加呼吸效果
// 添加颜色脉冲效果
vec3 pulseColor = vColor * (0.8 + breath * 0.2);
gl_FragColor = vec4(pulseColor, alpha);
// 在颜色计算后添加边缘光晕
float edgeGlow = smoothstep(0.7, 1.0, vy);
gl_FragColor.rgb += edgeGlow * breath * 0.5;
}
`
const container = ref(null)
let scene, renderer, camera, orbit, gui
const uniforms = {
vTime: {value: 0.01},
vPow: {value: 2.0},
vColor: {value: new THREE.Color("#ff0000")}
}
// 初始化场景
const initScene = () => {
scene = new THREE.Scene()
renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
renderer.setSize(window.innerWidth, window.innerHeight)
container.value.appendChild(renderer.domElement)
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 2000)
camera.add(new THREE.PointLight())
camera.position.set(10, 10, 10)
scene.add(camera)
orbit = new OrbitControls(camera, renderer.domElement)
orbit.enableDamping = true
scene.add(new THREE.GridHelper(10, 10))
}
// 创建网格
const createMesh = () => {
const planeHeight = 5
const geometry = new THREE.PlaneGeometry(10, planeHeight)
const material = new THREE.ShaderMaterial({
uniforms,
vertexShader,
fragmentShader,
transparent: true,
side: THREE.DoubleSide
})
const createWall = (position, rotation) => {
const mesh = new THREE.Mesh(geometry, material)
mesh.position.set(...position)
if (rotation) mesh.rotation.y = rotation
return mesh
}
scene.add(createWall([5, planeHeight / 2, 0], Math.PI / 2))
scene.add(createWall([-5, planeHeight / 2, 0], Math.PI / 2))
scene.add(createWall([0, planeHeight / 2, -5]))
scene.add(createWall([0, planeHeight / 2, 5]))
}
// 初始化GUI
const initGUI = () => {
gui = new GUI()
gui.add(uniforms.vPow, 'value', 0, 10).name('光栅栏高度')
const colors = {vColor: "#ff0000"}
gui.addColor(colors, 'vColor').name('光栅栏颜色').onChange(v => {
uniforms.vColor.value = new THREE.Color(v)
})
}
// 动画循环
const animate = () => {
requestAnimationFrame(animate)
renderer.render(scene, camera)
orbit.update()
uniforms.vTime.value += 0.01
}
onMounted(() => {
initScene()
createMesh()
initGUI()
animate()
window.addEventListener('resize', onWindowResize)
})
onBeforeUnmount(() => {
// 清理资源
gui.destroy()
scene.traverse(child => {
if (child.material) child.material.dispose()
if (child.geometry) child.geometry.dispose()
})
renderer.dispose()
window.removeEventListener('resize', onWindowResize)
})
// 响应窗口变化
const onWindowResize = () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
</script>
<style scoped>
.three-container {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
padding: 0;
}
</style>