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

用简单例子讲清楚webgl模板测试

文章目录

  • 搭建简易的webgl环境
  • 绘制简单三角形(不带stencilTest)
  • 绘制另一个三角形(不带模板测试)
  • 加入模板测试
  • 总结
  • 调参练习

搭建简易的webgl环境

  一直以来,我只是想通过搭建纯webgl环境,进行开发,来清楚地了解基础webgl功能,如果在C++环境里面使用opengl,还需要配置加载各种 lib,以及编译,容易错,而且里面有很多是其他各种库里面的函数,不能最大限度地接近原理。偶然间,发现了一个很简单的方法,基于最基本的webgl函数:WebGLRenderingContext里面的api。下面推荐一下,可以方便大家一起学习。

  进入mdn在线文档:
https://developer.mozilla.org/zh-CN/
  这里面不仅有详细地说明,还有一个平台给我们实践各种代码,如下图所示,点击其中的play:
在这里插入图片描述
  则进入写代码平台:
https://developer.mozilla.org/zh-CN/play

  平台就这么搭建好了,不用写任何东西,也不用安装任何软件,编译任何代码。否则,很多情况下,我的耐心都在各种无关的操作中被消磨掉了,到头来什么也没学到。

绘制简单三角形(不带stencilTest)

把下面这段js代码复制到上面写代码平台的最下面js框里面,然后右边会出现一个三角形:

const canvas = document.createElement("canvas");
canvas.width = 300;
canvas.height = 300;
document.body.append(canvas); // 创建和将 canvas 加入页面
const gl = canvas.getContext("webgl");
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 创建一个顶点着色器
gl.shaderSource(vertexShader,`attribute vec4 a_position;void main() {gl_Position = a_position; // 设置顶点位置}
`,
); // 编写顶点着色器代码
gl.compileShader(vertexShader); // 编译着色器代码const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 创建一个片元着色器
gl.shaderSource(fragmentShader,`precision mediump float;uniform vec4 u_color;void main() {gl_FragColor = u_color; // 设置片元颜色}
`,
); // 编写片元着色器代码
gl.compileShader(fragmentShader); // 编译着色器代码const program = gl.createProgram(); // 创建一个程序
gl.attachShader(program, vertexShader); // 添加顶点着色器
gl.attachShader(program, fragmentShader); // 添加片元着色器
gl.linkProgram(program); // 连接 program 中的着色器gl.useProgram(program); // 告诉 webgl 用这个 program 进行渲染const colorLocation = gl.getUniformLocation(program, "u_color");
// 获取 u_color 变量位置
gl.uniform4f(colorLocation, 0.93, 0, 0.56, 1); // 设置它的值const positionLocation = gl.getAttribLocation(program, "a_position");
// 获取 a_position 位置
const positionBuffer = gl.createBuffer();
// 创建一个顶点缓冲对象,返回其 ID,用来放三角形顶点数据,
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 将这个顶点缓冲对象绑定到 gl.ARRAY_BUFFER
// 后续对 gl.ARRAY_BUFFER 的操作都会映射到这个缓存
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([0, 0.5, 0.5, 0, -0.5, -0.5]), // 三角形的三个顶点// 因为会将数据发送到 GPU,为了省去数据解析,这里使用 Float32Array 直接传送数据gl.STATIC_DRAW, // 表示缓冲区的内容不会经常更改
);
// 将顶点数据加入的刚刚创建的缓存对象gl.vertexAttribPointer(// 告诉 OpenGL 如何从 Buffer 中获取数据positionLocation, // 顶点属性的索引2, // 组成数量,必须是1,2,3或4。我们只提供了 x 和 ygl.FLOAT, // 每个元素的数据类型false, // 是否归一化到特定的范围,对 FLOAT 类型数据设置无效0, // stride 步长 数组中一行长度,0 表示数据是紧密的没有空隙,让OpenGL决定具体步长0, // offset 字节偏移量,必须是类型的字节长度的倍数。
);
gl.enableVertexAttribArray(positionLocation);
// 开启 attribute 变量额,使顶点着色器能够访问缓冲区数据gl.clearColor(0, 1, 1, 1); // 设置清空颜色缓冲时的颜色值
gl.clear(gl.COLOR_BUFFER_BIT); // 清空颜色缓冲区,也就是清空画布gl.drawArrays(// 从数组中绘制图元gl.TRIANGLES, // 画三角形0, // 从哪个点开始画3, // 需要用到多少个点
);

