波紋效果,在智慧城市可視化開發中經常用到,這里分享一個比較好玩的案例
以下是詳細的步驟:
初始化部分:設置 Three.js 環境,包括場景、相機、渲染器和控制器
幾何體和紋理:創建平面幾何體并加載波紋紋理
著色器材質:使用自定義著色器實現波紋效果,包括頂點著色器和片段著色器
波紋算法:核心是 circleWave 函數,通過計算距離和時間來生成向外擴散的波紋
顏色混合:根據波紋強度混合兩種顏色,形成視覺層次感
交互控制:使用 GUI 工具允許用戶實時調整波紋顏色
動畫循環:通過不斷更新時間變量來驅動波紋動畫
瀏覽地址:https://aivogenx.github.io/threejs-cesium-webgpu-vue-js/#/codeMirror?navigation=Three.js%E6%A1%88%E4%BE%8B&classify=shader&id=circleWave
代碼
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Three.js波紋效果演示</title><!-- 這里可以添加CSS樣式文件 -->
</head><body><!-- 導入模塊映射,配置Three.js依賴路徑 --><script type="importmap">{"imports": {"three": "./threejs/build/three.module.js","three/addons/": "./threejs/examples/jsm/"}}</script><script type="module">// 導入Three.js核心庫和輔助工具import * as THREE from 'three'import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { GUI } from "three/addons/libs/lil-gui.module.min.js";// 獲取渲染容器,使用整個body元素const box = document.body;// 創建Three.js場景,作為所有3D對象的容器const scene = new THREE.Scene()// 創建透視相機,參數依次為:視野角度、寬高比、近裁剪面、遠裁剪面const camera = new THREE.PerspectiveCamera(50, box.clientWidth / box.clientHeight, 0.1, 1000)// 設置相機位置,從(1,1,1)的位置觀察場景camera.position.set(1, 1, 1)// 創建WebGL渲染器,啟用抗鋸齒、透明背景和對數深度緩沖區const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true })// 設置渲染器尺寸為容器大小renderer.setSize(box.clientWidth, box.clientHeight)// 將渲染器的DOM元素添加到頁面中box.appendChild(renderer.domElement)// 創建軌道控制器,允許用戶通過鼠標交互旋轉和縮放場景const controls = new OrbitControls(camera, renderer.domElement)// 啟用阻尼效果,使相機移動更平滑controls.enableDamping = true// 窗口大小變化時的響應函數,保持渲染內容適配窗口window.onresize = () => {// 更新渲染器尺寸renderer.setSize(box.clientWidth, box.clientHeight)// 更新相機的寬高比camera.aspect = box.clientWidth / box.clientHeight// 更新相機投影矩陣camera.updateProjectionMatrix()}// 創建平面幾何體,作為波紋效果的載體const geometry = new THREE.PlaneGeometry(2, 2);// 加載波紋紋理,這是實現波紋效果的基礎紋理// 注意:FILE_HOST變量需要在實際使用時替換為真實的資源路徑const texture = new THREE.TextureLoader().load(FILE_HOST + 'images/channels/wave.png')// 設置紋理在水平和垂直方向上重復texture.wrapS = THREE.RepeatWrappingtexture.wrapT = THREE.RepeatWrapping// 創建自定義著色器材質,實現波紋效果的核心部分const material = new THREE.ShaderMaterial({side: THREE.DoubleSide, // 雙面渲染transparent: true, // 啟用透明度blending: THREE.AdditiveBlending, // 添加混合模式讓效果更亮uniforms: {uTime: { value: 0.0 }, // 時間變量,控制波紋動畫uScanTex: { value: texture }, // 波紋紋理uScanColor: { value: new THREE.Color(0x00ffff) }, // 主要掃描顏色uScanColorDark: { value: new THREE.Color(0x0088ff) } // 暗部掃描顏色},// 頂點著色器:處理幾何體頂點,傳遞紋理坐標和位置信息給片段著色器vertexShader: `varying vec2 vUv; // 傳遞給片段著色器的紋理坐標varying vec3 vPosition; // 傳遞給片段著色器的頂點位置void main() {vUv = uv;vPosition = position;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`,// 片段著色器:計算每個像素的顏色,實現波紋效果的核心邏輯fragmentShader: `// 波紋原點和波紋擴展速度常量#define uScanOrigin vec3(0.0, 0.0, 0.0)#define uScanWaveRatio1 3.2#define uScanWaveRatio2 2.8uniform float uTime; // 時間變量uniform sampler2D uScanTex; // 波紋紋理uniform vec3 uScanColor; // 波紋主顏色uniform vec3 uScanColorDark; // 波紋暗部顏色varying vec2 vUv; // 從頂點著色器傳遞的紋理坐標varying vec3 vPosition; // 從頂點著色器傳遞的頂點位置// 計算圓形波紋效果float circleWave(vec3 p, vec3 origin, float distRatio) {float t = uTime;float dist = distance(p, origin) * distRatio; // 計算到原點的距離并應用縮放float radialMove = fract(dist - t); // 創建隨時間移動的波紋float fadeOutMask = 1.0 - smoothstep(1.0, 3.0, dist); // 波紋隨距離衰減radialMove *= fadeOutMask;float cutInitialMask = 1.0 - step(t, dist); // 控制波紋從中心向外擴展return radialMove * cutInitialMask;}// 根據位置和紋理計算波紋顏色vec3 getScanColor(vec3 worldPos, vec2 uv, vec3 col) {// 從紋理采樣,獲取波紋基礎圖案float scanMask = texture2D(uScanTex, uv).r;// 計算兩種不同速度的波紋效果,形成層次感float cw = circleWave(worldPos, uScanOrigin, uScanWaveRatio1);float cw2 = circleWave(worldPos, uScanOrigin, uScanWaveRatio2);// 為第一種波紋創建遮罩,控制波紋的顯示范圍和強度float mask1 = smoothstep(0.3, 0.0, 1.0 - cw);mask1 *= (1.0 + scanMask * 0.7); // 結合紋理增強效果// 為第二種波紋創建遮罩float mask2 = smoothstep(0.07, 0.0, 1.0 - cw2) * 0.8;mask1 += mask2;// 創建波紋邊緣高亮效果float mask3 = smoothstep(0.09, 0.0, 1.0 - cw) * 1.5;mask1 += mask3;// 根據遮罩強度混合兩種顏色,形成波紋的顏色變化vec3 scanCol = mix(uScanColorDark, uScanColor, mask1);return scanCol * mask1; // 返回最終的波紋顏色}void main() {vec3 col = vec3(0.0);// 計算波紋顏色,紋理坐標乘以10.0增強波紋密度col = getScanColor(vPosition, vUv * 10.0, col);// 根據顏色強度計算透明度,使波紋邊緣更柔和float alpha = length(col);gl_FragColor = vec4(col, alpha);}`});// 創建網格對象,將幾何體和材質組合,并添加到場景中const mesh = new THREE.Mesh(geometry, material);// 將平面旋轉90度,使其水平放置mesh.rotation.x = Math.PI / 2scene.add(mesh);// 創建圖形界面控制器,允許用戶交互調整參數const gui = new GUI()const params = {uScanColor: '#00ffff', // 波紋主顏色uScanColorDark: '#0088ff' // 波紋暗部顏色}// 添加顏色控制器,允許用戶修改波紋主顏色gui.addColor(params, 'uScanColor').onChange((value) => {material.uniforms.uScanColor.value.set(value)})// 添加顏色控制器,允許用戶修改波紋暗部顏色gui.addColor(params, 'uScanColorDark').onChange((value) => {material.uniforms.uScanColorDark.value.set(value)})// 動畫循環函數,驅動整個場景的渲染和更新animate()function animate() {// 請求下一幀動畫requestAnimationFrame(animate)// 更新控制器狀態controls.update()// 渲染場景renderer.render(scene, camera)// 更新時間變量,驅動波紋動畫material.uniforms.uTime.value += 0.005;}</script>
</body></html>