超酷炫的Three.js示例

今天写一个超级酷炫的Three.js示例,以下是文件源代码:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Cool Three.js Page with Stars, Interactions, and Audio</title><style>body { margin: 0; overflow: hidden; background-color: black; }canvas { display: block; }#info {position: absolute;top: 20px;left: 20px;color: white;font-family: Arial, sans-serif;font-size: 20px;z-index: 1;}audio {position: fixed;top: 20px;right: 20px;z-index: 10;width: 300px;}</style>
</head>
<body><div id="info">🚀 Three.js Demo with Stars ✨ + Click/Audio FX</div><audio id="audio" src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" controls autoplay loop></audio><!-- 使用兼容非模块版本的three.js和OrbitControls --><script src="https://cdn.jsdelivr.net/npm/three@0.140.0/build/three.min.js"></script><script src="https://cdn.jsdelivr.net/npm/three@0.140.0/examples/js/controls/OrbitControls.js"></script><script>const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,2000);camera.position.z = 100;const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 注意这里用 THREE.OrbitControls(旧版写法)const controls = new THREE.OrbitControls(camera, renderer.domElement);// 着色器材质代码(glow效果)const vertexShader = `varying vec3 vNormal;void main() {vNormal = normalize(normalMatrix * normal);gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`;const fragmentShader = `varying vec3 vNormal;void main() {float intensity = pow(0.6 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 2.0);gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0) * intensity;}`;const shaderMaterial = new THREE.ShaderMaterial({vertexShader,fragmentShader,blending: THREE.AdditiveBlending,side: THREE.FrontSide,  // 改为 FrontSidetransparent: true});const geometry = new THREE.IcosahedronGeometry(2, 1);const glowGroup = new THREE.Group();for (let i = 0; i < 200; i++) {const mesh = new THREE.Mesh(geometry, shaderMaterial);mesh.scale.multiplyScalar(1.5);mesh.position.set((Math.random() - 0.5) * 400,(Math.random() - 0.5) * 400,(Math.random() - 0.5) * 400);glowGroup.add(mesh);}scene.add(glowGroup);// 星空背景粒子const starGeometry = new THREE.BufferGeometry();const starCount = 5000;const starVertices = [];for (let i = 0; i < starCount; i++) {starVertices.push((Math.random() - 0.5) * 2000);starVertices.push((Math.random() - 0.5) * 2000);starVertices.push((Math.random() - 0.5) * 2000);}starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));const starMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.7 });const starField = new THREE.Points(starGeometry, starMaterial);scene.add(starField);// 灯光const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);scene.add(ambientLight);const pointLight = new THREE.PointLight(0xffffff, 1);camera.add(pointLight);scene.add(camera);// 鼠标点击爆炸效果const raycaster = new THREE.Raycaster();const mouse = new THREE.Vector2();window.addEventListener('click', event => {mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;raycaster.setFromCamera(mouse, camera);const intersects = raycaster.intersectObjects(glowGroup.children);if (intersects.length > 0) {const mesh = intersects[0].object;const explosion = new THREE.Vector3((Math.random() - 0.5) * 100,(Math.random() - 0.5) * 100,(Math.random() - 0.5) * 100);mesh.position.add(explosion);}});// 音频分析器const audio = document.getElementById('audio');const listener = new THREE.AudioListener();camera.add(listener);const sound = new THREE.Audio(listener);const audioLoader = new THREE.AudioLoader();audioLoader.load(audio.src, buffer => {sound.setBuffer(buffer);sound.setLoop(true);sound.setVolume(0.5);sound.play();});const analyser = new THREE.AudioAnalyser(sound, 32);function animate() {requestAnimationFrame(animate);const data = analyser.getAverageFrequency();glowGroup.children.forEach((mesh, i) => {const scale = 1.5 + Math.sin(Date.now() * 0.001 + i) * 0.3 + data / 256;mesh.scale.set(scale, scale, scale);});glowGroup.rotation.y += 0.002;starField.rotation.y += 0.0005;renderer.render(scene, camera);}animate();window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);});</script>
</body>
</html>