在这里插入图片描述

上面是绘制简单三角形的webgl js代码,为了便于理解,这篇文章从这个最一般的helloworld讲起,通过加入一些变化,说明stenciltest的原理,因为我在网上看到很多有各种复杂的部分,什么贴纹理啊,绘制地板,box啊,甚至还加了动画,实在不方便对单个模板测试功能的理解。

绘制另一个三角形(不带模板测试)

把以下这一小段代码加入到刚才的js代码后面,可以绘制两个重叠的三角形:

gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1.0, 0.0, 0.0, -1.0, 1., 0.0]), // 三角形的三个顶点// 因为会将数据发送到 GPU,为了省去数据解析,这里使用 Float32Array 直接传送数据gl.STATIC_DRAW, // 表示缓冲区的内容不会经常更改
);
gl.drawArrays(// 从数组中绘制图元gl.TRIANGLES, // 画三角形0, // 从哪个点开始画3, // 需要用到多少个点
);

在这里插入图片描述

加入模板测试

在刚才的基础上做如下修改:

  • 修改初始化gl的代码 ,原来的const gl = canvas.getContext("webgl");改成:const gl = canvas.getContext("webgl",{stencil: true});
  • 在绘制第一个三角形之前加入如下代码:
gl.enable(gl.STENCIL_TEST);
gl.colorMask(false, false, false, false);
gl.stencilFunc(gl.ALWAYS, 1, 255);
gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);

其中第一句是开启模板测试,第二句设置color遮罩,目的是不让它真正显示在结果框中,但是还是影响了模板测试,并且可以修改了stencilbuffer里面的值,第三句是设置通过模板测试的条件,gl.ALWAYS也就是永远通过,1代表ref(参照值),255代表位掩码,这里没用,因为是always通过模板测试,第四句是设置模板测试不通过或者通过而深度测试不通过或者模板测试和深度测试都通过以后干什么,这里都设置为REPLACE,表示不管咋样,要把模板buffer里面的值全部替换成ref(参照值),第三句已设定参照值为1。这些都要在绘制第一个三角形时生效,还记得第一个三角形是什么吗?那是一个倾斜的三角形,可以回头看截图。

这样设置后在绘制完第一个三角形以后stencilbuffer里面是这样子的(示意图):
在这里插入图片描述
其中只有在中间三角形地方的片源都是1,其他都是0,因为模板测试缓冲区默认是0,其他没绘制的地方保持不变,绘制地方(也就是三角形区域)不论是深度测试和模板测试是否通过都用ref值1替换。

这样设置以后整体运行后是看不到任何三角形的,因为gl.colorMask(false, false, false, false)这句话的原因。

  • 在绘制第二个三角形之前加入以下代码:
gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.NOTEQUAL, 1, 255);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);

第一句是设置color遮罩,这里红绿蓝透明都是true,意思是都可以绘制出来了,所以第二个三角形就能看到了,下面一句话是设置模板测试通过的条件,不等于1,具体是:ref=1, mask=255,令:逐片源看stenbuffer里面当前存储值=buf,设置为NOTEQUAL则意思是:ref&mask!==buf&mask,翻译过来就是:ref按位与mask的值不等于逐片源stencilbuffer里面当前值按位与mask的值,其中ref按位与mask为1,恒定,逐片源stencilbuffer里面当前值按位与mask则是:非第一个三角形绘制区域为0,第一个三角形绘制区域为1,所以这里模板测试通过的条件就是:非第一个三角形绘制区域,将通过模板测试。

而第三句规定了不通过模板测试和通过模板测试以及通过模板测试和深度测试之后都不改变stencilbuffer的当前值。
在以上设置完成后绘制的第二个三角形如下所示:
在这里插入图片描述
从图中看,第二个三角形中在第一个三角形区域的片源被去掉了,因为那些片源没有通过模板测试。符合我们之前的设置结果。

修改后的整体js代码如下:

