Three.js 城市光影教程

📅 2026/7/5 13:24:06
Three.js 城市光影教程
城市光影 ·City Light· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么onBeforeCompile 注入 GLSL 改造内置材质OrbitControls 相机轨道交互FBXLoader 加载 FBX 城市/角色模型glTF/Draco 模型加载与优化GSAP 时间轴与补间动画效果说明本案例演示城市光影效果建筑/模型随进度生长叠加扫光、扩散波等大屏 Shader 特效核心用到 onBeforeCompile、OrbitControls、FBXLoader。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念Scene / Camera / WebGLRenderer构成最小渲染闭环大场景可开logarithmicDepthBuffer缓解 Z-fighting。onBeforeCompile在 Three 拼好内置 shader 后替换#include片段适合在 PBR 材质上叠加大屏特效。OrbitControls提供轨道旋转/缩放开启enableDamping后需在 animate 中controls.update()。实现步骤搭建 Scene、PerspectiveCamera、WebGLRenderer挂载 canvas 并处理resize异步加载模型 / 3D Tiles / GeoJSON 等资源并加入 scene 或 entities定义 uniforms / onBeforeCompile 或 ShaderMaterial编写 GLSL 与材质参数创建 OrbitControls及 Raycaster 等交互控件若源码包含在定时器或 GSAP 时间轴中更新 uniform / 变换驱动特效播放在requestAnimationFrame循环中更新状态并 renderCesium 为viewer.render或自动渲染代码要点import * as THREE from threeimport { OrbitControls } from three/examples/jsm/controls/OrbitControls.js import { GLTFLoader } from three/examples/jsm/loaders/GLTFLoader.js import { DRACOLoader } from three/examples/jsm/loaders/DRACOLoader.js import { FBXLoader } from three/examples/jsm/loaders/FBXLoader.js import gsap from gsapconst size { width: window.innerWidth, height: window.innerHeight } const scene new THREE.Scene() const camera new THREE.PerspectiveCamera(45, size.width / size.height, 0.1, 1000) camera.position.set(5, 5, 5) const renderer new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true }) renderer.setSize(size.width, size.height) renderer.setPixelRatio(window.devicePixelRatio * 1.5) document.body.appendChild(renderer.domElement) new OrbitControls(camera, renderer.domElement) renderer.setAnimationLoop(() renderer.render(scene, camera))//加载gltf const dracoLoader new DRACOLoader() dracoLoader.setDecoderPath(FILE_HOST js/three/draco/) dracoLoader.preload() const loader new GLTFLoader() loader.setDRACOLoader(dracoLoader) loader.load(FILE_HOST models/glb/build.glb, (gltf) { const model gltf.scene model.scale.set(0.01, 0.01, 0.01) scene.add(model) model.traverse((child) { if (child instanceof THREE.Mesh) { child.material.dispose() child.material modifyMaterial() } }) })// fbx new FBXLoader().load(HOST /files/model/city.FBX, (object3d) { scene.add(object3d) object3d.scale.set(0.001, 0.001, 0.001) object3d.traverse((child) { if (child instanceof THREE.Mesh) { child.material.dispose() child.material modifyMaterial() } }) })//混合着色 function modifyMaterial() { const material new THREE.MeshBasicMaterial({ color: #28A1CC, // wireframe: true, opacity: 0.2, transparent: true, side: THREE.DoubleSide }) material.onBeforeCompile (shader) { shader.fragmentShader shader.fragmentShader.replace(/#include /,#include //替换标记) addColor(shader) addWave(shader) addLightLine(shader) addToTopLine(shader) } return material }// function addColor(shader) { // 获取物体的高度差 const uHeight 1200shader.uniforms.uTopColor { value: new THREE.Color(#e9eaef) } shader.uniforms.uHeight { value: uHeight }shader.vertexShader shader.vertexShader.replace( #include ,#include varying vec3 vPosition;)shader.vertexShader shader.vertexShader.replace( #include ,#include vPosition position;)shader.fragmentShader shader.fragmentShader.replace( #include ,#includeuniform vec3 uTopColor; uniform float uHeight; varying vec3 vPosition;) shader.fragmentShader shader.fragmentShader.replace( //替换标记,vec4 distGradColor gl_FragColor;// 设置混合的百分比 float gradMix vPosition.y/uHeight; // 计算出混合颜色 vec3 gradMixColor mix(distGradColor.xyz,uTopColor,gradMix); gl_FragColor vec4(gradMixColor,1); //替换标记) }/** *添加扩散波 / function addWave(shader) { // 设置扩散的中心点 shader.uniforms.uSpreadCenter { value: new THREE.Vector2(0, 0) } // 扩散的时间 shader.uniforms.uSpreadTime { value: -2000 } // 设置条带的宽度 shader.uniforms.uSpreadWidth { value: 40 }shader.fragmentShader shader.fragmentShader.replace( #include ,#includeuniform vec2 uSpreadCenter; uniform float uSpreadTime; uniform float uSpreadWidth;)shader.fragmentShader shader.fragmentShader.replace( //替换标记,float spreadRadius distance(vPosition.xz,uSpreadCenter); // 扩散范围的函数 float spreadIndex -(spreadRadius-uSpreadTime)*(spreadRadius-uSpreadTime)uSpreadWidth;if(spreadIndex0.0){ gl_FragColor mix(gl_FragColor,vec4(1,1,1,1),spreadIndex/uSpreadWidth); }//替换标记)gsap.to(shader.uniforms.uSpreadTime, { value: 800, duration: 3, ease: none, repeat: -1 }) }function addLightLine(shader) { // 扩散的时间 shader.uniforms.uLightLineTime { value: -1500 } // 设置条带的宽度 shader.uniforms.uLightLineWidth { value: 200 }shader.fragmentShader shader.fragmentShader.replace( #include ,#includeuniform float uLightLineTime; uniform float uLightLineWidth;)shader.fragmentShader shader.fragmentShader.replace( //替换标记,float LightLineMix -(vPosition.xvPosition.z-uLightLineTime)*(vPosition.xvPosition.z-uLightLineTime)uLightLineWidth;if(LightLineMix0.0){ gl_FragColor mix(gl_FragColor,vec4(0.8,1.0,1.0,1),LightLineMix /uLightLineWidth);}//替换标记)gsap.to(shader.uniforms.uLightLineTime, { value: 1500, duration: 5, ease: none, repeat: -1 }) }function addToTopLine(shader) { // 扩散的时间 shader.uniforms.uToTopTime { value: 0 } // 设置条带的宽度 shader.uniforms.uToTopWidth { value: 40 }shader.fragmentShader shader.fragmentShader.replace( #include ,#includeuniform float uToTopTime; uniform float uToTopWidth;)shader.fragmentShader shader.fragmentShader.replace( //替换标记,float ToTopMix -(vPosition.y-uToTopTime)*(vPosition.y-uToTopTime)uToTopWidth;if(ToTopMix0.0){ gl_FragColor mix(gl_FragColor,vec4(0.8,0.8,1,1),ToTopMix /uToTopWidth);}//替换标记)gsap.to(shader.uniforms.uToTopTime, { value: 500, duration: 3, ease: none, repeat: -1 }) }完整源码GitHub小结本文提供城市光影完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库