一、整体思路

  • 使用非模块版 Three.js(r140)与老写法的 THREE.OrbitControls
  • 场景里有两类物体:
    1. 200 个“发光”小多面体(用自定义 Shader 做到类似辉光的视觉)
    2. 5000 颗 Points 形式的星空粒子
  • 交互:点击“发光体”会被随机“炸开”移动一下。
  • 音频:加载一段 MP3,用 AudioAnalyser 得到频率均值,驱动 200 个发光体按音乐节奏伸缩。
  • 动画:群组及星空做缓慢自转,形成空间流动感。

二、HTML/CSS & 库加载

  • body { overflow: hidden; background:black } 全屏 WebGL 背景。
  • 右上角是 <audio> 播放器。
  • 通过 CDN 引入: three@0.140.0/build/three.min.js three@0.140.0/examples/js/controls/OrbitControls.js 这两者匹配“旧式全局 THREE”写法。

三、场景基础

scene / camera / renderer 标准三件套:

  • 透视相机 FOV 75,near=0.1 / far=2000,Z=100。

  • 抗锯齿渲染器,填满窗口。
    -(可优化)建议:renderer.setPixelRatio(window.devicePixelRatio) 让高 DPI 更清晰(性能充裕时)。

  • 轨道控制器: const controls = new THREE.OrbitControls(camera, renderer.domElement); 允许鼠标旋转/缩放观察。
    想要“丝滑阻尼”,可: controls.enableDamping = true; // 并在动画循环里加 controls.update();

四、自定义 Shader“发光体”

  • 顶点着色器:把法线变换到视图空间,传给片元: vNormal = normalize(normalMatrix * normal);
  • 片元着色器:根据与视线方向(z 轴)夹角计算强度: float intensity = pow(0.6 - dot(vNormal, vec3(0.0,0.0,1.0)), 2.0); gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0) * intensity; 视觉效果:面向镜头的区域更亮,形成“边缘辉光/自发光”的感觉。
  • 材质参数: blending: THREE.AdditiveBlending, side: THREE.FrontSide, transparent: true 使用加色混合以叠加高亮。
    改进建议:加色+透明一般配 depthWrite:false 避免透明深度写入带来的排序伪影: depthWrite: false
  • 几何体:IcosahedronGeometry(2, 1)(二十面体细分一级)。
    批量实例:创建 200 个网格,随机分布在 [-200,200]³(因乘 400 再减半)。
    性能评估:每个约百来个三角形,200 个共 ~几万三角,WebGL 轻松应付;共享同一个 ShaderMaterial,节省材质开销。
    (更进一步)可用 InstancedMesh 把 200 次 draw call 合并为 1 次,但要改为实例化方案。

五、星空粒子

  • BufferGeometry + Float32BufferAttribute 存 5000 个随机顶点。
  • PointsMaterial({ color: 0xffffff, size: 0.7 }) 形成星点。
    (可选)可以加 sizeAttenuation:true(默认就是 true),基于透视缩放更自然;或改用带纹理的点精灵实现更“星星”的感觉。

六、灯光

  • 有环境光和跟随相机的点光,但

    当前两类物体都“几乎不吃光”

    • ShaderMaterial 未开启 lights,着色完全自定义,不受灯光影响;
    • PointsMaterial 也是“自发光色”,不受灯光影响。
  • 因此这两盏灯“视觉贡献≈0”,可留作以后加其他受光物体时使用,也可以删掉减一点场景状态切换。

七、点击“爆炸”交互(Raycaster)

  • 鼠标点击 → 归一化设备坐标 → raycaster.setFromCamera()intersectObjects(glowGroup.children)
  • 若命中,随机向量把该网格位置抖走 50~100 单位。
    (可选)可改成给它一个速度,在 animate 中逐帧衰减,效果会更“物理”。