const canvas = document.createElement("canvas");
canvas.width = 300;
canvas.height = 300;
document.body.append(canvas); // 创建和将 canvas 加入页面
const gl = canvas.getContext("webgl",{stencil:true});
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 创建一个顶点着色器
gl.shaderSource(vertexShader,`attribute vec4 a_position;void main() {gl_Position = a_position; // 设置顶点位置}
`,
); // 编写顶点着色器代码
gl.compileShader(vertexShader); // 编译着色器代码const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 创建一个片元着色器
gl.shaderSource(fragmentShader,`precision mediump float;uniform vec4 u_color;void main() {gl_FragColor = u_color; // 设置片元颜色}
`,
); // 编写片元着色器代码
gl.compileShader(fragmentShader); // 编译着色器代码const program = gl.createProgram(); // 创建一个程序
gl.attachShader(program, vertexShader); // 添加顶点着色器
gl.attachShader(program, fragmentShader); // 添加片元着色器
gl.linkProgram(program); // 连接 program 中的着色器gl.useProgram(program); // 告诉 webgl 用这个 program 进行渲染const colorLocation = gl.getUniformLocation(program, "u_color");
// 获取 u_color 变量位置
gl.uniform4f(colorLocation, 0.93, 0, 0.56, 1); // 设置它的值const positionLocation = gl.getAttribLocation(program, "a_position");
// 获取 a_position 位置
const positionBuffer = gl.createBuffer();
// 创建一个顶点缓冲对象,返回其 ID,用来放三角形顶点数据,
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 将这个顶点缓冲对象绑定到 gl.ARRAY_BUFFER
// 后续对 gl.ARRAY_BUFFER 的操作都会映射到这个缓存
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([0, 0.5, 0.5, 0, -0.5, -0.5]), // 三角形的三个顶点// 因为会将数据发送到 GPU,为了省去数据解析,这里使用 Float32Array 直接传送数据gl.STATIC_DRAW, // 表示缓冲区的内容不会经常更改
);
// 将顶点数据加入的刚刚创建的缓存对象gl.vertexAttribPointer(// 告诉 OpenGL 如何从 Buffer 中获取数据positionLocation, // 顶点属性的索引2, // 组成数量,必须是1,2,3或4。我们只提供了 x 和 ygl.FLOAT, // 每个元素的数据类型false, // 是否归一化到特定的范围,对 FLOAT 类型数据设置无效0, // stride 步长 数组中一行长度,0 表示数据是紧密的没有空隙,让OpenGL决定具体步长0, // offset 字节偏移量,必须是类型的字节长度的倍数。
);
gl.enableVertexAttribArray(positionLocation);
// 开启 attribute 变量额,使顶点着色器能够访问缓冲区数据gl.clearColor(0, 1, 1, 1); // 设置清空颜色缓冲时的颜色值
gl.clear(gl.COLOR_BUFFER_BIT); // 清空颜色缓冲区,也就是清空画布
gl.enable(gl.STENCIL_TEST);
gl.colorMask(false, false, false, false);
gl.stencilFunc(gl.ALWAYS, 1, 255);
gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);gl.drawArrays(// 从数组中绘制图元gl.TRIANGLES, // 画三角形0, // 从哪个点开始画3, // 需要用到多少个点
);
gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.NOTEQUAL, 1, 255);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1.0, 0.0, 0.0, -1.0, 1., 0.0]), // 三角形的三个顶点// 因为会将数据发送到 GPU,为了省去数据解析,这里使用 Float32Array 直接传送数据gl.STATIC_DRAW, // 表示缓冲区的内容不会经常更改
);
gl.drawArrays(// 从数组中绘制图元gl.TRIANGLES, // 画三角形0, // 从哪个点开始画3, // 需要用到多少个点
);

总结

  通过实践,我发现模板测试是这样的一个思路,总结如下:

  在各种绘制语句之前加入一些配置项,这些配置项的目的是修改stencilbuffer以及决定这次的绘制是不是能通过模板测试,因为只有通过模板测试和深度测试的片源才能最终被绘制出来,而修改后的stencilbuffer里面的值会影响下次绘制是否能通过模板测试,所以模板测试并不会修改绘制出的东西的颜色啥的,他是一种高级裁剪。通过这种方式,经过多次不清空stencilbuffer的绘制过程,在stencilbuffer里面形成一幅独一无二的图像,这种图像是前面多次绘制的结果(它可能是复杂的),不是简单的行列排布,而是依赖于之前图像的一些特征,不断地影响着后面的绘制结果。

