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

OpenGL shader开发实战学习笔记:第十章 法线贴图

1. 10 法线贴图

1.1. 什么是法线贴图

我们如果想要在盾牌上实现凹凸感,应该如何做?一种方法是添加更多的顶点来建模更多的细节,但是网格的顶点越多,渲染网格所需的顶点着色器计算就越多,网格占用的内存就越多。大多数游戏不能仅仅因为凹凸和刮痕而为每个物体添加数千个顶点。

因此,我们用法线贴图来解决这个问题。法线贴图是使用一张纹理来存储法线信息,然后通过纹理采样来得到法线信息,最后将法线信息应用到物体上。

法线贴图是在每一个像素中存储归一化的向量而不是颜色的纹理。由于不存在负颜色,因此纹理无法表示小于0的值 。为了解决这个问题,我们需要将法线从 [ − 1 , 1 ] [-1,1] [1,1]映射到 [ 0 , 1 ] [0,1] [0,1]

以下是一个法线贴图的例子:

会看到其偏蓝色,其原因为:

1.1.1. 法线向量的表示

在三维空间里,法线向量是一个三维向量,通常用 ( x , y , z ) (x, y, z) (x,y,z) 来表示,其值的范围是 [ − 1 , 1 ] [-1, 1] [1,1]。这个向量定义了表面上某点的朝向,在光照计算里起着关键作用。

1.1.2. 纹理存储限制

纹理(如法线贴图)一般用来存储颜色信息,颜色分量(红、绿、蓝)的取值范围是 [ 0 , 1 ] [0, 1] [0,1],没办法直接存储负数。所以,要把范围在 [ − 1 , 1 ] [-1, 1] [1,1] 的法线向量映射到 [ 0 , 1 ] [0, 1] [0,1] 这个范围,才能存进纹理里。

1.1.3. 映射规则

映射的公式为:
mappedValue = originalValue + 1 2 \text{mappedValue} = \frac{\text{originalValue} + 1}{2} mappedValue=2originalValue+1

对于一个没有任何倾斜,垂直于表面的法线向量,其值是 ( 0 , 0 , 1 ) (0, 0, 1) (0,0,1)。按照上述映射公式进行转换:

  • 红色分量: x = 0 x = 0 x=0,映射后的值为 0 + 1 2 = 0.5 \frac{0 + 1}{2} = 0.5 20+1=0.5
  • 绿色分量: y = 0 y = 0 y=0,映射后的值为 0 + 1 2 = 0.5 \frac{0 + 1}{2} = 0.5 20+1=0.5
  • 蓝色分量: z = 1 z = 1 z=1,映射后的值为 1 + 1 2 = 1 \frac{1 + 1}{2} = 1 21+1=1
    转换后的颜色值是 ( 0.5 , 0.5 , 1 ) (0.5, 0.5, 1) (0.5,0.5,1),在 RGB 颜色空间里,这个颜色偏向蓝色,这是因为蓝色分量的值最大,达到了 1。

1.1.4. 切线空间

法线贴图使用一个称为切线空间的坐标空间,它是相对于网格曲面定义的。对于任何给定点,切线空间中的Z轴指向网格几何体法线向量的方向,这意味着存储的每个法线向量都将具有正的Z值 ,因此,每一像素的蓝色值都大于0.5。因而会更偏向蓝色。

切线空间是一个局部的三维坐标空间,它是相对于模型表面上的每一个点定义的。在这个空间中,每个点都有一组相互垂直的基向量,分别是切线向量(Tangent)、副切线向量(Bitangent,也叫副法线)和法线向量(Normal),通常用 T 、 B 、 N 表示,这三个向量构成了一个右手坐标系。

  • 法线向量(N) :垂直于模型表面,指向表面的外侧,定义了表面的朝向。
  • 切线向量(T) :沿着纹理坐标 U 轴的方向,位于表面的切平面上。
  • 副切线向量(B) :沿着纹理坐标 V 轴的方向,也位于表面的切平面上,并且与切线向量和法线向量都垂直。

1.1.5. 作用

  1. 可复用性 :切线空间中的法线贴图可以应用到不同的模型上。因为切线空间是基于每个模型表面点的局部空间,所以同一张法线贴图可以在不同的模型上产生合理的光照效果,而不需要为每个模型单独创建法线贴图。
  2. 方便光照计算 :在进行光照计算时,将光照向量和视线向量转换到切线空间中,可以简化计算过程。因为在切线空间中,法线向量的方向是固定的(通常是 (0, 0, 1) ),这样可以更方便地进行点积等运算。
  3. 支持模型变形 :当模型进行变形(如骨骼动画)时,切线空间中的法线贴图仍然可以正确地计算光照效果。因为切线空间是随着模型表面的变化而变化的。

