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

OpenGL ES 绘制一张图片

GLSL 语法与内建函数

GLSL 的修饰符与数据类型

GLSL 中变量的修饰符
  • const:修饰不可被外界改变的常量
  • attribute:修饰经常更改的变量,只可以在顶点着色器中使用
  • uniform:修饰不经常更改的变量,可用于顶点着色器和片段着色器
  • varying:修饰在顶点着色器计算,然后传递到片元着色器中使用的变量
GLSL 的基本数据类型
  • int
  • float
  • bool
    float 是可以再加一个修饰符的,这个修饰符用来指定精度。
  • highp:32bit,一般用于顶点坐标(vertex Coordinate)
  • medium:16bit,一般用于纹理坐标(texture Coordinate)
  • lowp:8bit,一般用于颜色表示(color)
GLSL 中就是向量类型(vec)

向量类型是 Shader 中最常用的一个数据类型,因为在做数据传递的时候经常要传递多个参数,相较于写多个基本数据类型,使用向量类型更加简单。
比如,通过 OpenGL 接口把物体坐标和纹理坐标传递到顶点着色器中,用的就是向量类型。每个顶点都是一个四维向量,在顶点着色器中利用这两个四维向量就能去做自己的运算。

attribute vec4 position;
矩阵类型(matrix)

矩阵类型在 GLSL 中同样也是一个非常重要的数据类型,在某些效果器的开发中,需要开发者自己传入一些矩阵类型的数据,用于像素计算。

uniform lowp mat4 colorMatrix;

上面的代码表示的是一个 44 的浮点矩阵,如果是 mat2 的声明,代表的就是 22 的浮点矩阵,而 mat3 代表的就是 3*3 的浮点矩阵。
OpenGL 为开发者提供了以下接口,把内存中的数据(mColorMatrixLocation)传递给着色器。

glUniformMatrix4fv(mColorMatrixLocation, 1, false, mColorMatrix);

其中,mColorMatrix 是这个变量在接口程序中的句柄。这里一定要注意,上边的这个函数不属于 GLSL 部分,而是属于客户端代码,也就是说,我们调用这个函数来和着色器进行交互。

纹理类型

一般只在片元着色器中使用,下面 GLSL 代码是二维纹理类型的声明方式。

uniform sampler2D texSampler;

首先我们需要拿到这个变量的句柄,定义为 mGLUniformTexture,然后就可以给它绑定一个纹理,接口程序的代码如下:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texId);
glUniform1i(mGLUniformTexture, 0);

注意,上述接口程序中的第一行代码激活的是哪个纹理句柄,在第三行代码中的第二个参数就需要传递对应的 Index,就比如说代码中激活的纹理句柄是 GL_TEXTURE0,对应的第三行代码中的第二个参数 Index 就是 0,如果激活的纹理句柄是 GL_TEXTURE1,那对应的 Index 就是 1,句柄的个数在不同的平台不一样,但是一般都会在 32 个以上。

传递类型

在 GLSL 中有一个特殊的修饰符就是 varying,这个修饰符修饰的变量都是用来在顶点着色器和片元着色器之间传递参数的。
最常见的使用场景就是在顶点着色器中修饰纹理坐标,顶点着色器会改变这个纹理坐标,然后把这个坐标传递到片元着色器,代码如下:

attribute vec2 texcoord;
varying vec2 v_texcoord;
void main(void)
{//计算顶点坐标v_texcoord = texcoord;
}

接着在片元着色器中也要声明同名的变量,然后使用 texture2D 方法来取出二维纹理中这个纹理坐标点上的纹理像素值,代码如下:

varying vec2 v_texcoord;
vec4 texel = texture2D(texSampler, v_texcoord);

取出了这个坐标点上的像素值,就可以进行像素变化操作了,比如说去提高对比度,最终将改变的像素值赋值给 gl_FragColor。

GLSL 的内置变量与内嵌函数

内置变量

常见的是两个 Shader 的输出变量,一个是顶点着色器的内置变量 gl_position,它用来设置顶点转换到屏幕坐标的位置。

vec4 gl_posotion;

另外一个内置变量用来设置每一个粒子矩形大小,一般是在粒子效果的场景下,需要为粒子设置绘制的半径大小时使用。

float gl_pointSize;

其次是片元着色器的内置变量 gl_FragColor,用来指定当前纹理坐标所代表的像素点的最终颜色值。

