
建筑渐变 ·Building Gradient· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么ShaderMaterial 自定义着色器实现核心视觉效果OrbitControls 相机轨道交互glTF/Draco 模型加载与优化requestAnimationFrame渲染循环与resize自适应效果说明本案例演示建筑渐变效果基于 WebGL 实现「建筑渐变」可视化效果附完整可运行源码核心用到 ShaderMaterial、OrbitControls、glTF/Draco。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念Scene / Camera / WebGLRenderer构成最小渲染闭环大场景可开logarithmicDepthBuffer缓解 Z-fighting。ShaderMaterial通过uniforms 自定义 GLSL 控制逐像素/逐点效果透明粒子常配合depthTest: false。OrbitControls提供轨道旋转/缩放开启enableDamping后需在 animate 中controls.update()。实现步骤搭建 Scene、PerspectiveCamera、WebGLRenderer挂载 canvas 并处理resize异步加载模型 / 3D Tiles / GeoJSON 等资源并加入 scene 或 entities定义 uniforms / onBeforeCompile 或 ShaderMaterial编写 GLSL 与材质参数创建 OrbitControls及 Raycaster 等交互控件若源码包含在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 { GUI } from three/examples/jsm/libs/lil-gui.module.min.jsconst box document.getElementById(box) const scene new THREE.Scene() const camera new THREE.PerspectiveCamera(75, box.clientWidth / box.clientHeight, 0.1, 100000) camera.position.set(1, 1, 1)const renderer new THREE.WebGLRenderer({ antialias: true, alpha: true , logarithmicDepthBuffer: true }) renderer.setSize(box.clientWidth, box.clientHeight) renderer.setPixelRatio(window.devicePixelRatio) box.appendChild(renderer.domElement)new OrbitControls(camera, renderer.domElement)const shaderMaterial new THREE.ShaderMaterial({ vertexShader:uniform vec3 uColorBottom; uniform vec3 uColorTop; uniform float uMinY; uniform float uMaxY; uniform float uTime; varying vec3 vColor; varying vec2 vUv; varying vec3 vNormal; varying vec3 vPosition; void main() { // 设置UV坐标类似原始着色器中的缩放方式 vUv vec2(position.x / 80.0, position.y / 250.0); vNormal normalize(normalMatrix * normal); vPosition position; // 基础渐变效果 float factor smoothstep(uMinY, uMaxY, position.y); vColor mix(uColorBottom, uColorTop, factor); gl_Position projectionMatrixmodelViewMatrixvec4(position, 1.0); }, fragmentShader:uniform vec3 uColorBottom; uniform vec3 uColorTop; uniform vec3 uSweepColor; uniform float uTime; uniform vec3 uLightDir; uniform float uScanWidth; uniform float uScanSoftness; varying vec3 vColor; varying vec2 vUv; varying vec3 vNormal; varying vec3 vPosition; // 随机函数与原始着色器相同 float random(vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); } // 叠加混合函数 - 让扫描颜色与基础渐变叠加 vec3 blendOverlay(vec3 base, vec3 blend) { vec3 result; result.r (base.r 0.5) ? (2.0base.rblend.r) : (1.0 - 2.0(1.0 - base.r)(1.0 - blend.r)); result.g (base.g 0.5) ? (2.0base.gblend.g) : (1.0 - 2.0(1.0 - base.g)(1.0 - blend.g)); result.b (base.b 0.5) ? (2.0base.bblend.b) : (1.0 - 2.0(1.0 - base.b)(1.0 - blend.b)); return result; } void main() { // 基础颜色混合基于Y坐标 vec3 originColor mix(uColorBottom, uColorTop, vUv.y); // 平滑的扫描波动效果 float scanPos fract(uTime * 0.2); // 扫描位置 (0-1循环) float scanLine 1.0 - abs(vUv.y - scanPos) / uScanWidth; scanLine smoothstep(0.0, uScanSoftness, scanLine); // 计算扫描颜色 - 简化处理移除彩虹选项 vec3 finalSweepColor uSweepColor; // 添加轻微的噪声获得更自然的效果 float noise random(vUv10.0 uTime0.1) * 0.03 0.97; // 计算最终扫描颜色 vec3 sweepColor finalSweepColor * noise; // 简化的光照效果 float diffuse dot(normalize(uLightDir), vNormal); diffuse clamp(-diffuse, 0.0, 0.45); // 应用最终效果 - 使用叠加混合而非简单混合 vec3 color originColor; // 叠加混合扫描效果 - 根据扫描线强度 vec3 overlayColor blendOverlay(originColor, sweepColor); color mix(color, overlayColor, scanLine * 0.8); // 添加额外的扫描光亮效果 color sweepColorscanLine0.2; // 添加轻微边缘发光 - 也使用叠加效果 float edge 1.0 - max(0.0, dot(vNormal, vec3(0.0, 0.0, 1.0))); vec3 edgeColor blendOverlay(originColor, sweepColor * edge); color mix(color, edgeColor, edge * 0.3); // 添加发光效果 vec3 emissive vec3(diffuse) * 0.5; color emissive; gl_FragColor vec4(color, 1.0); }, uniforms: { uColorBottom: { value: new THREE.Color(0x6373b6) }, uColorTop: { value: new THREE.Color(0xffffff) }, uSweepColor: { value: new THREE.Color(0xb1ddec) }, uMinY: { value: 0.0 }, uMaxY: { value: 1.0 }, uTime: { value: 0.0 }, uLightDir: { value: new THREE.Vector3(0.5, 0.5, 0.5).normalize() }, uScanWidth: { value: 0.1 }, uScanSoftness: { value: 0.8 } }, transparent: true, })new GLTFLoader().load(FILE_HOST models/whitebuild.glb, (gltf) { const model gltf.scene; const bounds new THREE.Box3().setFromObject(model); shaderMaterial.uniforms.uMinY.value bounds.min.y; shaderMaterial.uniforms.uMaxY.value bounds.max.y; model.traverse(child { if (child.isMesh) child.material shaderMaterial; }); scene.add(model); });function animate() { requestAnimationFrame(animate); shaderMaterial.uniforms.uTime.value 0.01; renderer.render(scene, camera); } animate()window.onresize () { renderer.setSize(box.clientWidth, box.clientHeight) camera.aspect box.clientWidth / box.clientHeight camera.updateProjectionMatrix() } // 扩展GUI控制 - 移除彩虹选项 const gui new GUI() const colorFolder gui.addFolder(颜色设置) colorFolder.addColor(shaderMaterial.uniforms.uColorBottom, value).name(底部颜色) colorFolder.addColor(shaderMaterial.uniforms.uColorTop, value).name(顶部颜色) colorFolder.addColor(shaderMaterial.uniforms.uSweepColor, value).name(扫描颜色)const effectsFolder gui.addFolder(效果设置) effectsFolder.add(shaderMaterial.uniforms.uTime, value, 0.001, 0.05).name(动画速度).onChange(value { animate function() { requestAnimationFrame(animate) shaderMaterial.uniforms.uTime.value value; renderer.render(scene, camera) } }) effectsFolder.add(shaderMaterial.uniforms.uScanWidth, value, 0.001, 0.3).name(扫描宽度) effectsFolder.add(shaderMaterial.uniforms.uScanSoftness, value, 0.1, 1.0).name(扫描柔和度)完整源码GitHub小结本文提供建筑渐变完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库