1.2. 叉积

副切线向量是一个同时垂直于切线向量和法线向量的向量,它的计算方式是通过对切线向量和法线向量进行叉积得到的。

如果我们计算左图中两个向量的叉积,得到的向量就是右图中绿色向量(竖直向上的向量)。

GLSL 中的叉积函数是 cross,其语法如下:

vec3 cross(vec3 a, vec3 b);

1.3. 法线贴图的工作原理

法线贴图的工作原理是将法线信息存储在一张纹理中,然后通过纹理采样来得到法线信息,最后将法线信息应用到物体上。
从法线贴图中采样法线信息的过程可以分为以下几个步骤:

  1. 纹理采样:首先,我们需要根据当前顶点的纹理坐标,从法线贴图中采样出一个颜色值。这个颜色值其范围是 [ 0 , 1 ] [0, 1] [0,1]
  2. 颜色到法线的转换:由于法线贴图存储的是归一化的向量,所以我们需要将采样出的颜色值转换回一个归一化的向量。这个转换的过程通常是将颜色值的每个分量从 [ 0 , 1 ] [0, 1] [0,1] 映射到 [ − 1 , 1 ] [-1, 1] [1,1]
  3. 切线空间转换:由于法线贴图是在切线空间中定义的,所以我们需要将采样出的法线向量转换到模型的切线空间中。这个转换的过程通常是将切线空间中的法线向量与切线空间中的副切线向量和切线空间中的法线向量进行叉积得到的。
  4. 法线贴图应用:最后,我们将转换后的法线向量应用到物体上,从而实现凹凸感。这个过程通常是将法线向量与光照向量进行点积,得到一个光照强度值,然后根据这个光照强度值来计算物体的光照效果。

1.3.1. TBN矩阵

TBN 矩阵是一个 3x3 的矩阵,它由切线向量、副切线向量和法线向量组成。TBN 矩阵的作用是将法线向量从模型空间转换到切线空间中,从而实现法线贴图的应用。TBN 矩阵可以通过以下公式计算得到:
T B N = [ T x B x N x T y B y N y T z B z N z ] TBN = \begin{bmatrix} T_x & B_x & N_x \\ T_y & B_y & N_y \\ T_z & B_z & N_z \end{bmatrix} TBN= TxTyTzBxByBzNxNyNz
其中, T x , T y , T z T_x, T_y, T_z Tx,Ty,Tz 分别是切线向量的 x、y、z 分量, B x , B y , B z B_x, B_y, B_z Bx,By,Bz 分别是副切线向量的 x、y、z 分量, N x , N y , N z N_x, N_y, N_z Nx,Ny,Nz 分别是法线向量的 x、y、z 分量。

openframeworks 默认不支持切线向量,我们采用折中的办法,将切线向量存贮在网格的顶点属性中。在计算 TBN 矩阵时,我们使用顶点属性中的切线向量来计算 TBN 矩阵。

layout (location =0) in vec3 pos;
//原本location=1的位置是颜色 ,我们将它改成了切线向量
layout (location =1) in vec4 tan;
layout (location =2) in vec3 nrm;
layout (location =3) in vec2 uv;

其中,pos 是顶点的位置,tan 是顶点的切线向量,nrm 是顶点的法线向量,uv 是顶点的纹理坐标。

1.3.1.1. 计算 TBN 矩阵
//计算切线向量
vec3 T = normalize(normal * tan.xyz, 0.0);
//计算副切线向量
vec3 B = normalize(normal*cross(tan.xyz,nrm) );
//计算法线向量
vec3 N = normalize(normal * nrm);mat3 TBN = mat3(T, B, N);

其中,normal 是法线矩阵,tan.xyz 是顶点的切线向量。

1.4. 法线贴图的应用(带凹凸的盾牌)


上图中左图是使用法线贴图之前的效果,右边未使用法线贴图。可以看到,左图使用法线贴图之后,盾牌的凹凸感更加明显,光照效果也更加真实。

1.4.1. ofApp.cpp

1.4.1.1. 计算切线向量

以下是计算切线向量的代码,其中 mesh 是要计算切线向量的网格。

