WebGL编程指南 - WebGL入门
初识绘图流程、缓冲区、着色器、attribute和uniform变量

先画一个蓝色的正方形
html代码:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body onload="main()"> //指定 onload 属性,告诉浏览器<body>元素加载完从何处开始执行<canvas id="example" width="400" height="400">Please use a browser that supports "canvas"</canvas><script src="./js/DrawRectangle.js"></script></body>
</html>
JS代码:
// DrawRectangle.js
function main() {// 获取<canvas>元素let canvas = document.getElementById('example')if (!canvas) {console.log('Failed to retrieve the <canvas> element')return}// 获取绘制二维图形的绘图上下文let ctx = canvas.getContext('2d')// 绘制蓝色矩形ctx.fillStyle = 'rgba(0,0,255,1.0)'ctx.fillRect(120, 10, 150, 150)
}
HTML5引入<canvas>标签,允许JavaScript动态的绘制图形
<body οnlοad="main()"> //指定 onload 属性,告诉浏览器<body>元素加载完从何处开始执行,作为JavaScript程序的入口
绘制蓝色矩形的js程序的,为了在<canvas>上绘制二维图形,需要经过三个步骤:
- 获取<canvas>元素
- 向该元素请求二维图形的“绘制上下文”
- 在绘图上下文上调用相应的绘图函数,绘制二维图形
ctx对象的fillRect方法,方法的语法为 context.fillRect(x,y,width,height),x,y为矩形左上角的坐标,剩下两个是宽度和高度
canvas 中的坐标和 OpenGL 一致

清空绘图区示例与背景色设置(初识多缓冲区模型与颜色缓冲区)
效果:
代码:
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"/><meta http-equiv="X_UA_Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,inital-scale=1.0"/><title>Document</title></head><body onload="main()"><canvas id="webgl" width="400" height="400"></canvas><script src="HelloCanvas.js"></script></body>
</html>
// HelloCanvas.js
function main() {// 获取<canvas>元素let canvas = document.getElementById('webgl')// 获取WebGL绘图上下文// let gl = getWebGLContext(canvas) (书中的函数)let gl = canvas.getContext('webgl')if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 指定清空<canvas>的颜色gl.clearColor(0.0, 0.0, 0.0, 1.0) // 黑色// 清空<canvas>gl.clear(gl.COLOR_BUFFER_BIT)}
因为没找到书中的函数库,所以参考了别人的代码
示例程序中 main 函数的执行流:

设置 canvas 背景色

- 一旦指定背景色后,背景色会贮存在WebGL系统中,在下次调用
gl.clearColor()方法之前不会改变。所以,如果未来还需要再次清空绘图区,没必要再指定一次颜色。 - 这是挂载在
WebGLRenderingContext对象上的方法。
gl.clear(buffer)
清空颜色缓冲区将导致 WebGL 清空页面上的 <canvas> 区域


WebGL中的gl.clear()方法继承自OpenGL,基于多基本缓冲区模型。
绘制一个点 与 静态着色器
相关内容:着色器初识、使用着色器的WebGL程序结构、初始化着色器、顶点着色器及内置变量、片元着色器及内置变量、齐次坐标、WebGL坐标系统、JavaScript程序和着色器程序的协同运行机制
相关函数:initShaders(), vec4(), gl.drawArrays()
示例程序将在原点(0.0, 0.0, 0.0)处的10个像素大的红色的点。
因为WebGL处理的是三维图形,所以我们有必要为这个点指定三维坐标。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Draw a point (1)</title>
</head>
<body onload="main()"><canvas id="webgl" width="400" height="400">Please use the browser supporting "canvas"</canvas><script src="../libs/webgl-utils.js"></script><script src="../libs/webgl-debug.js"></script><script src="../libs/cuon-utils.js"></script><script src="HelloPoint1.js"></script>
</body>
</html>
// HelloPoint1.js
// 顶点着色器程序
var VSHADER_SOURCE ='void main() {\n' +' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 设置坐标' gl_PointSize = 10.0;\n' + // 设置尺寸'}\n'
// 片元着色器程序
var FSHADER_SOURCE ='void main() {\n' +' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置颜色'}\n'
// 主程序
function main() {// 获取<canvas>元素let canvas = document.getElementById('webgl')// 获取WebGL绘图上下文// let gl = getWebGLContext(canvas)let gl = canvas.getContext('webgl')if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置<canvas>背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空<canvas>gl.clear(gl.COLOR_BUFFER_BIT)// 绘制一个点gl.drawArrays(gl.POINTS, 0, 1)
}
着色器程序以字符串的形式嵌入在 JavaScript 文件中,在程序真正开始运行前它就已经设置好了
WebGL中有两种着色器:
- 顶点着色器(Vertex shader):顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。顶点(vertex)是指二维或三维空间中的一个点,比如二维或三维图形的端点或交点。
- 片元着色器(Fragment shader):进行逐片元处理过程如光照的程序。片元(fragment)是一个WebGL术语,你可以将其理解为像素(图像的单元)。
JavaScript 读取了着色器的相关信息,然后存在 WebGL 系统中以供调用