八、音频与可视化

  • 页面上有 <audio id="audio" controls autoplay loop>,同时 Three.js 里又:

    1. 创建 AudioListener 并挂相机;
    2. AudioLoader.load(audio.src, ...) 再次下载同一路径音频,塞进 THREE.Audioplay()
    3. AudioAnalyser(sound, 32) 获取频域数据均值 getAverageFrequency(),驱动缩放。
  • 潜在问题与改进

    1. 重复播放/重复下载
      页面 <audio> 播放一次、AudioLoader 又播一次,音频可能重叠。
      ➜ 选一种即可。最简方案:复用 <audio> 元素作音源const sound = new THREE.Audio(listener); sound.setMediaElementSource(audioElement); // 直接用 <audio> 的流 const analyser = new THREE.AudioAnalyser(sound, 32); 这样不再重复下载,播放器的播放/暂停也直接影响可视化。

    2. 自动播放策略

      现代浏览器通常禁止带声音的自动播放 。

      • 你虽然写了 autoplaysound.play(),但往往会被拦下,除非用户先有手势(点击等)。
      • 兼容做法:在第一次 pointerdown/click 时执行: const ac = listener.context; if (ac.state === 'suspended') ac.resume(); audioElement.play().catch(()=>{ /* 显示提示或忽略 */ });
    3. 跨域 (CORS)
      若用 AudioLoader加远程 MP3,需要服务器响应 Access-Control-Allow-Origin:*,否则 WebAudio 可能拿不到频谱数据。

      • 复用 <audio crossorigin="anonymous"> + setMediaElementSource 可以更稳。
    4. FFT 分辨率
      new THREE.AudioAnalyser(sound, 32) 频段较少,变化较“钝”。

      • 想要更丰富的律动,可用 128/256,再用 getFrequencyData() 做更细粒度的驱动。
  • 动画里用: const data = analyser.getAverageFrequency(); // 0~255 const scale = 1.5 + Math.sin(time + i) * 0.3 + data / 256; 叠加了“个体相位差的正弦摆动 + 音量项”,既保留群体呼吸感又随音乐起伏。

九、动画循环与窗口自适应

  • requestAnimationFrame(animate) 驱动渲染;群组与星空各自缓慢自转。
  • 监听 resize 更新相机投影与渲染尺寸,属于标准写法。
    (可优化)把 const t = performance.now()*0.001; 放循环开头,少做一次 Date.now()
    若启用 enableDamping,记得每帧 controls.update()