void calcTangents(ofMesh& mesh)
{using namespace glm;std::vector<vec4> tangents;tangents.resize(mesh.getNumVertices());uint indexCount = mesh.getNumIndices();const vec3* vertices = mesh.getVerticesPointer();const vec2* uvs = mesh.getTexCoordsPointer();const uint* indices = mesh.getIndexPointer();for (uint i = 0; i < indexCount - 2; i += 3){const vec3& v0 = vertices[indices[i]];const vec3& v1 = vertices[indices[i + 1]];const vec3& v2 = vertices[indices[i + 2]];const vec2& uv0 = uvs[indices[i]];const vec2& uv1 = uvs[indices[i + 1]];const vec2& uv2 = uvs[indices[i + 2]];vec3 edge1 = v1 - v0;vec3 edge2 = v2 - v0;vec2 dUV1 = uv1 - uv0;vec2 dUV2 = uv2 - uv0;float f = 1.0f / (dUV1.x * dUV2.y - dUV2.x * dUV1.y);vec4 tan;tan.x = f * (dUV2.y * edge1.x - dUV1.y * edge2.x);tan.y = f * (dUV2.y * edge1.y - dUV1.y * edge2.y);tan.z = f * (dUV2.y * edge1.z - dUV1.y * edge2.z);tan.w = 0;tan = normalize(tan);tangents[indices[i]] += (tan);tangents[indices[i + 1]] += (tan);tangents[indices[i + 2]] += (tan);}int numColors = mesh.getNumColors();for (int i = 0; i < tangents.size(); ++i){vec3 t = normalize(tangents[i]);if (i >= numColors){mesh.addColor(ofFloatColor(t.x, t.y, t.z, 0.0));}else{mesh.setColor(i, ofFloatColor(t.x, t.y, t.z, 0.0));}}
}
1.4.1.2. setup()
  1. 调用 calcTangents 函数计算切线向量。
  2. 加载法线贴图。
void ofApp::setup()
{ofDisableArbTex();ofEnableDepthTest();shieldMesh.load("shield.ply");calcTangents(shieldMesh); // 计算切线shieldShader.load("mesh.vert", "diffuse.frag");diffuseImage.load("shield_diffuse.png");specImage.load("shield_spec.png");normalImage.load("shield_normal.png"); // 法线贴图directionalLightData.direction = glm::vec3(1.0f, -1.0f, 0.0f); directionalLightData.color = glm::vec3(1.0f, 1.0f, 1.0f); // 白色directionalLightData.intensity = 1.0f; // 强度		
}
1.4.1.3. draw()
  1. 设置光照参数。
  2. 绘制网格。