vec4 gl_FragColor;

然后是 GLSL 内嵌函数部分,我们在这里只介绍常用的几个常用函数。

内嵌函数

官方文档

内嵌函数说明
abs(genType x)绝对值函数
floor(genType x)向下取整函数
ceil(genType x)向上取整函数
mod(genType x, genType y)取模函数
min(genType x, genType y)取得最小值函数
max(genType x, genType y)取得最大值函数
clamp(genType x, genType y, genType z)取得中间值函数
step(genType edge, genType x)如果x<edge,返回0.0,否则返回1.0
smoothstep(genType edge0, genType edge1, genType x)如果x<=edge0,返回0.0,如果x>=dege1,返回1.0,如果edge0<x<edge1,则执行0~1之间的平滑差值
mix(genType x, genType y, genType a)返回线性混合的x和y,用公式表示为x*(1-a)+y*a,这个函数在mix两个纹理图像的时候非常有用
对于一种语言的语法来讲,剩下的就是控制流的部分了。
GLSL 的控制流与 C 语言非常类似,既可以使用 for、while 以及 do-while 实现循环,也可以使用 if 和 if-else 进行条件分支的操作。

OpenGL ES 的纹理

OpenGL 中的纹理用 GLUint 类型来表示,通常我们称之为 Texture 或者 TextureID,可以用来表示图像、视频画面等数据。
对于二维的纹理,每个二维纹理都由许多小的片元组成,每一个片元我们可以理解为一个像素点。
大多数的渲染过程,都是基于纹理进行操作的,最简单的一种方式就是从一个图像文件加载数据,然后上传到显存中构造成一个纹理。

纹理坐标系

为了访问到纹理中的每一个片元(像素点),OpenGL ES 构造了纹理坐标空间,坐标空间的定义是从左下角的(0,0)到右上角的(1,1)。
横轴维度称为 S 轴,左边是 0,右边是 1,纵轴维度称为 T 轴,下面是 0,上面是 1。
按照这个规则就构成了左图所示的坐标系,可以看到上下左右四个顶点的坐标位置,而中间的位置就是(0.5,0.5)。
另外在这里不得不提的是计算机系统里的坐标空间,通常 X 轴称之为横轴,从左到右是 0~1,Y 轴称之为纵轴,是从上到下是 0~1,如图所示:
在这里插入图片描述
无论是计算机还是手机的屏幕坐标,X 轴是从左到右是 0~1,Y 轴是从上到下是 0~1,这种存储方式是和图片的存储是一致的。
我们这里假设图片(Bitmap)的存储是把所有像素点存储到一个大数组中,数组的第一个像素点表示的就是图片左上角的像素点(即第一排第一列的像素点),数组中的第二个元素表示的是第一排第二列的第二个像素点,依此类推。
这样你会发现这种坐标其实是和 OpenGL 中的纹理坐标做了一个旋转 180 度。 因此从本地图片中加载一张纹理并且渲染到界面上的时候,就会用到纹理坐标和计算机系统的坐标的转换。

纹理创建与绑定

创建

加载一张图片作为 OpenGL 中的纹理。首先要在显卡中创建一个纹理对象,OpenGL ES 提供了方法原型如下:

void glGenTextures (GLsizei n, GLuint* textures)

这个方法中的第一个参数是需要创建几个纹理对象,第二个参数是一个数组(指针)的形式,函数执行之后会将创建好的纹理句柄放入到这个数组中。
如果仅仅需要创建一个纹理对象的话,只需要声明一个 GLuint 类型的 texId,然后将这个纹理 ID 取地址作为第二个参数,就可以创建出这个纹理对象,代码如下:

glGenTextures(1, &texId);

执行完上面这个指令之后,OpenGL 引擎就会在显卡中创建出一个纹理对象,并且把这个纹理对象的句柄存储到 texId 这个变量中。

绑定

那接下来我们要对这个纹理对象进行操作,OpenGL ES 提供的都是类似于状态机的调用方式,也就是说在对某个 OpenGL ES 对象操作之前,先进行绑定操作,然后接下来所有操作的目标都是针对这个绑定的对象进行的。对于纹理 ID 的绑定调用代码如下:

glBindTexture(GL_TEXTURE_2D, texId);