调参练习

   为了便于理解我们可以修改其中的某些参数,来测试自己是不是真正理解,比如,我们把第二次绘制之前的模板测试条件设置语句中notequal改成equal:gl.stencilFunc(gl.EQUAL, 1, 255);那么我们将得到另一个三角形:
在这里插入图片描述

  最后留一道思考题,如何修改以上代码,绘制出以下图形呢?
在这里插入图片描述
  快来尝试一下吧。

最近在学习英语,所以想用英语表达一下一点学习感悟:
Many engineers want to create the complex system which looks like cool and amazing at the beginning and ignore the easy and basic things, I know this feeling and experienced that, it is about anxiety, about unexpectable future, which tells me that we must work hard. After years of working, I found it is impossible, every skill need to practice from the easy, little, looks boring system, it may not be needed by our boss, by the society, we can not get any afford from that, but we need to face that, if we just copy any other code from the internet, and make out something that looks well, it doesn’t mean I have mastered that skill. Perhaps, it listened stupid, what? to make wheels by ourselves? What time is now? yeah, I confess it is stupid, but I just want to be the person, only in my way to understand the world.

相关文章:

用简单例子讲清楚webgl模板测试

文章目录 搭建简易的webgl环境绘制简单三角形(不带stencilTest)绘制另一个三角形(不带模板测试)加入模板测试总结调参练习 搭建简易的webgl环境 一直以来,我只是想通过搭建纯webgl环境,进行开发,来清楚地了…...

区块链(8):p2p去中心化之websoket服务端实现业务逻辑

1 业务逻辑 例如 peer1和peer2之间相互通信 peer1通过onopen{ write(Mesage(QUERY_LATEST))} 向peer2发送消息“我要最新的区块”。 peer2通过onMessage收到消息,通过handleMessage方法对消息进行处理。 handleMessage根据消息类型进行处理 RESPONSE_BLOCKCHAIN:返回区块链…...

composer安装与设置

1、到官网下载 composer.phar。下载地址:Composer 2、将下载的composer.phar 复制到 composer 文件夹中 3、在composer文件夹中新建文件 composer.bat,内容为 php "%~dp0composer.phar" %* 5、设置环境变量的path,添加composer文件夹...

unordered_map/unordered_set的学习[unordered系列]

文章目录 1.老生常谈_遍历2.性能测试3.OJ训练3.1存在重复元素3.2两个数组的交集Ⅱ3.3两句话中的不常见单词3.4两个数组的交集3.5在长度2N的数组中找出重复N次的元素 1.老生常谈_遍历 #pragma once #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <l…...

C++位图—布隆过滤器

目录 位图概念位图应用 布隆过滤器简介布隆过滤器的优缺点布隆过滤器应用场景布隆过滤器实现布隆过滤器误判率分析 总结 位图概念 位图是一种数据结构&#xff0c;用于表示一组元素的存在或不存在&#xff0c;通常用于大规模数据集的快速查询。它基于一个位数组&#xff08;或位…...

SQL SELECT 语句进阶

之前探讨了SQL SELECT 语句的基础内容,包括语法、字段选择、记录限制和数据源指定。今天将进一步深入,探讨多表连接、过滤结果集和逻辑运算等高级主题,还有LIKE 模糊查询、ORDER BY 对结果集排序、运用聚合函数汇总结果以及 GROUP BY 子句与相关应用。 本文将继续使用《三国…...

Mac程序坞美化工具 uBar

uBar是一款为Mac用户设计的任务栏增强软件&#xff0c;它可以为您提供更高效和更个性化的任务管理体验。 以下是uBar的一些主要特点和功能&#xff1a; 更直观的任务管理&#xff1a;uBar改变了Mac上传统的任务栏设计&#xff0c;将所有打开的应用程序以类似于Windows任务栏的方…...

【数据结构】排序之插入排序和选择排序

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;数据结构 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、排序的概念及其分类 &#x1f4d2;1.1排序的概念 &#x1f4d2;1.2排序…...

6.html表单

HTML表单&#xff08;HTML form&#xff09;是网页中用于收集用户输入数据的一种方式。表单由多个表单元素组成&#xff0c;通常包括输入框&#xff0c;复选框&#xff0c;单选按钮&#xff0c;下拉列表和提交按钮等。 HTML表单元素的基本结构如下&#xff1a; <form acti…...

