实现一个漂亮的Three.js 扫光地面 圆形贴图扫光

实现一个漂亮的Three.js 扫光地面 圆形贴图扫光
https://threehub.cn/#/codeMirror?navigation=ThreeJS&classify=shader&id=circleWave

bash 复制代码
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GUI } from 'dat.gui'

const box = document.getElementById('box')

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(50, box.clientWidth / box.clientHeight, 0.1, 1000)

camera.position.set(0, 1, 2)

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true })

renderer.setSize(box.clientWidth, box.clientHeight)

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);

const texture = new THREE.TextureLoader().load(`https://file.threehub.cn/` + 'images/channels/wave.png') 
texture.wrapS = THREE.RepeatWrapping
texture.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.8

        uniform 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);
            col = getScanColor(vPosition, vUv * 10.0, col);
            
            // 计算alpha通道
            float alpha = length(col);  // 根据颜色强度计算透明度
            
            gl_FragColor = vec4(col, alpha);
        }
    `
});

// 创建网格并添加到场景
const mesh = new THREE.Mesh(geometry, material);
mesh.rotation.x = Math.PI / 2
scene.add(mesh);

// 创建dat.GUI
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;

}


/**
 * title: Circle Wave
 * author: Elegant https://z2586300277.github.io/
 * refer:https://shadertoy-playground.netlify.app/entries/#circle-wave 
 */
相关推荐
南囝coding1 分钟前
Vercel 发布 AI Gateway 神器!可一键访问数百个模型,助力零门槛开发 AI 应用
前端·后端
AI大模型8 分钟前
前端学 AI 不用愁!手把手教你用 LangGraph 实现 ReAct 智能体(附完整流程 + 代码)
前端·llm·agent
小红24 分钟前
网络通信基石:从IP地址到子网划分的完整指南
前端·网络协议
一枚前端小能手29 分钟前
🔥 前端储存这点事 - 5个存储方案让你的数据管理更优雅
前端·javascript
willlzq32 分钟前
深入探索Swift的Subscript机制和最佳实践
前端
RockerLau36 分钟前
micro-zoe子应用路由路径污染问题
前端
代码代码快快显灵42 分钟前
Axios的基本知识点以及vue的开发工程(基于大事件)详细解释
前端·javascript·vue.js
文心快码BaiduComate42 分钟前
再获殊荣!文心快码荣膺2025年度优秀软件产品!
前端·后端·代码规范
Mintopia43 分钟前
🚀 Next.js 后端能力扩展:错误处理与 HTTP 状态码规范
前端·javascript·next.js
IT酷盖44 分钟前
Android解决隐藏依赖冲突
android·前端·vue.js