执行完上面这个指令之后,OpenGL ES 引擎认为这个纹理对象已经处于绑定状态,那么接下来所有对于纹理的操作都是针对这个纹理对象的了,当我们操作完毕之后可以调用如下代码进行解绑:

glBindTexture(GL_TEXTURE_2D, 0);

上面这行指令执行完毕之后,就代表我们不会对 texId 这个纹理对象做任何操作了,所以上面这行代码一般在一个 GLProgram 执行完成之后调用。

过滤

首先就是纹理的过滤方式,当纹理对象被渲染到物体表面上的时候,纹理的过滤方式指定纹理的放大和缩小规则。
实际上,是 OpenGL ES 的绘制管线中将纹理的元素映射到片元这一过程中的映射规则,因为纹理(可以理解为一张图片)大小和物体(可以理解为手机屏幕的渲染区域)大小不太可能一致,所以要指定放大和缩小的时候应该具体确定每个片元(像素)是如何被填充的。
放大(magnification)规则的设置:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

缩小(minification)规则的设置:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

上述两个指令设置的过滤方式都是 GL_LINEAR,这种过滤方式叫做双线性过滤,底层使用双线性插值算法来平滑像素之间的过渡部分,OpenGL 的具体实现会使用四个邻接的纹理元素,并在它们之间用一个线性插值算法做插值,这种过滤方式是最常用的。
OpenGL 还提供了 GL_NEAREST 的过滤方式,GL_NEAREST 被称为最邻近过滤,底层为每个片段选择最近的纹理元素进行填充,缺点就是当放大的时候会丢失掉一些细节,会有很严重的锯齿效果。因为是原始的直接放大,相当于降采样。而当缩小的时候,因为没有足够的片段来绘制所有的纹理单元,也会丢失很多细节,是真正的降采样。
其实 OpenGL 还提供了另外一种技术,叫做 MIP 贴图,但是这种技术会占用更多的内存,优点是渲染也会更快。当缩小和放大到一定程度之后效果也比双线性过滤的方式更好,但是它对纹理的尺寸以及内存的占用是有一定限制的。不过,在处理以及渲染视频的时候不需要放大或者缩小这么多倍,所以在这种场景下 MIP 贴图并不适用。
综合对比这几种过滤方式,在使用纹理的过滤方式时我们一般都会选用双线性过滤的过滤方式(GL_LINEAR)。

在纹理坐标系中的 s 轴和 t 轴超出范围的纹理处理规则,常见的代码设置如下:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

上述代码表示的含义就是,给这个纹理的 s 轴和 t 轴的坐标设置为 GL_CLAMP_TO_EDGE 类型,代表所有大于 1 的像素值都按照 1 这个点的像素值来绘制,所有小于 0 的值都按照 0 这个点的像素值来绘制。
除此之外,OpenGL ES 还提供了 GL_REPEAT 和 GL_MIRRORED_REPEAT 的处理规则,从名字也可以看得出来,GL_REPEAT 代表超过 1 的会从 0 再重复一遍,也就是再平铺一遍,而 GL_MIRRORED_REPEAT 就是完全镜像地平铺一遍。

纹理的上传与下载

假设我们有一张 PNG 类型的图片,我们需要将它解码为内存中 RGBA 裸数据,所以首先我们需要解码。可以采用跨平台(C++ 层)的方式,引用 libpng 这个库来进行解码操作,当然也可以采用各自平台的 API 进行解码。无论哪一种方式,最终都可以得到 RGBA 的数据。等拿到 RGBA 的数据之后,记为 uint8_t 数组类型的 pixels。
接下来,就是要将 PNG 素材的内容放到这个纹理对象上面去

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,GL_UNSIGNED_BYTE, pixels);

执行上述指令的前提是我们已经绑定了某个纹理,OpenGL 的大部分纹理一般只接受 RGBA 类型的数据。当然在视频场景下,考虑性能问题也会使用到 GL_LUMINANCE 类型,不过需要在片元着色器中,把 YUV420P 格式转换成 RGBA 格式。
上述指令正确执行之后,RGBA 的数组表示的像素内容会上传到显卡里面 texId 所代表的纹理对象中,以后要使用这个图片,直接使用这个纹理 ID 就可以了。
既然有内存数据上传到显存的操作,那么一定也会有显存的数据回传回内存的操作

glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