【python学习第11节:numpy】

文章目录 一&#xff0c;numpy&#xff08;上&#xff09;1.1基础概念1.2数组的属性1.3数组创建1.4 类型转换1.5ndarry基础运算&#xff08;上&#xff09;矢量化运算1.6拷贝和视图1.6.1完全不复制1.6.2视图或浅拷贝1.6.3深拷贝 1.7索引&#xff0c;切片和迭代1.7.1一维数组1.7…...

Eclipse 主网即将上线迎空投预期,Zepoch 节点或成受益者?

目前&#xff0c;Zepoch 节点空投页面中&#xff0c;模块化 Layer2 Rollup 项目 Eclipse 出现在其空投列表中。 配合近期 Eclipse 宣布了其将由 SVM 提供支持的 Layer2 主网架构&#xff0c;并将在今年年底上线主网的消息后&#xff0c;不免引发两点猜测&#xff1a;一个是 Ecl…...

JavaSE | 初识Java(四) | 输入输出

基本语法 System.out.println(msg); // 输出一个字符串, 带换行 System.out.print(msg); // 输出一个字符串, 不带换行 System.out.printf(format, msg); // 格式化输出 println 输出的内容自带 \n, print 不带 \n printf 的格式化输出方式和 C 语言的 printf 是基本一致的 代码…...

车牌超分辨率:License Plate Super-Resolution Using Diffusion Models

论文作者&#xff1a;Sawsan AlHalawani,Bilel Benjdira,Adel Ammar,Anis Koubaa,Anas M. Ali 作者单位&#xff1a;Prince Sultan University 论文链接&#xff1a;http://arxiv.org/abs/2309.12506v1 内容简介&#xff1a; 1&#xff09;方向&#xff1a;图像超分辨率技术…...

如何制作在线流程图?6款在线工具帮你轻松搞定

流程图&#xff0c;顾名思义 —— 用视觉化的方式来描述一种过程或流程。它可以应用于各种领域&#xff0c;从业务流程&#xff0c;算法&#xff0c;到计算机程序等。然而&#xff0c;在创建流程图时&#xff0c;可能会遇到许多问题或者困惑&#xff0c;如缺乏专业的设计技能&a…...

反SSDTHOOK的另一种思路-0环实现自己的系统调用

反SSDTHOOK的另一种思路-0环实现自己的系统调用 大家都知道我们在应用层使用系统api除了gdi相关的都会走中断门或者systementer进0环然后在走ssdt表去执行0环的函数 这也就导致了ssdthook可以挡下大部分的api调用&#xff0c;那如果我们进0环走另外一条路线的话不通过ssdt就可…...

Certbot签发和续费泛域名SSL证书(通过DNS TXT记录来验证域名有效性)

我们在使用let’s encrypt获取免费的HTTPS证书的时候&#xff0c;let’s encrypt需要对域名进行验证&#xff0c;以确保域名是你自己的 之前用默认的文件验证方式总有奇怪的问题导致失败&#xff0c;我也是很无奈&#xff0c;于是改用验证DNS-TXT记录的方式来验证&#xff0c;而…...

PY32F003F18之RTC

一、RTC振荡器 PY32F003F18实时时钟的振荡器是内部RC振荡器&#xff0c;频率为32.768KHz。它也可以使用HSE时钟&#xff0c;不建议使用。HAL库提到LSE振荡器&#xff0c;但PY32F003F18实际上没有这个振荡器。 缺点&#xff1a;CPU掉电后&#xff0c;需要重新配置RTC&#xff…...

redis主从从,redis-7.0.13

redis主从从&#xff0c;redis-7.0.13 下载redis安装redis安装redis-7.0.13过程报错1、没有gcc&#xff0c;报错2、没有python3&#xff0c;报错3、[adlist.o] 错误 127 解决安装报错安装完成 部署redis 主从从结构redis主服务器配置redis启动redis登录redisredis默认是主 redi…...

力扣-338.比特位计数

Idea 直接暴力做法&#xff1a;计算从0到n&#xff0c;每一位数的二进制中1的个数&#xff0c;遍历其二进制的每一位即可得到1的个数 AC Code class Solution { public:vector<int> countBits(int n) {vector<int> ans;ans.emplace_back(0);for(int i 1; i < …...

【Leetcode】 17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits "23" 输出&…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...