void ofApp::draw()
{//其它代码shieldShader.setUniformTexture("normalTex", normalImage.getTexture(), 2); // 纹理//其它代码}

1.4.2. mesh.vert

计算TBN矩阵,将TBN矩阵传递给片元着色器。

#version 410layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 tan; //tangent 切线 ,借用颜色通道
layout (location = 2) in vec3 nrm; //normal 法线
layout (location = 3) in vec2 uv;uniform mat4 mvp;
uniform mat3 normalMatrix;
uniform mat4 model; //模型矩阵
out vec2 fragUV;
out vec3 fragNormal;
out vec3 fragWorldPos; //片元的世界坐标out mat3 TBN; // 切线空间矩阵,用于法线贴图
void main()
{gl_Position =  vec4( pos, 1.0);gl_Position = mvp * gl_Position;fragUV = vec2(uv.x, 1.0-uv.y);fragNormal =normalMatrix * nrm;fragWorldPos =( model * vec4( pos, 1.0)).xyz; //片元的世界坐标vec3 T = normalize(normalMatrix * tan); //切线vec3 N = normalize(normalMatrix * nrm); //法线vec3 B = cross(N, T); //副切线TBN = mat3(T, B, N);
}

1.4.3. diffuse.frag

重要代码:

  1. 从法线贴图中采样法线信息。
  2. 将法线信息从 [ 0 , 1 ] [0, 1] [0,1] 映射到 [ − 1 , 1 ] [-1, 1] [1,1]
  3. TBN与法线向量相乘以得到最终片元使用的法线向量,即从切线空间转换到世界空间。
#version 410uniform vec3 lightDir; // light directio
uniform vec3 lightCol; // light color
//uniform vec3 meshCol;	// mesh color
//uniform vec3 meshSpecCol; // mesh specular color
uniform vec3 rimCol; // rim color
uniform  vec3 cameraPos; //相机的世界坐标
uniform vec3 ambientCol; //环境光
uniform sampler2D diffuseTex; //diffuse texture
uniform sampler2D specularTex; //specular texture
uniform sampler2D normalTex; in vec2 fragUV;
in vec3 fragNormal;
out vec4 outCol;
in vec3 fragWorldPos; //片元的世界坐标
in mat3 TBN; // 切线空间矩阵,用于法线贴图void main()
{//outCol = vec4(fragUV, 0.0, 1.0);//vec3 normal=normalize(fragNormal);vec3 normalTexture=texture(normalTex,fragUV).rgb;vec3 normal=normalize(normalTexture*2.0-1.0);normal=normalize(TBN*normal);float diffuse = max(0.0, dot(normal, lightDir));vec3 meshCol=texture(diffuseTex,fragUV).rgb;vec3 meshSpecCol=texture(specularTex,fragUV).rgb;vec3 diffuseCol=meshCol*lightCol*diffuse;vec3 toCam=(normalize(cameraPos-fragWorldPos));//vec3 reflectDir=normalize(reflect(-lightDir,normal));//float specAmt=max(0.0,dot(reflectDir,toCam));vec3 halfDir=normalize(lightDir+toCam);float specAmt=max(0.0,dot(halfDir,normal));specAmt=pow(specAmt,2.0f);vec3 specCol=meshSpecCol*lightCol*specAmt;vec3 ambient=ambientCol*meshCol;//outCol = vec4(lightCol* diffuse * meshCol , 1.0);outCol = vec4(ambient + diffuseCol + specCol, 1.0);}

1.5. 水面

下图是动图,受制于网络,gif图效果较差。建议实际运行查看。
[外链图片转存中…(img-QjIiFMTW-1744813483940)]

图中能看到水面及反光效果,这是通过法线贴图实现的。
下图为法线贴图

核心代码如下:

1.5.1. water.vert

	float t=time*0.05;float t2=time*0.02;fragUV = vec2(uv.x+t, 1.0-uv.y)*3.0f;fragUV2 = vec2(uv.x+t2, 1.0-uv.y)*2.0f;

1.5.2. water.frag

	vec3 normal=texture(normalTex,fragUV).rgb;normal=(normal*2.0-1.0);vec3 normal2=texture(normalTex,fragUV2).rgb;normal2=(normal2*2.0-1.0);normal=normalize(TBN*(normal+normal2));

代码解析

  • t 和 t2 是根据时间 time 计算得到的偏移量, t 的变化速度是 time 的 0.05 倍, t2 的变化速度是 time 的 0.02 倍。通过不同的变化速度,可以模拟出不同频率的水波效果。
  • fragUV 和 fragUV2 是经过处理后的纹理坐标。在原始纹理坐标 uv 的基础上, x 分量分别加上 t 和 t2 ,实现纹理在 x 方向上的动态偏移,模拟水波的流动。 y 分量取 1.0 - uv.y 是为了反转纹理的垂直方向。最后分别乘以 3.0f 和 2.0f 来调整纹理的缩放比例
  • 将两个采样得到的法线向量 normal 和 normal2 相加,融合不同频率的水波法线信息,模拟更复杂的水波效果。

代码解析

  • t 和 t2 是根据时间 time 计算得到的偏移量, t 的变化速度是 time 的 0.05 倍, t2 的变化速度是 time 的 0.02 倍。通过不同的变化速度,可以模拟出不同频率的水波效果。
  • fragUV 和 fragUV2 是经过处理后的纹理坐标。在原始纹理坐标 uv 的基础上, x 分量分别加上 t 和 t2 ,实现纹理在 x 方向上的动态偏移,模拟水波的流动。 y 分量取 1.0 - uv.y 是为了反转纹理的垂直方向。最后分别乘以 3.0f 和 2.0f 来调整纹理的缩放比例
  • 将两个采样得到的法线向量 normal 和 normal2 相加,融合不同频率的水波法线信息,模拟更复杂的水波效果。

注:也可以只采用fragUV,不采用fragUV2,也能看到水波效果,但效果不如采用两个纹理坐标得到的好。

相关文章:

OpenGL shader开发实战学习笔记:第十章 法线贴图

1. 10 法线贴图 1.1. 什么是法线贴图 我们如果想要在盾牌上实现凹凸感&#xff0c;应该如何做&#xff1f;一种方法是添加更多的顶点来建模更多的细节&#xff0c;但是网格的顶点越多&#xff0c;渲染网格所需的顶点着色器计算就越多&#xff0c;网格占用的内存就越多。大多数…...

神经光子渲染:物理级真实感图像生成——从麦克斯韦方程到深度学习

一、技术背景与核心突破 2025年&#xff0c;神经光子渲染&#xff08;Photonic Neural Rendering, PNR&#xff09;技术通过物理光学方程与神经辐射场的深度融合&#xff0c;在AIGC检测工具&#xff08;如GPTDetector 5.0&#xff09;的识别准确率从98%降至12%。该技术突破性地…...

MCP 协议知识分享

MCP 协议知识分享 一、MCP 协议概述1.1 定义与背景1.2 核心价值1.3 与传统 API 的对比 二、技术架构与工作原理2.1 核心组件2.2 通信机制2.3 典型工作流程 三、关键技术与应用场景3.1 核心技术3.2 典型应用场景 四、与微软技术的集成4.1 Azure OpenAI 服务4.2 Playwright MCP 服…...

spring boot 文件下载

1.添加文件下载工具依赖 Commons IO is a library of utilities to assist with developing IO functionality. <dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version> </depe…...

Redis --- 基本数据类型

Redis --- 基本数据类型 Redis Intro5种基础数据类型 Redis Intro Redis&#xff08;Remote Dictionary Server&#xff09;是一款开源的高性能键值存储系统&#xff0c;常用于缓存、消息中间件和实时数据处理场景。以下是其核心特点、数据类型及典型使用场景&#xff1a; 核心…...

随机IP的重要性:解锁网络世界的无限可能

IP地址不仅是连接互联网的“身份证”&#xff0c;更是企业、开发者和个人用户实现高效运营与安全防护的核心工具。然而&#xff0c;固定IP的局限性日益凸显——从隐私泄露到访问受限&#xff0c;从爬虫封禁到商业竞争壁垒&#xff0c;这些问题如何破解&#xff1f;答案就是随机…...

C#: 用Libreoffice实现Word文件转PDF

现实场景中要实现Word格式转PDF格式还是比较常见的。 如果要用开源的组件&#xff0c;只有用Libreoffice了。 一、下载安装Libreoffice 先进入如下链接&#xff0c;找到最新版本和匹配的操作系统来安装。 官网试过&#xff0c;下载是能下载&#xff0c;但安装了用不了&…...

客户验收标准模糊,如何明确

客户验收标准模糊往往会导致项目延迟、质量不符合期望或客户不满意&#xff0c;明确验收标准的关键在于与客户的充分沟通、制定清晰的文档、并确保双方对目标一致性达成共识。在项目的执行过程中&#xff0c;如果客户未能明确表达他们的验收标准&#xff0c;或者项目团队未能确…...

Halcon应用:九点标定-手眼标定

提示&#xff1a;若没有查找的算子&#xff0c;可以评论区留言&#xff0c;会尽快更新 Halcon应用&#xff1a;九点标定-手眼标定 前言一、Halcon应用&#xff1f;二、应用实战1、图形理解[eye-to-hand]&#xff1a;1.1、开始应用2 图形理解[eye-in-hand] 前言 本篇博文主要用…...

springboot3 cloud gateway 配置websocket代理转发教程

前言 最近微服务的项目&#xff0c;需要集成websocket的功能&#xff0c;我在其中的一个微服务模块中集成websocket代码实现&#xff0c;通过模块的端口测试正常&#xff0c;但是通过springboot cloud gateway的端口访问&#xff0c;连接失败&#xff01;我通过各种百度、和AI…...

详解与FTP服务器相关操作

目录 什么是FTP服务器 搭建FTP服务器相关 ​编辑 Unity中与FTP相关的类 上传文件到FTP服务器 使用FTP服务器上传文件的关键点 开始上传 从FTP服务器下载文件到客户端 使用FTP下载文件的关键点 开始下载 关于FTP服务器的其他操作 将文件的上传&#xff0c;下载&…...

制作一款打飞机游戏教程8:抖动

我们讨论了爆炸效果&#xff0c;这是非常重要的内容。我们制作了一个可以改变大小的小圆点&#xff0c;并展示了一些微调&#xff0c;比如绘制的圆圈数量和颜色调整等。但我们也提到将要做一些重大改变&#xff0c;这些改变将涉及到颜色的使用方式。 颜色使用方式的改变 目前…...

Linux搭建环境:从零开始掌握基础操作(四)

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 软件测试第一步就是搭建测试环境&#xff0c;如何搭建好测试环境&#xff0c;需要具备两项的基础知识&#xff1a; 1、Linux 命令: 软件测试第一个任务, 一般都需要进行环境搭建, 一部分&#xff0c;环境搭建内容是在服…...

第2.4节:学会像AWK一样思考

1 第2.4节&#xff1a;学会像AWK一样思考 AWK的工作方式类似于工厂的流水线。文本数据就像流水线上的产品&#xff0c;AWK逐行读取这些文本&#xff0c;对每行文本进行分割处理&#xff0c;然后通过一系列的模式匹配和动作执行来完成特定的任务。下面我们详细介绍AWK的工作流程…...

内网穿透原理解析、使用网络场景、及如何实现公网访问步骤教程

​不多废话&#xff0c;一文了解内网穿透原理解析、使用网络场景、及如何实现公网访问步骤教程。 一&#xff0c;内网穿透原理解析 内网穿透的核心原理是通过中间服务器端口数据转发或点到点技术建立端对端的直连通信通道&#xff0c;使外网设备能够访问内网设备和服务。 1&…...

购买电脑时,主要需要关注以下核心配置,它们直接影响性能、使用体验和价格。根据需求(办公、游戏、设计、编程等),侧重点会有所不同。看看Deepseek的建议

1. 处理器&#xff08;CPU&#xff09; 作用&#xff1a;电脑的“大脑”&#xff0c;影响整体运算速度和多任务处理能力。关键参数&#xff1a; 品牌与型号&#xff1a;Intel&#xff08;酷睿i3/i5/i7/i9&#xff09;或 AMD&#xff08;锐龙R3/R5/R7/R9&#xff09;。核心/线程…...

数据结构与算法[零基础]---4.树和二叉树

四、树和二叉树 &#xff08;一&#xff09;树 1.相关定义 树是由一个或多个结点组成的有限集T&#xff0c;它满足以下两个条件&#xff1a;第一个是有一个特定的结点&#xff0c;作为根结点&#xff1b;第二个其余的结点分成m(m>0)个互不相交的有限集T0&#xff0c;T1,.…...

Sklearn入门之数据预处理preprocessing

、 Sklearn全称:Scipy-toolkit Learn是 一个基于scipy实现的的开源机器学习库。它提供了大量的算法和工具&#xff0c;用于数据挖掘和数据分析&#xff0c;包括分类、回归、聚类等多种任务。本文我将带你了解并入门Sklearn下的preprocessing在机器学习中的基本用法。 获取方式…...

4.16学习总结 IO流综合练习

爬虫获取网站内的数据&#xff0c;获得完整姓名 网站一&#xff1a;姓氏 网站二&#xff1a;男生名字 网站三&#xff1a;女生名字 进行拼接&#xff0c;获取完整的男生女生姓名。 //导包 import org.apache.commons.io.FileUtils; import java.io.*; import java.io.IOEx…...

大模型全景解析:从技术突破到行业变革

目录 一、引言&#xff1a;人工智能的新纪元 二、大模型发展历史与技术演进 1. 早期探索期&#xff08;2015-2017&#xff09;&#xff1a;从"人工智障"到初具规模 RNN/LSTM架构时代&#xff08;2013-2017&#xff09; Transformer革命&#xff08;2017&#xf…...

充电宝项目中的MQTT(轻量高效的物联网通信协议)

文章目录 补充&#xff1a;HTTP协议MQTT协议MQTT的核心特性MQTT vs HTTP&#xff1a;关键对比 EMQX项目集成EMQX集成配置客户端和回调方法具体接口和方法处理处理类 补充&#xff1a;HTTP协议 HTTP是一种应用层协议&#xff0c;使用TCP作为传输层协议&#xff0c;默认端口是80…...

AgentOps - 帮助开发者构建、评估和监控 AI Agent

文章目录 一、关于 AgentOps二、关键集成 &#x1f50c;三、快速开始 ⌨️2行代码中的Session replays 首类开发者体验 四、集成 &#x1f9be;OpenAI Agents SDK &#x1f587;️CrewAI &#x1f6f6;AG2 &#x1f916;Camel AI &#x1f42a;Langchain &#x1f99c;&#x1…...

n8n 为技术团队打造的安全工作流自动化平台

AI MCP 系列 AgentGPT-01-入门介绍 Browser-use 是连接你的AI代理与浏览器的最简单方式 AI MCP(大模型上下文)-01-入门介绍 AI MCP(大模型上下文)-02-awesome-mcp-servers 精选的 MCP 服务器 AI MCP(大模型上下文)-03-open webui 介绍 是一个可扩展、功能丰富且用户友好的…...

MyBatis:SpringBoot结合MyBatis、MyBatis插件机制的原理分析与实战

&#x1fa81;&#x1f341; 希望本文能给您带来帮助&#xff0c;如果有任何问题&#xff0c;欢迎批评指正&#xff01;&#x1f405;&#x1f43e;&#x1f341;&#x1f425; 文章目录 一、背景二、Spring Boot项目中结合MyBatis2.1 数据准备2.2 pom.xml依赖增加2.3 applicat…...

【数据结构】3.单链表专题

文章目录 单链表的实现0、准备工作1、链表的打印2、尾插3、头插4、尾删5、头删6、查找指定数据的位置7、在指定位置之前插入数据8、在指定位置之后插入数据9、删除指定位置的数据10、删除指定位置之后的数据11、单链表的销毁 单链表的实现 什么是单链表呢&#xff1f;单链表可…...

**Microsoft Certified Professional(MCP)** 认证考试

1. MCP 认证考试概述 MCP&#xff08;Microsoft Certified Professional&#xff09;是微软认证体系中的一项入门级认证&#xff0c;旨在验证考生在微软产品和技术&#xff08;如 Windows Server、Azure、SQL Server、Microsoft 365&#xff09;方面的技能。2020 年&#xff0…...

C++学习之游戏服务器开发git命令

目录 1.服务器需求分析 2.面向框架编程简介 3.ZINX框架初始 4.回显标准输入 5.VS结合GIT 6.完善readme范例 7.添加退出功能 8.添加命令处理类 9.添加日期前缀思路 10.添加日期前缀功能 1.服务器需求分析 zinx 描述 zinx 框架是一个处理多路 IO 的框架。在这个框架中提…...

Maven 多仓库与镜像配置全攻略:从原理到企业级实践

Maven 多仓库与镜像配置全攻略&#xff1a;从原理到企业级实践 一、核心概念&#xff1a;Repository 与 Mirror 的本质差异 在 Maven 依赖管理体系中&#xff0c;repository与mirror是构建可靠依赖解析链的两大核心组件&#xff0c;其核心区别如下&#xff1a; 1. Repositor…...

无锁队列--知识分享

目录 无锁队列 无锁队列是什么 为什么需要无锁队列 队列的类型 无锁队列的分类 ringbuffer&#xff08;SPSC&#xff09; ret_ring&#xff08;MPMC&#xff09; 无锁队列 无锁队列是什么 无锁队列通过原子操作来实现线程安全的队列&#xff0c;属于非阻塞队列 …...

Flask快速入门

1.安装 Flask 要使用 Flask&#xff0c;你需要先安装它。打开终端&#xff0c;运行以下命令&#xff1a; pip install flask 2.创建文件结构 3.app.py from flask import Flask&#xff1a;从 flask 库中导入 Flask 类。app Flask(__name__)&#xff1a;创建一个 Flask 应…...

LeetCode -- Flora -- edit 2025-04-16

1.两数之和 1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。 你可以按…...

【Unity笔记】实现可视化配置的Unity按键输入管理器(按下/长按/松开事件 + UnityEvent绑定)

【Unity笔记】实现可视化配置的Unity按键输入管理器 适用于角色控制、技能触发的Unity按键输入系统&#xff0c;支持UnityEvent事件绑定、长按/松开监听与启用开关 一、引言 在 Unity 游戏开发中&#xff0c;处理键盘输入是最常见的交互方式之一。尤其是角色控制、技能释放、菜…...

SpringMVC学习(请求与响应。常见参数类型接收与响应。@RequestParam、@RequestBody的使用)(详细示例)

目录 一、请求与响应。(RequestMapping) &#xff08;1&#xff09;使用注解RequestMapping对业务模块区分。 StudentController。 TeacherController。 &#xff08;2&#xff09;Apifox请求与响应。 "/student/login"。 "/teacher/login"。 二、常见参数…...

springboot 切面拦截自定义注解

使用切面来拦截被该注解标记的方法 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>1. 定义自定义注解 import java.lang.annotation.ElementType; imp…...

QT —— 信号和槽(自定义信号和槽函数)

QT —— 信号和槽&#xff08;自定义信号和槽函数&#xff09; 自定义信号和槽函数一、自定义信号函数规范1. 声明位置2. 返回值与实现3. 参数与重载 二、自定义槽函数规范1. 声明位置&#xff08;不同版本差异&#xff09;2. 返回值与实现3. 参数与重载 三、信号发射规范1. 基…...

朋克编码以潮玩语言讲述中国文化|益民艺术馆展演东方潮力

朋克编码于广州益民艺术馆推出“艺术家潮玩”系列主题展&#xff0c;将传统文化元素融入 潮玩设计&#xff0c;并通过数字科技与空间场景创新&#xff0c;讲述中国故事、传递东方美学。 展览作品结合太空猿等原创 IP 与“中式元素”视觉符号&#xff0c;引发观众情感共鸣。“我…...

TA学习之路——2.2 模型与材质基础

1.模型基础 1.1 图形渲染管线 1.2 模型实现的原理 点连成线,线构成面,面构成模型。 1.2 UV UV例如一个正方体的纸盒展开,平铺在一个二维的坐标系中。 模型的每一个顶点在三维空间和二维空间中都能一 一对应。在二维坐标系中的顶点对应的位置就是顶点的纹理坐标。 因此…...

helm的go模板语法学习

1、helm chart 1.0、什么是helm&#xff1f; 介绍&#xff1a;就是个包管理器。理解为java的maven、linux的yum就好。 安装方法也可参见官网&#xff1a; https://helm.sh/docs/intro/install 通过前面的演示我们知道&#xff0c;有了helm之后应用的安装、升级、查看、停止都…...

Windows 图形显示驱动开发-WDDM 1.2功能—Windows 8 中的 DirectX 功能改进(一)

Windows 8包括 Microsoft DirectX 功能改进&#xff0c;使开发人员、最终用户和系统制造商受益。 功能改进在以下几个方面&#xff1a; 像素格式 (5551、565、4444) &#xff1a;在低功耗硬件配置下&#xff0c;DirectX 应用程序的性能更高。双精度着色器功能&#xff1a;高级…...

软件测试|App测试面试相关问题(2)

一、App 稳定怎么做的?Monkey 怎么用(App 稳定测试)? 稳定性这块&#xff0c;我们当时用的是SDK 自动的一个Monkey 工具进行测试的&#xff0c;其实Monkey工具主要通过模拟用户发送伪随机时间去操作软件&#xff0c;通过执行Monkey 命令&#xff0c;它会自动出报告&#xff…...

模拟电路需要了解的一些基础知识(部分)

基本的单路元件 1. 电阻&#xff1b;特性&#xff1a;阻碍电流流动&#xff0c;消耗电能并转化为热能&#xff08;遵循欧姆定律&#xff09;。是无源元件&#xff0c;应用&#xff1a;限流、分压、发热等&#xff1b; 2. 电容&#xff1b;特性&#xff1a;存储电荷和电场能&am…...

[特殊字符] MySQL MCP 开发实战:打造智能数据库操作助手

&#x1f4a1; 简介&#xff1a;本文详细介绍如何利用MCP&#xff08;Model-Control-Panel&#xff09;框架开发MySQL数据库操作工具&#xff0c;使AI助手能够直接执行数据库操作。 &#x1f4da; 目录 引言MCP框架简介项目架构设计开发环境搭建核心代码实现错误处理策略运行和…...

软考备考(一)学习笔记

一、软考介绍 计算机软考,计算机技术与软件专业技术资格(水平)考试 一年考试两次: 一次上旬(5月底),下旬一次(11月初) 初级资格:程序员 中级资格: 软件设计师 高级资格: 系统架构设计师 初级: 科目一:计算机硬软件基础知识 150min 笔试、选择 科目二:程序设…...

Linux环境变量

目录 环境变量 基本概念 常见环境变量 查看环境变量方法 测试PATH 测试HOME 和环境变量相关的命令 环境变量的组织方式 通过代码如何获取环境变量 通过系统调用获取或设置环境变量 ​编辑 环境变量通常是具有全局属性的 实验 环境变量 基本概念 环境变量(environment variables…...

跨浏览器书签同步方案:WebDAV + Floccus插件实操指南

FloccusWebDAV能够帮助把多个不同浏览器书签统一私有化管理&#xff0c;以下是介绍&#xff1a; Floccus 是一个允许用户在不同浏览器和设备之间私密同步书签的扩展&#xff0c;开源地址&#xff1a;https://github.com/floccusaddon/floccusWebDAV是一种基于HTTP的协议&#…...

银河麒麟系统 达梦8 安装 dlask 框架后端环境

适配的一套环境为 dmPython2.5.8 dmSQLAlchemy1.4.39 Flask2.0.3 Flask-Cors3.0.10 Flask-SQLAlchemy2.5.1 SQLAlchemy1.4.54 Werkzeug2.2.2其中 # sqlalchemy-dm1.4.39 通过dmdbms目录内文件进行源码安装 (MindSpore) [ma-user python]$pwd /home/syl/dmdbms/drivers/python…...

代码随想录算法训练营Day31

力扣738.单调递增的数字【medium】 力扣968.监控二叉树【hard】 一、力扣738.单调递增的数字【medium】 题目链接&#xff1a;力扣738.单调递增的数字 视频链接&#xff1a;代码随想录 1、思路 先将整数转为字符串变成可迭代对象&#xff0c;再转为列表从后向前遍历&#xff…...

LeetCode Hot100 刷题笔记(10)—— ACM格式输入输出练习

目录 Trick: 1. 只有输出 2. 单组_AB 3. 多组_AB_EOF形式 4. 多组_AB_T组形式 5. 多组_AB_零尾形式 6. 单组_一维数组 7. 多组_二维数组_T组形式 8. 单组_二维数组 9. 多组_二维数组_T组形式 10. 单组_字符串 11. 多组_字符串_T组形式 12. 单组_二维字符数组 13. 多组_带空格的…...

iPaaS集成平台在制造业有哪些应用场景

在制造业迈向智能化的进程中&#xff0c;“数据不通”“系统割裂”“响应迟缓”等问题如同隐形的锁链&#xff0c;束缚着企业转型升级的步伐。面对设备、系统、供应链之间错综复杂的连接需求&#xff0c;传统定制化开发周期长、成本高&#xff0c;难以满足快速变化的业务需求。…...

【Docker项目实战】使用Docker部署Gitblit服务器

【Docker项目实战】使用Docker部署Gitblit服务器 一、Gitblit介绍1.1 Gitblit 介绍1.2 主要特点 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Gitblit镜像五、部署Gitbli…...