执行上述指令的前提是我们已经绑定了某个纹理,然后将绑定的这个纹理对象代表的内容拷贝回 pixels 这个数组中,这个拷贝会比较耗时,并且拷贝时间会和分辨率(width\height)大小成正比。
一般在实际的开发工作中要尽量避免这种内存和显存之间的数据拷贝与传输,而是使用各个平台提供的快速映射 API 去完成内存与显存的拷贝工作。

物体坐标与纹理绘制

物体坐标系

OpenGL 规定物体坐标系中 X 轴从左到右是从 -1 到 1 变化的,Y 轴从下到上是从 -1 到 1 变化的,物体的中心点是 (0, 0) 的位置。
在这里插入图片描述
接下来的任务就是将这个纹理绘制到物体(屏幕)上,首先要搭建好各自平台的 OpenGL ES 的环境,包括上下文与窗口管理,然后创建显卡可执行程序,最终让程序跑起来。

纹理的绘制

先来看一个最简单的顶点着色器(Vertex Shader),代码如下:

static char* COMMON_VERTEX_SHADER ="attribute vec4 position;                   \n""attribute vec2 texcoord;                   \n""varying vec2 v_texcoord;                   \n""                                            \n""void main(void)                              \n""{                                              \n""   gl_Position = position;                 \n""   v_texcoord = texcoord;                  \n""}                                              \n";

片元着色器(Fragment Shader),代码如下:

static char* COMMON_FRAG_SHADER ="precision highp float;                                       \n""varying highp vec2 v_texcoord;                               \n""uniform sampler2D texSampler;                                \n""                                                               \n""void main() {                                                 \n""    gl_FragColor = texture2D(texSampler, v_texcoord);      \n""}                                                                                                                                       \n";

利用上面两个 Shader 创建好的这个 Program,我们记为 mGLProgId。

接下来我们需要将这个 Program 中的重点属性以及常量的句柄寻找出来,以备后续渲染过程中向顶点着色器和片元着色器传递数据。

mGLVertexCoords = glGetAttribLocation(mGLProgId, "position");
mGLTextureCoords = glGetAttribLocation(mGLProgId, "texcoord");
mGLUniformTexture = glGetUniformLocation(mGLProgId, "texSampler");

在这个例子里,我们要从 Program 的顶点着色器中读取两个 attribute,并放置到全局变量的 mGLVertexCoords 与 mGLTextureCoords 中,从 Program 的片元着色器中读取出来的 uniform 会放置到 mGLUniformTexture 这个变量里。
所有准备工作都做好了之后,接下来进行真正的绘制操作。
首先,规定窗口大小:

glViewport(0, 0, screenWidth, screenHeight);

函数中的参数 screenWidth 表示绘制 View 或者目标 FBO 的宽度,screenHeight 表示绘制 View 或者目标 FBO 的高度。
然后使用显卡绘制程序:

glUseProgram(mGLProgId);

设置物体坐标与纹理坐标:

GLfloat vertices[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f };
glVertexAttribPointer(mGLVertexCoords, 2, GL_FLOAT, 0, 0, vertices);
glEnableVertexAttribArray(mGLVertexCoords);

设置纹理坐标:

GLfloat texCoords1[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f };
GLfloat texCoords2[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f };
glVertexAttribPointer(mGLTextureCoords, 2, GL_FLOAT, 0, 0,  texCoords2);
glEnableVertexAttribArray(mGLTextureCoords);

代码中有两个纹理坐标数组,分别是 texCoords1 与 texCoords2,最终我们使用的是 texCoords2 这个纹理坐标。
因为我们的纹理对象是将一个 RGBA 格式的 PNG 图片上传到显卡上,其实上传上来本身就需要转换坐标系,这两个纹理坐标恰好就是做了一个上下的翻转,从而将计算机坐标系和 OpenGL 坐标系进行转换。
对于第一次上传内存数据的场景纹理坐标一般都会选用 texCoords2。
但是如果这个纹理对象是 OpenGL 中的一个普通纹理对象的话,则需要使用 texCoords1。
接下来,指定我们要绘制的纹理对象,并且将纹理句柄传递给片元着色器中的 uniform 常量:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texId);
glUniform1i(mGLUniformTexture, 0);

执行绘制操作:

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

上述这行指令执行成功之后,就相当于将最初内存中的 PNG 图片绘制到默认的 FBO 上去了,最终再通过各平台的窗口管理操作(Android 平台的 swapBuffer、iOS 平台的 renderBuffer),就可以让用户在屏幕上看到了。
当确定这个纹理对象不再使用了,则需要删掉它,执行代码是:

glDeleteTextures(1, &texId);

除此之外,关于纹理的绘制我们还要额外注意一点:我们提交给 OpenGL 的绘图指令并不会马上送给图形硬件执行,而是会放到一个指令缓冲区中。
考虑性能的问题,等缓冲区满了以后,这些指令会被一次性地送给图形硬件执行,指令比较少或比较简单的时候,是没办法填满缓冲区的,所以这些指令不能马上执行,也就达不到我们想要的效果。
因此每次写完绘图代码,想让它立即完成效果的时候,就需要我们自己手动调用 glFlush() 或 gLFinish() 函数。

  • glFlush:将缓冲区中的指令(无论是否为满)立刻送给图形硬件执行,发送完立即返回;
  • glFinish:将缓冲区中的指令(无论是否为满)立刻送给图形硬件执行,但是要等待图形硬件执行完后这些指令才返回。

相关文章:

OpenGL ES 绘制一张图片

GLSL 语法与内建函数 GLSL 的修饰符与数据类型 GLSL 中变量的修饰符 const&#xff1a;修饰不可被外界改变的常量attribute&#xff1a;修饰经常更改的变量&#xff0c;只可以在顶点着色器中使用uniform&#xff1a;修饰不经常更改的变量&#xff0c;可用于顶点着色器和片段…...

Python 之 Pandas DataFrame 数据类型的行操作和常用属性和方法汇总

文章目录一、DataFrame 行操作1. 标签选取2. 数值型索引和切片3. 切片操作多行选取4. 添加数据行4.1 追加字典4.2 追加列表5. 删除数据行二、常用属性和方法汇总1. 转置2. axes3. dtypes4. empty5. columns6. shape7. values8. head() & tail()9. 修改列名 rename()10. inf…...

MacOS下载钉钉直播回放视频的Python最新解决方案

tags: Python MacOS Tips 写在前面 之前写过一篇关于用Charles抓包下载钉钉直播回放视频的方法, 那会还是可以直接通过FFmpeg下载m3u8链接并且直接合并的, 但是现在直接上FFmpeg会出现403, 所以还是用别的方法来做吧. 后来发现抓包找到的m3u8不是加密视频流, 那就直接下载ts…...

2023年测试人跳槽新功略,涨薪10K+

软件测试是如何实现涨薪的呢&#xff1f;很多人眼中的软件测试岗位可能是简单的&#xff0c;技术含量不是那么高&#xff0c;就是看看需求、看业务、设计文档、然后点一点功能是否实现&#xff0c;再稍微深入一点就是测试下安装部署时会不会出现兼容性问题&#xff0c;以及易用…...

RabbitMQ之Work Queues

Work Queues 工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务,而不得不等待它完成。相反我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当有多个工作线程时,这些工作线程将一起处理这些任务…...

CRM哪家好?这5个CRM管理系统很好用!

CRM哪家好&#xff1f;这5个CRM管理系统很好用&#xff01; CRM(Customer Relationship Management)即客户关系管理&#xff0c;能够帮助提高客户的价值、满意度、赢利性和忠实度&#xff0c;缩减销售周期和销售成本、增加收入、寻找扩展业务所需的新的市场和渠道&#xff0c;…...

国内ce认证机构有哪些 国内十大CE认证机构排名 做ce认证的公司推荐

CE认证&#xff0c;是任何企业的任何产品走进欧盟市场的硬性要求。挑选CE认证机构&#xff0c;一定要认准拥有正规资质的机构/企业&#xff0c;例如中国质量认证中心CQC、CVC威凯、CTI华测检测&#xff0c;以及TV南德认证、安博检测、万泰认证等。本文中&#xff0c;MaiGoo小编…...

多If函数封装的策略

在工作中我们经常遇到有多个if的判读函数&#xff0c;这是一件很正常的事情&#xff0c;如下&#xff1a; let order function (orderType, isPay, count) {if (orderType 1) {// 充值 500if (isPay true) {// 充值成功console.log(中奖100元)} else {if (count > 0) {c…...

238. 银河英雄传说

