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

使用 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 实现热力渐变效果

大家好&#xff01;我是 [数擎 AI]&#xff0c;一位热爱探索新技术的前端开发者&#xff0c;在这里分享前端和 Web3D、AI 技术的干货与实战经验。如果你对技术有热情&#xff0c;欢迎关注我的文章&#xff0c;我们一起成长、进步&#xff01; 开发领域&#xff1a;前端开发 | A…...

java-异常家族梳理(流程图)

前言: 使用流程图梳理异常,便于理解 梳理: Throwable ├── Error(严重错误,无需捕获) │ ├── OutOfMemoryError │ ├── StackOverflowError │ └── ... ├── Exception(可捕获处理) │ ├── RuntimeException(非检查异常/Unchecked) │ …...

开启蓝耘之旅:DeepSeek R1 模型在智算平台的起步教程

----------------------------------------------------------我的个人主页-------------------- 动动你的手指----------------------------------------点赞&#x1f44d; 收藏❤--------------------------------------------------------------- 引言 在深度学习的广袤领…...

[高等数学]不定积分的概念与性质

一、知识点 &#xff08;一&#xff09;原函数与不定积分的概念 定义1&#xff08;原函数&#xff09; 如果在区间 I I I 上&#xff0c;可导函数 F ( x ) F(x) F(x) 的导函数为 f ( x ) f(x) f(x)&#xff0c;即对任一 x ∈ I x\in I x∈I&#xff0c;都有 F ′ ( x )…...

【算法】【高精度】acwing算法基础 793. 高精度乘法

题目 给定两个非负整数&#xff08;不含前导 0&#xff09; A 和 B&#xff0c;请你计算 AB 的值。 输入格式 共两行&#xff0c;第一行包含整数 A&#xff0c;第二行包含整数 B。 输出格式 共一行&#xff0c;包含 AB 的值。 数据范围 1≤A的长度≤100000, 0≤B≤10000 输入样…...

sqlite 查看表结构

在SQLite中&#xff0c;查看表结构通常有以下几种方法&#xff1a; 使用.schema命令 在SQLite的命令行界面中&#xff0c;你可以使用.schema命令加上表名来查看该表的结构。例如&#xff0c;如果你想查看名为your_table_name的表结构&#xff0c;你可以这样做&#xff1a; .s…...

测试中的第一性原理:回归本质的质量思维革命

在软件工程领域&#xff0c;测试活动常被惯性思维和经验主义所主导——测试用例库无限膨胀、自动化脚本维护成本居高不下、测试策略与业务目标渐行渐远。要突破这种困境&#xff0c;第一性原理&#xff08;First Principles Thinking&#xff09;提供了独特的解题视角&#xff…...

flink判断两个事件之间有没有超时(不使用CEP)

1.为啥不使用cep呢&#xff0c;cep的超时时间设置不好配置化&#xff0c;无法满足扩展要求 2.超时怎么界定。A事件发生后&#xff0c;过了N时间&#xff0c;还没有收到B事件&#xff0c;算超时。 代码如下&#xff1a; import com.alibaba.fastjson.JSONObject; import lombo…...

二级C语言题解:十进制转其他进制、非素数求和、重复数统计

目录 一、程序填空&#x1f4dd; --- 十进制转其他进制 题目&#x1f4c3; 分析&#x1f9d0; 二、程序修改&#x1f6e0;️ --- 非素数求和 题目&#x1f4c3; 分析&#x1f9d0; 三、程序设计&#x1f4bb; --- 重复数统计 题目&#x1f4c3; 分析&#x1f9d0; 前言…...

打家劫舍3

今天和打家讲一下打家劫舍3 题目&#xff1a; 题目链接&#xff1a;337. 打家劫舍 III - 力扣&#xff08;LeetCode&#xff09; 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为root。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“…...

练习题(2025.2.9)