WebGL 程序的执行流程

初始化着色器

顶点着色器先执行,它对gl_Posetion变量和gl_PointSize变量进行赋值,并将他们传入片元着色器,然后片元着色器再执行。
实际上,片元着色器并不直接接收两个变量,而是接收经过光栅化处理后的片元值,这一部分会在书的第五章进行讨论
WebGL程序包括运行在浏览器中的JavaScript和运行在WebGL系统中的着色器程序两个部分。
顶点着色器
// 顶点着色器程序
var VSHADER_SOURCE ='void main() {\n' +' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 设置坐标' gl_PointSize = 10.0;\n' + // 设置尺寸'}\n'
顶点着色器设置了点的位置和大小。该语言本身和C语言一样,必须包含一个main()函数,main()函数前面的关键字void表示这个函数没有返回值,同时也不能指定参数。
与大多数语言一样,着色器程序使用=操作符进行赋值,将点的位置和尺寸分别赋值给gl_Position和gl_PointSize,这两个变量是内置在顶点着色器之中的:

注:gl_Position必须被赋值,否则着色器无法正常工作;gl_PointSize并不是必须的,默认值为1.0。

如果类型赋值错误,如10.0变成10,就会报错
gl_Position 表示顶点的位置,一般顶点只需要三个点即可,用内置函数 vec() 创建 vec4类型的变量
vec4(v0, v1, v2, v3):根据v0,v1,v2,v3值创建vec4对象
参数:
v0, v1, v2, v3: 指定4个浮点型分量;
返回值:
由v0, v1, v2, v3组成的vec4对象。
在示例中,我们添加了1.0作为函数第四分量。在WebGL(或者说OpenGL)中,将gl_Positon设置为vec4类型的变量实际上是采用了齐次坐标的方式,因为它能提高处理三维数据的效率,所以在三维图形系统中被大量使用。关于齐次坐标可以看下面的说明:

片元着色器
顶点着色器控制点的位置和大小,片元着色器控制点的颜色。
片元着色器的作用是处理片元,使其显示在屏幕上
// 片元着色器程序
var FSHADER_SOURCE ='void main() {\n' +' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置颜色'}\n'

绘制操作
// 绘制一个点gl.drawArrays(gl.POINTS, 0, 1)
建立着色器之后,与HelloCanvas.js示例一样,进行设置背景色和清空绘制区域的操作,最后进行绘制操作。绘制操作采用的方法为gl.drawArrays()。

当程序调用 gl.drawArrays() 时,顶点着色器将其执行 count 次,每次处理一个顶点
示例程序函数参数设置如下:
- 因为我们绘制的是单独的点,所以设置第一个参数为gl.POINTS;
- 设置第二个参数为0,表示从第1个顶点(虽然只有一个顶点)开始画起;
- 第三个参数count为1,表示程序中只绘制1个点
WebGL 坐标系统
本身不是左手也不是右手,可以认为时右手坐标系
由此可见,WebGL坐标系和<canvas>绘图区的坐标系不同,需要将前者映射到后者。在<canvas>绘图区,WebGL坐标系统如下图所示:

绘制一个点2 与 attribute变量(动态设置顶点着色器)
相关内容:用JavaScript程序从着色器外部向着色器传输数据(动态,而不是静态写在着色器中)、attribute变量、uniform变量
相关函数:gl.getAttribLocation()、gl.vertexAttrib3f()及其同族函数小结:动态设置着色器参数,主要包括以下几步:1.在着色器语言中,设置attribute或uniform变量,将变量赋值到主函数参数中;2.JavaScript程序中通过相关函数获取变量存储地址;3.通过相关函数,向该地址传输数据。
在前一节中,点的位置是直接编(“硬编码”)写在顶点着色器中的,虽然易于理解但缺乏可扩展性;这一节中,Web程序可以将顶点的位置坐标从JavaScript传到着色器程序中,然后再对应位置上绘制点
目前而言,将位置信息从JavaScript程序中传给顶点着色器,有两种方式:attribute变量和uniform变量。

- attribute变量:传输与顶点相关的数据
- uniform变量:传输那些对于所有顶点都相同(或与顶点无关)的数据
两个变量都是GLSL ES变量,被用来从外部向顶点着色器内传输数据,示例中选用attribute变量,示例代码如下:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Draw a point (1)</title></head><body onload="main()"><canvas id="webgl" width="400" height="400">Please use the browser supporting "canvas"</canvas><!-- 书中的函数库 --><script src="./libs/webgl-utils.js"></script><script src="./libs/webgl-debug.js"></script><script src="./libs/cuon-utils.js"></script><script src="./js/HelloPoint2.js"></script></body>
</html>
// HelloPoint2.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'void main(){\n' +' gl_Position = a_Position;\n' +' gl_PointSize = 10.0;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main() {\n' +' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置颜色'}\n'
// 主函数
function main() {// 获取<canvas>元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = canvas.getContext('webgl')if (!gl) {console.log('Failed to get the rendering context for WebGL')}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 获取attribute变量的存储位置let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')}// 将顶点位置传输给attribute变量gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)// 设置<canvas>背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清除<canvas>gl.clear(gl.COLOR_BUFFER_BIT)// 绘制一个点gl.drawArrays(gl.POINTS, 0, 1)
}
attribute变量的声明与赋值:
在顶点着色器的第一行GLSL ES语言中声明了此变量(第3行):
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'void main(){\n' +' gl_Position = a_Position;\n' +' gl_PointSize = 10.0;\n' +'}\n'
为了使用 attribute 变量,需要包含一下步骤:
- 在顶点着色器中,声明 attribute 变量
- 将 attribute 变量赋值给 gl_Position 变量
- 向 attribute 变量传输数据
具体使用讲解:
attribute变量的声明按照以下的格式:<存储限定符><类型><变量名>

- 关键词attribute被称为存储限定符(storage qualifier),它表示接下来的变量(示例中是a_Position)是一个attribute变量。attribute变量必须声明成全局变量,数据将从着色器外部传给该变量。
与一般的JavaScript变量赋值不同,在WebGL系统之外向系统内的变量赋值需要知道该变量的存储地址,再通过存储地址向变量传输数据。获取attribute变量地址的部分代码如下:
// 获取attribute变量的存储位置let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')}
gl.getAttribLocation(program, name):获取由name参数指定的attribute变量的存储地址。
参数:
program: 指定包含顶点着色器和片元着色器的着色器程序对象;
name: 指定想要获取其存储地址的attribute变量的名称;
返回值:
大于等于0:attribute变量的存储地址
-1:指定的attribute变量不存在,或者其命名具有gl_或webgl_前缀
向 attribute 变量赋值
// 将顶点位置传输给attribute变量gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0)
通过存储地址向 attribute 变量赋值
代码中我们传递了三个浮点数,而a_Position变量得数据类型时vec4,此处该方法会默认将第四个分量设置为1.0。颜色值得第4个分量为1.0表示该颜色完全不透明,而齐次坐标得第4个分量为1.0使齐次坐标和三维坐标对应起来,所以1.0是一个“安全”的第4分量。下一部分对其同族函数的说明中介绍了默认填充的方式。

