news 2026/7/5 13:22:18

Three.js 建筑渐变教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Three.js 建筑渐变教程

建筑渐变 ·Building Gradient· ▶ 在线运行案例

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

你将学到什么

  • 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循环中更新状态并 render(Cesium 为viewer.render或自动渲染)
  • 代码要点

    import * as THREE from 'three'

    import { 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.js'

    const 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 开源仓库
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 13:22:12

hsb fpga/ 目录分析

1.主要结构 根据对 fpga/ 目录下代码的深入分析&#xff0c;该目录包含的是 Holoscan Sensor Bridge (HSB) 的 FPGA 核心 IP 及参考设计&#xff0c;其主要功能可以概括为&#xff1a;将各类传感器数据通过 FPGA 采集、封装为网络数据包&#xff0c;并以极低延迟通过以太网&…

作者头像 李华
网站建设 2026/7/5 13:21:00

3分钟上手NSC_BUILDER:Switch游戏文件管理的终极解决方案

3分钟上手NSC_BUILDER&#xff1a;Switch游戏文件管理的终极解决方案 【免费下载链接】NSC_BUILDER Nintendo Switch Cleaner and Builder. A batchfile, python and html script based in hacbuild and Nuts python libraries. Designed initially to erase titlerights encry…

作者头像 李华
网站建设 2026/7/5 13:20:26

高精度电压管理系统设计与STM32实现

1. 高精度电压管理系统的核心价值在工业自动化、精密仪器和嵌入式系统开发中&#xff0c;电压管理精度往往直接决定整个系统的性能上限。传统采用机械电位器或分立元件搭建的电压调节方案&#xff0c;普遍存在三个致命缺陷&#xff1a;调节精度低&#xff08;通常只有1%-5%&…

作者头像 李华
网站建设 2026/7/5 13:20:08

Multi-Agent 协作中的冲突消解机制

随着 AI Agent 从单智能体向多智能体系统演进&#xff0c;多个 Agent 协作完成任务已经成为常态。然而&#xff0c;多 Agent 系统面临一个核心挑战&#xff1a;冲突。当多个 Agent 拥有不同目标、不同知识或不同优先级时&#xff0c;冲突不可避免。如何设计有效的冲突消解机制&…

作者头像 李华
网站建设 2026/7/5 13:18:09

AI生成中文图片文字错乱?解析扩散模型与中文处理的底层瓶颈

&#x1f680; 30款热门AI模型一站整合&#xff0c;DeepSeek/GLM/Qwen 随心用&#xff0c;限时 5 折。 &#x1f449; 点击领海量免费额度 如果你用AI生成中文内容的图片&#xff0c;结果经常出现文字错乱、笔画粘连、结构扭曲&#xff0c;甚至像“鬼画符”一样难以辨认&…

作者头像 李华
网站建设 2026/7/5 13:17:03

影刀RPA深度教程:飞书生态联动实战

影刀RPA深度教程&#xff1a;飞书生态联动实战 飞书是和影刀联动最深的平台。消息通知、多维表格、审批、日程&#xff0c;全流程都能自动化。 这篇文章把飞书联动讲透&#xff0c;附带3个完整实战案例。 先装好环境 www.yingdao.com 下载&#xff0c;社区版免费。 飞书授权…

作者头像 李华