题目背景 “咚咚咚……”“查水表&#xff01;”原来是查水表来了&#xff0c;现在哪里找这么热心上门的查表员啊&#xff01;小明感动得热泪盈眶&#xff0c;开起了门…… 题目描述 妈妈下班回家&#xff0c;街坊邻居说小明被一群陌生人强行押上了警车&#xff01;妈妈丰富…...

【练习】PAT 乙 1074 宇宙无敌加法器

题目 地球人习惯使用十进制数&#xff0c;并且默认一个数字的每一位都是十进制的。而在PAT星人开挂的世界里&#xff0c;每个数字的每一位都是不同进制的&#xff0c;这种神奇的数字称为“PAT数”。每个PAT星人都必须熟记各位数字的进制表&#xff0c;例如“……0527”就表示最…...

网络防御高级02-综合实验

web页面&#xff1a; [FW]interface GigabitEthernet 0/0/0 [FW-GigabitEthernet0/0/0]service-manage all permit 需求一&#xff0c;接口配置&#xff1a; SW2: [Huawei]sysname SW2 1.创建vlan [sw2]vlan 10 [sw2]vlan 20 2.接口配置 [sw2]interface GigabitEther…...

UITableView的复用原理

UITableView复用的基本原理是Cell复用机制&#xff0c;它通过重用已经创建的Cell来减少内存开始并提高性能&#xff0c;避免频繁创建和销毁Cell。 复用的流程 1.队列管理 UITableView维护一个可复用队列&#xff08;reuse queue&#xff09;&#xff0c;存储离屏的UITableVi…...

SQL条件分支中的大讲究

在SQL中&#xff0c;条件分支用于根据不同的条件执行不同的操作&#xff0c;适用于数据查询、数据更新以及存储过程等场景。合理使用SQL条件分支&#xff0c;可以优化数据操作流程&#xff0c;提高代码的可读性和可维护性。 目录 1. 逻辑判断的基本概念 2. CASE 语句&#xf…...

Cherry Studio:一站式多模型AI交互平台深度解析 可配合大模型搭建私有知识库问答系统

Cherry Studio&#xff1a;一站式多模型AI交互平台深度解析 可配合大模型搭建私有知识库问答系统 大模型本地化部署流程可查看文章 3分钟教你搭建属于自己的本地大模型 DeepSeek Cherry Studio地址&#xff1a;https://cherry-ai.com/download Cherry Studio 简介 Cherry S…...

工业相机,镜头的选型及实战

工业相机和镜头的选型是机器视觉系统中的关键步骤&#xff0c;选型不当可能导致成像质量差或系统性能不达标。&#xff08;用于个人的学习和记录&#xff09; 一、工业相机选型方法 确定分辨率 分辨率需求&#xff1a;根据被测物体的尺寸和检测精度要求计算所需分辨率。 公式…...

C++模板学习从专家到入门:关键字typename与class

文章目录 共同点typename特性class特性 共同点 在定义类模板或者函数模板时&#xff0c;typename 和 class 关键字都可以用于指定模板参数中的类型。 template <class T> template <typename T>typename特性 C 允许在类内定义类型别名&#xff0c;且其使用方法与…...

BFS算法篇——FloodFill问题的高效解决之道(下)

文章目录 前言一. 图像渲染1.1 题目链接&#xff1a;https://leetcode.cn/problems/flood-fill/description/1.2 题目分析&#xff1a;1.3 思路讲解&#xff1a;1.4 代码实现&#xff1a; 二. 岛屿数量2.1 题目链接&#xff1a;https://leetcode.cn/problems/number-of-islands…...

Android性能优化

Android性能优化 如何优化一个包含大量图片加载的Android应用&#xff0c;以提高性能和用户体验&#xff1f; 优化一个包含大量图片加载的Android应用&#xff0c;可以从以下几个方面入手&#xff0c;以提高性能和用户体验&#xff1a; 选择合适的图片加载库 使用成熟的图片…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...