//矢量版本 可以接收类型化数组 矢量版本的函数在原函数的末尾加了字母‘v’
var position = new Float32Array([1.0, 2.0, 3.0, 1.0])
gl.vertexAttrib4fv(a_Position, position)
//对于接收数组的矢量版本,函数名中的数字代表所接收数组中的元素个数。
也可以加一个顶点大小数据,控制绘制的大小:部分代码
var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute float a_PointSize;\n' +'void main(){\n' + 'gl_Position = a_Position;\n' + 'gl_PointSize = a_PointSize;\n' + '}\n'let a_PointSize = gl.getAttribLocation(gl.program,'a_PointSize');if(a_Position < 0) {console.log('Failed to get the storage location of a_Position');return;}gl.vertexAttrib3f(a_Position,0.5,0.0,0.0);gl.vertexAttrib1f(a_PointSize,50.0);

写的太长了,网页开始卡顿了,再开一篇,这一章真的很长
参考:【《WebGL编程指南》读书笔记-WebGL入门】_getwebglcontext-CSDN博客
相关文章:
WebGL编程指南 - WebGL入门
初识绘图流程、缓冲区、着色器、attribute和uniform变量 先画一个蓝色的正方形 html代码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content&…...
mysql--数据类型
目录 搞定所有数据类型 一、常见数据类型分类 二、数值类型 1、bit类型 2、float类型 编辑3、decimal类型 4、字符类型 (1)char (2)varchar (3)varchar和char有甚区别? ࿰…...
代码随想录第40天|
#include <bits/stdc.h> using namespace std;vector<list<int>> graph; // 删除局部 graph,使用全局 graph vector<vector<int>> res; vector<int> path; int N, M;void dfs(int index) {if (index N) {res.push_back(path);…...
Turn-it:优化线材重构雕塑制造
🐨文章摘要abstract 电线雕塑在工业应用和日常生活中都很重要。 本文提出了一种新的制造策略,通过调整目标形状以适应电线弯曲机,然后由人工将其弯曲回目标形状。(机器弯曲人工弯曲) 该方法通过两阶段弯曲策略实现&a…...
微深节能 堆取料机动作综合检测系统 格雷母线
微深节能的堆取料机动作综合检测系统结合了格雷母线定位系统,是工业自动化领域的一项重要创新。该系统通过集成多种传感器和控制设备,实现对堆取料机的全面监控和精确控制,包括位置、速度、力度、振动以及工作状态等。格雷母线定位系统作为一…...
【JAVA面试题】什么是Springboot的自动配置以及注意事项
文章目录 强烈推荐核心概念:自动配置的关键特点:示例: 需要注意的点1.默认配置可能不适合所有场景2.Bean 冲突与覆盖3.应用启动慢的问题4.过度依赖自动配置5.安全性问题6.依赖冲突与版本兼容7.过多不必要的自动配置8.调试困难 专栏集锦 强烈推…...
华为鸿蒙开发笔记
记在前面 官方文档链接 因无法直接使用chatgpt进行编程(悲,2024/10),故记录笔记,方便查阅,基于arkts语言 DevEco 中文 deveco是默认有中文包的,所以在市场里面搜不错,而应该在已安装里面搜索,然后启用就行了 测试 对ts进行单独测试 打开entry/src/test/LocalUnit.test.…...
Go语言Gin框架的常规配置和查询数据返回json示例
文章目录 路由文件分组查询数据库并返回jsonservice层controller路由运行效果 启动多个服务 在 上一篇文章《使用Go语言的gorm框架查询数据库并分页导出到Excel实例》 中主要给大家分享了较多数据的时候如何使用go分页导出多个Excel文件并合并的实现方案,这一篇文章…...
JavaEE----多线程(二)
文章目录 1.进程的状态2.线程的安全引入3.线程安全的问题产生原因4.synchronized关键字的引入4.1修饰代码块4.2修饰实例方法4.3修饰静态方法4.4对象头介绍4.5死锁-可重入的特性 5.关于死锁的分析总结5.1死锁的分析5.2死锁成因的必要条件5.3死锁的解决方案 1.进程的状态 public…...
【K8S】快速入门Kubernetes
之前企业都是使用容器化和来构建自己的服务和应用程序,其中容器化优点有很多:提升了部署效率、稳定性、提高了资源的利用率降低了成本。 但是也带来了一些新的问题:容器的数量变得很多,管理就是一个新的问题。所以Kubernetes就出…...
如何在 MySQL 中处理大量的 DELETE 操作??
全文目录: 开篇语前言摘要简介概述DELETE 操作的基本概念常用的 DELETE 方法 核心源码解读简单 DELETE 语句批量 DELETE 示例 案例分析案例1:使用简单 DELETE 删除用户数据案例2:使用分批 DELETE 应用场景演示场景1:用户管理系统场…...
LabVIEW中句柄与引用
在LabVIEW中,句柄(Handle) 是一种用于引用特定资源或对象的标识符。它类似于指针,允许程序在内存中管理和操作复杂的资源,而不需要直接访问资源本身。句柄用于管理动态分配的资源,如队列、文件、网络连接、…...
【三十四】【QT开发应用】音量图标以及滑动条,没有代码补全的小技巧
效果展示 鼠标位于音量图标区域内,显示出滑动条。鼠标移出音量图标区域内滑动条隐藏。鼠标点击音量图标,如果此时音量为0,音量变成50,如果此时音量不为零,音量变为0。 CVolumeButton.h 音量图标头文件 #pragma once …...
Android修改第三方应用相机方向
以下修改基于Android7.1 diff --git a/frameworks/base/core/java/android/hardware/Camera.java b/frameworks/base/core/java/android/hardware/Camera.java index 8c7434b..7201481 100755 --- a/frameworks/base/core/java/android/hardware/Camera.java b/frameworks/ba…...
Python 读取文件汇总
readline和readlines的区别 使用 open()读取文件时,readline是读取文件的一行;而readlines是加载全部文档,以list形式保存每一行内容。 使用with避免资源泄露 with语句不仅限于open()函数,任何实现了上下文管理协议的对象都可以…...
云原生:一张图了解devops 中CI/CD
一个典型的云原生应用的开发和部署过程,其中涉及到的主要工具有 Git、Docker、Jenkins/CircleCI、Ansible、Kubernetes 等。以下是每个步骤的简要说明: 开发人员(Developers)使用 Git 进行版本控制,他们将代码推送到 G…...
无人机之自组网通信技术篇
无人机的自组网通信技术是一种利用无人机作为节点,通过无线通信技术实现节点间自主组网、动态路由和数据传输的技术。 一、技术原理与特点 技术原理:无人机自组网技术基于自组织网络(Ad-Hoc Network)的原理,通过无线…...
【WebLogic】Oracle发布2024年第四季度中间件安全公告
Oracle于美国时间2024年10月15日发布了 WebLogic 12c(12.2.1.4.0)和14c(14.1.1.0.0)两个大版本2024年第4季度的安全公告,涉及漏洞ID共计 6 个,包含2个高危漏洞 2 个,4个中危漏洞,其中…...
Java集合(3:Set和Map)
文章目录 Set概述哈希值HashSet去重原理LinkedHashSetTreeSet自定义排序规则 Map概述Map的基本方法Map集合的获取功能哈希表HashMap底层源码 特点注意 Set 概述 Set集合也是一个接口,继承自Collection,与List类似,都需要通过实现类来进行操…...
【Golang】Gin框架中如何定义路由
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
