使用 Three.js 实现热力渐变效果
大家好!我是 [数擎 AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步!
开发领域:前端开发 | AI 应用 | Web3D | 元宇宙
技术栈:JavaScript、React、ThreeJs、WebGL、Go
经验经验:6 年+ 前端开发经验,专注于图形渲染和 AI 技术
演示地址:演示地址
开源项目:智简未来、晓智元宇宙、数字孪生引擎 、源码地址
演示地址:https://shader.shuqin.cc/4sfywn
源码地址]:https://github.com/dezhizhang/shadertoy
在这篇文章中,我们将通过 Three.js 实现一个简单而炫酷的热力渐变效果。我们将使用着色器(Shader)来模拟热力图,创造出动态的渐变效果。这种效果常用于可视化数据,或者只是为了增加网页的视觉冲击力。
项目概述
我们使用 Three.js 来创建一个全屏的 WebGL 场景,通过自定义的着色器(Shader)渲染一个动态的热力渐变效果。这个效果展示了从冷到热的渐变色,并通过动态的点位置变化模拟热力分布的变化。
环境搭建
首先,需要在 HTML 中引入 Three.js 库,我们使用了 CDN 来引入:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
这将为我们提供 Three.js 的核心功能,如创建场景、相机、渲染器以及应用自定义着色器等。
创建基本场景
在 JavaScript 中,我们首先创建了一个基础的 3D 场景,其中包括相机、渲染器以及全屏的平面几何体来显示效果。
let scene, camera, renderer, material;function init() {scene = new THREE.Scene();camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);
}
- 相机:我们使用了正交相机(THREE.OrthographicCamera)来渲染二维平面,这种相机没有透视效果,适合渲染平面效果。
- 渲染器:通过 THREE.WebGLRenderer 实现高效的 3D 渲染。
- 全屏平面:为了将热力渐变效果显示为全屏,我们使用了一个 2x2 的平面几何体 (THREE.PlaneGeometry),并将其拉伸到全屏。
const plane = new THREE.PlaneGeometry(2, 2);
const mesh = new THREE.Mesh(plane, material);
scene.add(mesh);
自定义着色器
着色器(Shader)是实现热力渐变效果的核心部分。我们通过自定义的顶点着色器和片段着色器来控制每个像素的颜色变化。
顶点着色器
顶点着色器的作用是计算每个顶点的坐标和纹理坐标,在这里我们只是将纹理坐标传递给片段着色器:
vertexShader: `varying vec2 vUv;void main() {vUv = uv;gl_Position = vec4(position, 1.0);}
`
片段着色器
片段着色器的主要任务是根据热力分布计算每个像素的颜色。我们使用了一个简单的色彩渐变算法,将不同的权重值映射到不同的颜色,并生成最终的热力图效果。
fragmentShader: `precision highp float;uniform float iTime;uniform vec2 iResolution;varying vec2 vUv;// 渐变颜色定义const vec3 colors[5] = vec3[](vec3(0.,0.,0.6),vec3(0.,1.,1.),vec3(0.0,1.0,0.),vec3(1.0,1.0,0.),vec3(1.0,0.0,0.));const float points[5] = float[](0.0, 0.15, 0.5, 0.65, 1.0);vec3 gradian(vec3 c1, vec3 c2, float a) {return mix(c1, c2, a);}vec3 heat4(float weight) {if(weight <= points[0]) return colors[0];if(weight >= points[4]) return colors[4];for(int i = 1; i < 5; i++) {if(weight < points[i]) {float a = (weight - points[i-1]) / (points[i] - points[i-1]);return gradian(colors[i-1], colors[i], a);}}return vec3(0.0);}float d(vec2 a, vec2 b) {return pow(max(0.0, 1.0 - distance(a, b) / 0.6), 2.0);}void main() {vec2 uv = vUv * 4.0 - vec2(2.0);uv.x *= iResolution.x / iResolution.y;float totalWeight = 0.0;// 优化循环次数为常量表达式for (float i = 0.0; i < 112.0; i += 1.0) {totalWeight += 0.5 * d(uv, vec2(sin(iTime * 0.3 + i) * 2.0 + 2.0 * sin(i * i),cos(iTime * 0.4 + i * 1.5) * 2.0));}gl_FragColor = vec4(heat4(totalWeight), 1.0);}
`
- 渐变颜色:我们定义了五个颜色,从深蓝到红色,代表从冷到热的渐变。
- 权重计算:我们使用了一个简单的函数 d() 来计算每个像素的权重,根据距离和时间动态生成热力分布。
动画效果
我们使用 requestAnimationFrame 来创建一个平滑的动画效果。每一帧,时间和分辨率都会被更新,从而动态改变热力渐变效果的颜色。
function animate() {requestAnimationFrame(animate);material.uniforms.iTime.value = performance.now() / 1000;renderer.render(scene, camera);
}animate();
响应式设计
为了确保热力渐变效果在不同屏幕尺寸下都能正常显示,我们通过监听窗口尺寸变化事件来动态调整渲染器的尺寸和分辨率:
function onWindowResize() {const width = window.innerWidth;const height = window.innerHeight;renderer.setSize(width, height);material.uniforms.iResolution.value.set(width, height);
}window.addEventListener('resize', onWindowResize);
完整代码
import * as THREE from 'three';let scene, camera, renderer, material;function init() {// 初始化场景scene = new THREE.Scene();camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 着色器材质配置const uniforms = {iTime: { value: 0 },iResolution: { value: new THREE.Vector2() }};material = new THREE.ShaderMaterial({uniforms: uniforms,vertexShader: `varying vec2 vUv;void main() {vUv = uv;gl_Position = vec4(position, 1.0);}`,fragmentShader: `precision highp float;uniform float iTime;uniform vec2 iResolution;varying vec2 vUv;// 渐变颜色定义const vec3 colors[5] = vec3[](vec3(0.,0.,0.6),vec3(0.,1.,1.),vec3(0.0,1.0,0.),vec3(1.0,1.0,0.),vec3(1.0,0.0,0.));const float points[5] = float[](0.0, 0.15, 0.5, 0.65, 1.0);vec3 gradian(vec3 c1, vec3 c2, float a) {return mix(c1, c2, a);}vec3 heat4(float weight) {if(weight <= points[0]) return colors[0];if(weight >= points[4]) return colors[4];for(int i = 1; i < 5; i++) {if(weight < points[i]) {float a = (weight - points[i-1]) / (points[i] - points[i-1]);return gradian(colors[i-1], colors[i], a);}}return vec3(0.0);}float d(vec2 a, vec2 b) {return pow(max(0.0, 1.0 - distance(a, b) / 0.6), 2.0);}void main() {vec2 uv = vUv * 4.0 - vec2(2.0);uv.x *= iResolution.x / iResolution.y;float totalWeight = 0.0;// 优化循环次数为常量表达式for (float i = 0.0; i < 112.0; i += 1.0) {totalWeight += 0.5 * d(uv, vec2(sin(iTime * 0.3 + i) * 2.0 + 2.0 * sin(i * i),cos(iTime * 0.4 + i * 1.5) * 2.0));}gl_FragColor = vec4(heat4(totalWeight), 1.0);}`});// 创建全屏平面const plane = new THREE.PlaneGeometry(2, 2);const mesh = new THREE.Mesh(plane, material);scene.add(mesh);// 初始化分辨率onWindowResize();window.addEventListener('resize', onWindowResize);
}function onWindowResize() {const width = window.innerWidth;const height = window.innerHeight;renderer.setSize(width, height);material.uniforms.iResolution.value.set(width, height);
}function animate() {requestAnimationFrame(animate);material.uniforms.iTime.value = performance.now() / 1000;renderer.render(scene, camera);
}init();
animate();
总结
通过自定义的着色器和 Three.js,我们可以创建出动态的热力渐变效果,这在数据可视化、交互式网页中非常有用。希望这篇博客能帮助你理解如何使用 Three.js 和着色器创建炫酷的视觉效果。如果你对 Three.js 或者着色器有任何疑问,欢迎在评论区留言讨论。
相关文章:
使用 Three.js 实现热力渐变效果
大家好!我是 [数擎 AI],一位热爱探索新技术的前端开发者,在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步! 开发领域:前端开发 | A…...
java-异常家族梳理(流程图)
前言: 使用流程图梳理异常,便于理解 梳理: Throwable ├── Error(严重错误,无需捕获) │ ├── OutOfMemoryError │ ├── StackOverflowError │ └── ... ├── Exception(可捕获处理) │ ├── RuntimeException(非检查异常/Unchecked) │ …...
开启蓝耘之旅:DeepSeek R1 模型在智算平台的起步教程
----------------------------------------------------------我的个人主页-------------------- 动动你的手指----------------------------------------点赞👍 收藏❤--------------------------------------------------------------- 引言 在深度学习的广袤领…...
[高等数学]不定积分的概念与性质
一、知识点 (一)原函数与不定积分的概念 定义1(原函数) 如果在区间 I I I 上,可导函数 F ( x ) F(x) F(x) 的导函数为 f ( x ) f(x) f(x),即对任一 x ∈ I x\in I x∈I,都有 F ′ ( x )…...
【算法】【高精度】acwing算法基础 793. 高精度乘法
题目 给定两个非负整数(不含前导 0) A 和 B,请你计算 AB 的值。 输入格式 共两行,第一行包含整数 A,第二行包含整数 B。 输出格式 共一行,包含 AB 的值。 数据范围 1≤A的长度≤100000, 0≤B≤10000 输入样…...
sqlite 查看表结构
在SQLite中,查看表结构通常有以下几种方法: 使用.schema命令 在SQLite的命令行界面中,你可以使用.schema命令加上表名来查看该表的结构。例如,如果你想查看名为your_table_name的表结构,你可以这样做: .s…...
测试中的第一性原理:回归本质的质量思维革命
在软件工程领域,测试活动常被惯性思维和经验主义所主导——测试用例库无限膨胀、自动化脚本维护成本居高不下、测试策略与业务目标渐行渐远。要突破这种困境,第一性原理(First Principles Thinking)提供了独特的解题视角ÿ…...
flink判断两个事件之间有没有超时(不使用CEP)
1.为啥不使用cep呢,cep的超时时间设置不好配置化,无法满足扩展要求 2.超时怎么界定。A事件发生后,过了N时间,还没有收到B事件,算超时。 代码如下: import com.alibaba.fastjson.JSONObject; import lombo…...
二级C语言题解:十进制转其他进制、非素数求和、重复数统计
目录 一、程序填空📝 --- 十进制转其他进制 题目📃 分析🧐 二、程序修改🛠️ --- 非素数求和 题目📃 分析🧐 三、程序设计💻 --- 重复数统计 题目📃 分析🧐 前言…...
打家劫舍3
今天和打家讲一下打家劫舍3 题目: 题目链接:337. 打家劫舍 III - 力扣(LeetCode) 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为root。 除了 root 之外,每栋房子有且只有一个“父“…...
练习题(2025.2.9)
题目背景 “咚咚咚……”“查水表!”原来是查水表来了,现在哪里找这么热心上门的查表员啊!小明感动得热泪盈眶,开起了门…… 题目描述 妈妈下班回家,街坊邻居说小明被一群陌生人强行押上了警车!妈妈丰富…...
【练习】PAT 乙 1074 宇宙无敌加法器
题目 地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的。而在PAT星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为“PAT数”。每个PAT星人都必须熟记各位数字的进制表,例如“……0527”就表示最…...
网络防御高级02-综合实验
web页面: [FW]interface GigabitEthernet 0/0/0 [FW-GigabitEthernet0/0/0]service-manage all permit 需求一,接口配置: SW2: [Huawei]sysname SW2 1.创建vlan [sw2]vlan 10 [sw2]vlan 20 2.接口配置 [sw2]interface GigabitEther…...
UITableView的复用原理
UITableView复用的基本原理是Cell复用机制,它通过重用已经创建的Cell来减少内存开始并提高性能,避免频繁创建和销毁Cell。 复用的流程 1.队列管理 UITableView维护一个可复用队列(reuse queue),存储离屏的UITableVi…...
SQL条件分支中的大讲究
在SQL中,条件分支用于根据不同的条件执行不同的操作,适用于数据查询、数据更新以及存储过程等场景。合理使用SQL条件分支,可以优化数据操作流程,提高代码的可读性和可维护性。 目录 1. 逻辑判断的基本概念 2. CASE 语句…...
Cherry Studio:一站式多模型AI交互平台深度解析 可配合大模型搭建私有知识库问答系统
Cherry Studio:一站式多模型AI交互平台深度解析 可配合大模型搭建私有知识库问答系统 大模型本地化部署流程可查看文章 3分钟教你搭建属于自己的本地大模型 DeepSeek Cherry Studio地址:https://cherry-ai.com/download Cherry Studio 简介 Cherry S…...
工业相机,镜头的选型及实战
工业相机和镜头的选型是机器视觉系统中的关键步骤,选型不当可能导致成像质量差或系统性能不达标。(用于个人的学习和记录) 一、工业相机选型方法 确定分辨率 分辨率需求:根据被测物体的尺寸和检测精度要求计算所需分辨率。 公式…...
C++模板学习从专家到入门:关键字typename与class
文章目录 共同点typename特性class特性 共同点 在定义类模板或者函数模板时,typename 和 class 关键字都可以用于指定模板参数中的类型。 template <class T> template <typename T>typename特性 C 允许在类内定义类型别名,且其使用方法与…...
BFS算法篇——FloodFill问题的高效解决之道(下)
文章目录 前言一. 图像渲染1.1 题目链接:https://leetcode.cn/problems/flood-fill/description/1.2 题目分析:1.3 思路讲解:1.4 代码实现: 二. 岛屿数量2.1 题目链接:https://leetcode.cn/problems/number-of-islands…...
Android性能优化
Android性能优化 如何优化一个包含大量图片加载的Android应用,以提高性能和用户体验? 优化一个包含大量图片加载的Android应用,可以从以下几个方面入手,以提高性能和用户体验: 选择合适的图片加载库 使用成熟的图片…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