Powered by:NEFU AB-IN Link 文章目录238. 银河英雄传说题意思路代码238. 银河英雄传说 题意 有一个划分为 N列的星际战场&#xff0c;各列依次编号为 1,2,…,N 有 N艘战舰&#xff0c;也依次编号为 1,2,…,N&#xff0c;其中第 i号战舰处于第 i列。 有 T条指令&#xff0c;每…...

centos7 开机自启动自定义脚本

centos7 开机自启动自定义脚本背景配置自启动jar1.首先书写自启动脚本2.在rc.local中加入脚本reboot测试docker版本的自启动背景 项目中有遇到2个问题&#xff0c; 1&#xff1a; 使用java启动jar包 2&#xff1a; docker容器中自启动个服务。 这2个都要使用linux的开机自启动问…...

【Linux】动静态库的制作

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;动静库和静…...

数据备份学习笔记2

Linux实现本地备份的命令&#xff1a; mkdir -p /root/backup/date "%Y-%m-%d" tar -zcvPf /root/backup/date "%Y-%m-%d"/test20230221.tar.gz /root/test20230221/ 我们再看下tar命令选项&#xff1a; tar -czvf txt3.tar.gz txt3 tar -xvf txt4.tar.g…...

webRTC

WebRTC是一种实时通信技术&#xff0c;可以在浏览器中实现音频、视频和数据的实时传输。WebRTC使用标准的API和协议&#xff0c;如RTCPeerConnection和RTCDataChannel等&#xff0c;可以实现点对点通信和多方会议等多种应用场景。WebRTC可以应用于Web前端、移动端和桌面端等多种…...

用Python搓一个黑洞

文章目录简介单位制观测绘图简介 黑洞图像大家都知道&#xff0c;毕竟前几年刚发布的时候曾火遍全网&#xff0c;甚至都做成表情包了。 问题在于&#xff0c;凭什么认为这就是黑洞的照片&#xff0c;而不是一个甜甜圈啥的给整模糊了得到的呢&#xff1f;有什么理论依据吗&…...

Spring MVC常用功能及注解

目录 一、什么是Spring MVC 1.1 Spring MVC定义 1.2 MVC定义 1.3 MVC和Spring MVC的关系 1.4 Spring MVC的作用 二、Spring MVC的使用 2.1 Spring MVC的创建和连接 2.1.1 RequestMapping注解 2.1.2 GetMapping注解 2.1.3 PostMapping注解 2.2 获取参数 2.2.1 获取单…...

shell 编程

文章目录一、shell 编程1.1. 脚本执行1.2. 变量1.3. 特殊变量1.4. 运算符1.5. for 循环1.6. while 循环1.7. case 语句1.8. read 命令1.9. if 判断1.10. 判断语句1.11. 自定义函数1.12. 脚本调试二、sed2.1. sed 选项2.2. sed function2.3. sed 删除&#xff08;d 命令&#xf…...

Leetcode.1401 圆和矩形是否有重叠

题目链接 Leetcode.1401 圆和矩形是否有重叠 Rating &#xff1a; 1709 题目描述 给你一个以 (radius, xCenter, yCenter)表示的圆和一个与坐标轴平行的矩形 (x1, y1, x2, y2)&#xff0c;其中 (x1, y1)是矩形左下角的坐标&#xff0c;而 (x2, y2)是右上角的坐标。 如果圆和矩…...

CHAPTER 3 Web Server - httpd配置(二)

Web Server - httpd配置二3.1 httpd配置3.1.1 基于用户的访问控制3.1.2 basic认证配置示例&#xff1a;1. 添加用户2. 添加网页文件3. 定义安全域4. 修改父目录权限5. 访问效果6. 在配置文件中定义一个".htaccess"隐藏文件7. 添加组3.1.3 虚拟主机1. 构建方案2. 基于…...

VSCode 连接 SSH 服务器

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/129133964 配置VSCode 下载VSCode:https://code.visualstudio.com/ 安装 Remote - SSH: 点击右下角蓝色图标: 连接服务器: 即可。 默认连接:ssh chen…...

如何选择靠谱的插画培训课程

如何选择靠谱的插画培训课程&#xff0c;今天教你3个维度选择一个靠谱的插画培训班&#xff01; 插画培训机构课程&#xff1a; 1.选择插画培训班时&#xff0c;要先考察课程&#xff0c;看看课程内容是否符合自己的需求&#xff0c;是否有助于提高插画技术。课程设置应该灵活…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...