十、数值与视觉小建议

  • Shader 里这句: float intensity = pow(0.6 - dot(vNormal, vec3(0,0,1)), 2.0);dot(...) > 0.6 时底数为负,指数是 2.0(整数),在 GLSL 里通常仍能得到正值,但不同平台精度可能不一致。
    更稳:夹取到非负区间: float intensity = pow(max(0.0, 0.6 - dot(vNormal, vec3(0,0,1))), 2.0);
  • 透明加色材质建议: const shaderMaterial = new THREE.ShaderMaterial({ vertexShader, fragmentShader, blending: THREE.AdditiveBlending, transparent: true, side: THREE.FrontSide, depthWrite: false // ★ 推荐 });
  • 画质/性能开关: renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); 在 4K 屏上能避免过高像素负担。

十一、一步到位的音频改造示例(可直接替换你原来的音频段)

目的:不重复下载,不触发自动播放拦截时的黑屏“无响应”,并让频谱与播放器同步。

<audio id="audio" crossorigin="anonymous"src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"controls loop></audio>
// 音频(替换原有 AudioLoader 部分)
const audioEl = document.getElementById('audio');
const listener = new THREE.AudioListener();
camera.add(listener);const sound = new THREE.Audio(listener);
sound.setMediaElementSource(audioEl); // 直接复用 <audio> 元素
const analyser = new THREE.AudioAnalyser(sound, 128);// 解决自动播放限制:用户首次点击页面时恢复 AudioContext 并尝试播放
let audioInit = false;
function initAudioOnce() {if (audioInit) return;audioInit = true;const ctx = listener.context;if (ctx.state === 'suspended') ctx.resume();audioEl.play().catch(() => {/* 可以提示“请点击播放” */});
}
window.addEventListener('pointerdown', initAudioOnce, { once: true });

你可以直接用复制开头的代码到记事本并另存为.html格式然后在浏览器里跑,实现效果:
在这里插入图片描述
该代码可通过鼠标进行交互。


最后推荐一个超酷的ThreeJS网站:https://ykob.github.io/sketch-threejs/
在这里插入图片描述

重拾编程的乐趣和无尽的探索欲在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/93585.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/93585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始大模型之实现GPT模型

从零开始大模型之从头实现GPT模型1.大语言模型整体架构2 大语言的Transformer模块2.1 层归一化2.2 GELU激活函数2.3 前馈神经网络2.4 快捷连接3 附录3.1 anacondapython环境搭建1.数据预处理&#xff1a;原始数据进行词元化&#xff0c;以及通过&#xff0c;依据词汇表生成ID编…

[1Prompt1Story] 滑动窗口机制 | 图像生成管线 | VAE变分自编码器 | UNet去噪神经网络

链接&#xff1a;https://github.com/byliutao/1Prompt1Story 这个项目是一个基于单个提示生成一致文本到图像的模型。它在ICLR 2025会议上获得了聚焦论文的地位。该项目提供了生成一致图像的代码、Gradio演示代码以及基准测试代码。 主要功能点: 使用单个提示生成一致的文本…

【GitHub开源AI精选】Sitcom-Crafter:北航联合港中文等高校打造的剧情驱动3D动作生成系统

系列篇章&#x1f4a5; No.文章1【GitHub开源AI精选】LLM 驱动的影视解说工具&#xff1a;Narrato AI 一站式高效创作实践2【GitHub开源AI精选】德国比勒费尔德大学TryOffDiff——高保真服装重建的虚拟试穿技术新突破3【GitHub开源AI精选】哈工大&#xff08;深圳&#xff09;…

智和信通全栈式运维平台落地深圳某学院,赋能运维管理提质提效

深圳某学院校园内信息化设备众多&#xff0c;网络环境复杂&#xff0c;使得网络管理工作面临着诸多难题与挑战。为保障校园网络能够稳定、高效地运行&#xff0c;学院亟须构建一套集高效、智能、协同于一体的网络运维平台。 对运维平台的期望包括&#xff1a; 实现校园内教学…

开疆智能Ethernet转ModbusTCP网关连接测联无纸记录仪配置案例

本案例是通过Ethernet转ModbusTCP网关将记录仪数据传送到欧姆龙PLC&#xff0c;具体操作过程如下。欧姆龙PLC配置首先打开主站组态软件“Sysmac Studio”并新建项目。设置PLC的IP地址点击工具-Ethernet/IP连接设置&#xff0c;在弹出的选个框内选择显示EDS库添加网关eds文件开始…

Eureka故障处理大汇总

#作者&#xff1a;Unstopabler 文章目录1. Eureka 服务启动故障处理1.1 端口占用导致启动失败1.2 配置文件错误导致启动失败1.3 依赖冲突与类加载错误2. 服务注册与发现异常2.1 服务无法注册到 Eureka2.2 Eureka 控制台看不到注册的服务2.3 服务注册后立即被剔除3. Eureka 集群…

基于Transformer的机器翻译——模型篇

1.模型结构 本案例整体采用transformer论文中提出的结构&#xff0c;部分设置做了调整。transformer网络结构介绍可参考博客——入门级别的Transformer模型介绍&#xff0c;这里着重介绍其代码实现。 模型的整体结构&#xff0c;包括词嵌入层&#xff0c;位置编码&#xff0c;…

上位机TCP/IP通信协议层常见问题汇总

以太网 TCP 通信是上位机开发中常用的通信方式&#xff0c;西门子 S7 通信、三菱 MC 通信以及 MQTT、OPC UA、Modbus TCP 等都是其典型应用。为帮助大家更好地理解 TCP 通信&#xff0c;我整理了一套常见问题汇总。一、OSI参考模型与TCP/IP参考模型基于TCP/IP的参考模型将协议分…

搭建ktg-mes

项目地址 该安装事项&#xff0c;基于当前最新版 2025年8月16日 之前的版本 下载地址&#xff1a; 后端JAVA 前端VUE 后端安装&#xff1a; 还原数据表 路径&#xff1a;根目录/sql/ry_20210908.sql、根目录/sql/quartz.sql、根目录/doc/实施文档/ktgmes-202505180846.sql.g…

uniapp纯前端绘制商品分享图

效果如图// useMpCustomShareImage.ts interface MpCustomShareImageData {canvasId: stringprice: stringlinePrice: stringgoodsSpecFirmName: stringimage: string }const CANVAS_WIDTH 500 const CANVAS_HEIGHT 400 const BG_IMAGE https://public-scjuchuang.oss-cn-ch…

醋酸镧:看不见的科技助力

虽然我们每天都在使用各种科技产品&#xff0c;但有些关键的化学物质却鲜为人知。醋酸镧&#xff0c;就是这样一种默默为科技进步贡献力量的“幕后英雄”。它不仅是稀土元素镧的一种化合物&#xff0c;还在许多高科技领域中发挥着重要作用。今天&#xff0c;让我们一起来了解这…

苍穹外卖日记

day 1 windows系统启动nginx报错: The system cannot find the path specified 在启动nginx的时候报错&#xff1a; /temp/client_body_temp" failed (3: The system cannot find the path specified) 解决办法&#xff1a; 1.检查nginx的目录是否存在中文 &#xff0c;路…

楼宇自控系统赋能建筑全维度管理,实现环境、安全与能耗全面监管

随着城市化进程加速和绿色建筑理念普及&#xff0c;现代楼宇管理正经历从粗放式运营向精细化管控的转型。楼宇自控系统&#xff08;BAS&#xff09;作为建筑智能化的核心载体&#xff0c;通过物联网、大数据和人工智能技术的深度融合&#xff0c;正在重构建筑管理的全维度框架&…

【HarmonyOS】Window11家庭中文版开启鸿蒙模拟器失败提示未开启Hyoer-V

【HarmonyOS】Window11家庭中文版开启鸿蒙模拟器失败提示未开启Hyoer-V一、问题背景 当鸿蒙模拟器启动时&#xff0c;提示如下图所示&#xff1a;因为Hyper-V 仅在 Windows 11 专业版、企业版和教育版中作为预装功能提供&#xff0c;而家庭版&#xff08;包括中文版&#xff09…

vscode远程服务器出现一直卡在正在打开远程和连接超时解决办法

项目场景&#xff1a; 使用ssh命令或者各种软件进行远程服务器之后&#xff0c;结果等到几分钟之后自动断开连接问题解决。vscode远程服务器一直卡在正在打开远程状态问题解决。问题描述 1.连接超时 2.vscode远程一直卡在正在打开远程...原因分析&#xff1a;需要修改设置超时断…

Maven下载和配置-IDEA使用

目录 一 MAVEN 二 三个仓库 1. 本地仓库&#xff08;Local Repository&#xff09; 2. 私有仓库&#xff08;Private Repository&#xff0c;公司内部仓库&#xff09; 3. 远程仓库&#xff08;Remote Repository&#xff09; 依赖查找流程&#xff08;优先级&#xff09…

Dify实战应用指南(上传需求稿生成测试用例)

一、Dify平台简介 Dify是一款开源的大语言模型&#xff08;LLM&#xff09;应用开发平台&#xff0c;融合了“Define&#xff08;定义&#xff09; Modify&#xff08;修改&#xff09;”的设计理念&#xff0c;通过低代码/无代码的可视化界面降低技术门槛。其核心价值在于帮助…

学习日志35 python

1 Python 列表切片一、切片完整语法列表切片的基本格式&#xff1a; 列表[start:end:step]start&#xff1a;起始索引&#xff08;包含该位置元素&#xff0c;可省略&#xff09;end&#xff1a;结束索引&#xff08;不包含该位置元素&#xff0c;可省略&#xff09;step&#…

Linux -- 文件【下】

目录 一、EXT2文件系统 1、宏观认识 2、块组内部构成 2.1 Data Block 2.2 i节点表(Inode Table) 2.3 块位图&#xff08;Block Bitmap&#xff09; 2.4 inode位图&#xff08;Inode Bitmap&#xff09; 2.5 GDT&#xff08;Group Descriptor Table&#xff09; 2.6 超…

谷歌手机刷机和面具ROOT保姆级别教程

#比较常用的谷歌输入root面具教程,逆向工程师必修课程# 所需工具与材料清单 真机设备 推荐使用 Google Pixel 4 或其他兼容设备&#xff0c;确保硬件支持刷机操作。 ADB 环境配置 通过安装 Android Studio 自动配置 ADB 和 Fastboot 工具。安装完成后&#xff0c;需在系统环境…