当前位置: 首页 > news >正文

Three.js 雪花教程

雪花 ·Snow· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • onBeforeCompile 注入 GLSL 改造内置材质
  • OrbitControls 相机轨道交互
  • BufferGeometry 自定义顶点/索引数据
  • 监听窗口resize同步更新 camera 与 renderer

效果说明

本案例演示雪花效果:基于 WebGL 实现「雪花」可视化效果,附完整可运行源码;核心用到 onBeforeCompile、OrbitControls、BufferGeometry。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • Scene / Camera / WebGLRenderer构成最小渲染闭环;大场景可开logarithmicDepthBuffer缓解 Z-fighting。
  • onBeforeCompile在 Three 拼好内置 shader 后替换#include片段,适合在 PBR 材质上叠加大屏特效。
  • OrbitControls提供轨道旋转/缩放;开启enableDamping后需在 animate 中controls.update()

实现步骤

  • 搭建 Scene、PerspectiveCamera、WebGLRenderer,挂载 canvas 并处理resize
  • 定义 uniforms / onBeforeCompile 或 ShaderMaterial,编写 GLSL 与材质参数
  • 创建 OrbitControls(及 Raycaster 等交互控件,若源码包含)
  • requestAnimationFrame循环中更新状态并 render(Cesium 为viewer.render或自动渲染)
  • 代码要点

    import * as THREE from "three";

    import { OrbitControls } from "three/addons/controls/OrbitControls.js";

    let scene = new THREE.Scene(); let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 1000); camera.position.set(0, 0, 7); let renderer = new THREE.WebGLRenderer(); renderer.setSize(innerWidth, innerHeight); document.body.appendChild(renderer.domElement); window.addEventListener("resize", event => { camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth, innerHeight); })

    new OrbitControls(camera, renderer.domElement);

    let gu = { time: {value: 0} }

    class Flakes extends THREE.Points{ constructor(gu){ let flakeData = []; let g = new THREE.BufferGeometry().setFromPoints(new Array(1000).fill().map(_ => { flakeData.push(((Math.random() < 0.5) ? -1 : 1), 0, 0, 0); return new THREE.Vector3().random().subScalar(0.5).multiplyScalar(10) })); g.setAttribute("flakeData", new THREE.Float32BufferAttribute(flakeData, 4)); let m = new THREE.PointsMaterial({ color: 0xfbec5d, size: 0.75, onBeforeCompile: shader => { shader.uniforms.time = gu.time; shader.vertexShader =uniform float time; attribute vec4 flakeData; varying vec4 vFlakeData; varying float vId; ${shader.vertexShader}.replace(#include,#include vId = float(gl_VertexID); vFlakeData = flakeData;

    vec3 p = vec3(position);

    transformed.y = 5. - mod(p.y + time * 0.5 - 5., 10.);

    );

    shader.fragmentShader =uniform float time; varying vec4 vFlakeData; varying float vId;

    // 2D Random float random (in vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898,78.233)))

    • 43758.5453123);
    }

    // 2D Noise based on Morgan McGuire @morgan3d // https://www.shadertoy.com/view/4dS3Wd float noise (in vec2 st) { vec2 i = floor(st); vec2 f = fract(st);

    // Four corners in 2D of a tile float a = random(i); float b = random(i + vec2(1.0, 0.0)); float c = random(i + vec2(0.0, 1.0)); float d = random(i + vec2(1.0, 1.0));

    // Smooth Interpolation

    // Cubic Hermine Curve. Same as SmoothStep() vec2 u = ff(3.0-2.0*f); // u = smoothstep(0.,1.,f);

    // Mix 4 coorners percentages return mix(a, b, u.x) + (c - a)u.y(1.0 - u.x) + (d - b)u.xu.y; }

    mat2 rot(float a){ float c = cos(a); float s = sin(a); return mat2(c, -s, s, c); }

    ${shader.fragmentShader}.replace(#include,#include vec2 baseUv = gl_PointCoord.xy - 0.5; vec2 uv = rot(mod(vId + (timevFlakeData.x), PI2))(baseUv * 10.); float a = atan(uv.y, uv.x) + PI; float r = length(uv); float aStep = PI / 3.; float aPart = abs(mod(a, aStep) - (0.5 * aStep)); vec2 suv = vec2(cos(aPart), sin(aPart)) * r - vec2(time, 0.); float n = noise(suv + vId); //n = pow(n, 2.); if (length(baseUv) > 0.5 || n < 0.5) discard;).replace(#include,#include diffuseColor.rgb = mix(diffuseColor.rgb, vec3(0.875, 0, 0), smoothstep(1., 0.5, n));); console.log(shader.fragmentShader); } }) super(g, m); } } let flakes = new Flakes(gu); scene.add(flakes);

    let clock = new THREE.Clock();

    renderer.setAnimationLoop(() => { gu.time.value = clock.getElapsedTime() * 0.5; renderer.render(scene, camera); });

    完整源码:GitHub

    小结

    • 本文提供雪花完整 Three.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库
http://www.jsqmd.com/news/1127146/

相关文章:

  • 学类加载器的时候,我被“谁加载谁“绕了好几天
  • VIISP 模块培训总结:从接口协议到实战排查
  • 基于Python与OpenCV的围棋棋盘定位:从颜色特征到轮廓提取的实战解析
  • Java社工密码生成器部署与实战:从环境配置到高命中字典生成
  • 90天从AI小白到能做项目的人,真的可能吗?
  • Robot Framework面试指南:从基础到高级的29道核心问题解析
  • 使用OpenSSL生成本地证书https+nginx
  • HarmonyOS宠物邻里实战第5篇:通知中心、已读同步与AppStorage刷新闭环
  • 塑胶件自攻螺丝设计指南:M1.2-M4.0 尺寸公式与 8 次拆装极限解析
  • PyTorch GPU Tensor转NumPy:4步解决CUDA数据到CPU的跨设备转换
  • 【小白也能轻松玩转龙虾】虾壳云一键部署 OpenClaw v2.7.9,离线本地 AI 搭建教学(附最新安装包)
  • 0704晨间日记
  • mcpsnoop:实时显示AI客户端与MCP服务器调用,功能强大且安装便捷!
  • 2026 年人类网络访问量首被机器超越,AI 时代如何守护真实人际连接?
  • Supabase:基于 Postgres 的开发平台,功能丰富,支持多语言开发
  • 【HarmonyOS 7开发者前瞻】03 HarmonyOS 7 API 26 新 API 找不到,先用 5 层状态判断能力可用性
  • AI 短视频运营时代,视频号作品与评论数据为何成为核心决策资产?
  • 2026年Claude Mythos预览版发布后:6月严重网络漏洞披露数达发布前3.5倍
  • 可解释AI安全:针对SHAP/LIME的对抗攻击与鲁棒防御实践
  • 网络通信基础:IP协议、ARP协议、DHCP
  • 2026-2030工业堆焊行业发展趋势:从维修辅业到智造核心工艺
  • OpenSpec 入门详解:核心基础概念与核心作用全梳理
  • Awesome OpenClaw Skills:4000+ 中文 AI 技能库
  • 2026年无锡细胞存储市场格局观察:四家企业的传承脉络与业务分野
  • 百考通AI高质量开题报告开启智慧新篇章
  • 【小白也能轻松玩转龙虾】虾壳云一键部署实操,图文讲解 OpenClaw v2.7.9 完整安装流程(附最新安装包)
  • 5分钟快速上手:Wallpaper Engine资源提取神器RePKG完全指南
  • 射阳冰箱维修怎么找靠谱
  • 孤能子视角:三十六计之暗度陈仓——双通道并行
  • 宜春口腔机构甄选与避坑实测指南