【OpenGL学习】texture

news2024/12/29 13:36:49

纹理

一、什么是纹理?

引用百度百科的定义:

计算机图形学中的纹理既包括通常意义上物体表面的纹理即使物体表面呈现凹凸不平的沟纹,同时也包括在物体的光滑表面上的彩色图案,通常我们更多地称之为花纹。对于花纹而言,就是在物体表面绘出彩色花纹或图案,产生了纹理后的物体表面依然光滑如故。对于沟纹而言,实际上也是要在表面绘出彩色花纹或图案,同时要求视觉上给人以凹凸不平感即可。 凹凸不平的图案一般是不规则的。在计算机图形学中,这两种类型的纹理的生成方法完全一致, 这也是计算机图形学中把他们统称为纹理的原因所在。

在上节中我们给每个顶点都添加了一个颜色值,从而使每个顶点都有对应的颜色,纹理的其实就是指定了每一个顶点对应的颜色,存储在一张2D或者3D的图片当中,然后把某个顶点对应的颜色在图片中的位置定义为纹理坐标,也就是说,知道了一个顶点的纹理坐标,我们就可以在图片中查找得到该顶点对应的颜色。有了纹理,我们就能够给物体添加更多的细节。

二、纹理映射

纹理映射 (Texture Mapping) 是一种将物体空间坐标点转化为纹理坐标,进而从纹理上获取对应点的值,以增强着色细节的方法。

以2D纹理举例说明,2D的纹理一般对应的纹理坐标从左下角到右上角分别为(0,0)和(1,1),例如下面这张图片:

在这里插入图片描述

如果我指定三角形的三个顶点对应的纹理坐标如下:

	float vertices[3 * 8] =
	{
		//pos				//Color				//texcoord
		-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
		 0.5f, -0.5f, 0.0f,	0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
		 0.0f,  0.5f, 0.0f,	0.0f, 0.0f, 1.0f, 0.5f, 1.0f
	};

在这里插入图片描述

正确渲染得到的结果是这样的:

为了便于观察,我把两张图放到一起对比一下:

在这里插入图片描述

左边这张是我们的纹理图片,右边这张是给三角形指定纹理坐标后输出的结果,可以看到,根据我们设定的纹理坐标,三角形左面的顶点对应(0 , 0),所以对应纹理中的左下角,右面的顶点对应(1 ,0),对应纹理中的右下角,上面顶点同理对应纹理中上边缘的中间,因此三角形被绘制成了右图所示的样子。此外,还记得上节中指定了三角形三个顶点的颜色后输出的图形是彩色的吗,是因为顶点属性被进行了插值处理,这节中纹理坐标也是相同的原理,虽然我们仅指定了三个顶点的纹理坐标,但是经过插值后可以得到三角形区域对应的所有纹理坐标,从而在纹理中找到对应的颜色值输出,我们看到的输出结果就正好是纹理本身的样子了,输出图像也是纹理映射的结果。

三、创建纹理

想要使用纹理,首先需要创建并绑定纹理对象,和之前的顶点数组以及缓冲区一样:

unsigned int texture;
glGenTexture(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glBindTexture函数的第一个参数指定了要绑定的纹理格式,是2D还是3D纹理,第二个参数是我们的纹理对象。

四、纹理的加载

使用纹理之前,首先要做的事情就是将准备好的图片文件加载到我们的应用中,这里使用图像加载库stb_image来实现,有关stb_image 的配置这里不多做介绍,可以参阅: stb_image库及使用

将图片加载到应用中:

	//load image
	int width, height, channels;
	stbi_set_flip_vertically_on_load(1);
	unsigned char* data = stbi_load("Asset/texture/leidian.jpg", &width, &height, &channels, 0);

这里创建了三个变量,其中width用于保存图像的宽度,也就是在水平方向的像素数量,height用于保存高度,channels保存图像拥有的通道数,一般为 rgb 三个或是 rgba 四个,然后我们需要使用函数stbi_set_flip_vertically_on_load来实现翻转图像的 y 坐标,如果不这么做,你会得到下面的结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M6thHPWN181407b444868684eb741ea97c54.png#pic_center)null#pic_center)]

具体原因和图像存储和读取的方式有关,OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部,这里不再做展开。

然后创建了一个无符号字符数组用于保存读取到的图像数据,使用函数stbi_load 传入的参数分别为:图像文件所在位置,用于存放宽度高度和通道数变量的地址,最后一个是期望通道数对应的变量地址,这里我们直接填0。

如果加载成功,那么就创建纹理,否则输出读取失败:

if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
		std::cout << "Failed to load image!" << std::endl;
	stbi_image_free(data);

glTexImage2D函数的参数:

  • 第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
  • 第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
  • 第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。
  • 第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
  • 下个参数应该总是被设为0(历史遗留的问题)。
  • 第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
  • 最后一个参数是真正的图像数据。

使用glTexImage2D后,当前绑定的纹理对象会被附加上我们添加的纹理图像,如果没有指定多级渐远纹理(mipmap),会默认只有基本级别的纹理图像,如果要添加 mipmap,可以通过指定上述第二个参数,也可以通过调用glGenerateMipmap,会为当前绑定的纹理自动生成所有需要的多级渐远纹理。

注意生成纹理之后一定要释放图像内存:stbi_image_free(data)

五、纹理的应用

这次我们使用之前使用过的矩形来应用纹理,首先需要指定矩形对应的顶点数组:

//rectangle vertices
	float rectangle_vertices[] =
	{
		//pos				//Color				//texcoord
		-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
		 0.5f, -0.5f, 0.0f,	0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
		-0.5f,  0.5f, 0.0f,	0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
		 0.5f,  0.5f, 0.0f,	0.0f, 0.0f, 1.0f, 1.0f, 1.0f
	};

添加了顶点坐标之后,顶点数据对应的布局如下:

img

所以添加新的顶点布局:

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);

注意此时的步长由于添加了2个float数据变为了8,对应的偏移量也变成了6个浮点数的长度,因为前面有3个顶点坐标和三个颜色对应的float值。

接着在vertex shader中指定输入顶点属性对应的布局:

#version 330 core		
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 a_Color;
layout(location = 2) in vec2 a_texCoord;

out vec3 v_Position;
out vec3 v_Color;
out vec2 v_texCoord;

//uniform float u_Offset;

void main()
{
	v_Position = a_Position;
	v_Color = a_Color;
	v_texCoord = a_texCoord;
	//v_Position.x += u_Offset;
	gl_Position = vec4(v_Position, 1.0);
}

fragment shader:

#version 330 core
layout(location = 0) out vec4 FragColor;
			
in vec3 v_Position;
in vec3 v_Color;
in vec2 v_texCoord;
uniform vec4 u_Color;
uniform float u_time_factor;

uniform sampler2D Texture;

void main()
{
	//FragColor = u_Color;
	//FragColor = vec4(v_Color * u_time_factor, 1);
	FragColor = texture(Texture, v_texCoord);
}

在片元着色器中,我们声明了纹理采样器:sampler2D,用于访问创建的纹理,使用GLSL内建的texture函数来采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。

在调用绘制函数之前首先先对纹理进行绑定,绑定之后就会把纹理赋值给片元着色器中的纹理采样器。

		glBindTexture(GL_TEXTURE_2D, texture);
		glBindVertexArray(rectangle_vertex_array);
		//glDrawArrays(GL_TRIANGLES, 0, 3);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

运行结果:

在这里插入图片描述

可以通过混合输出颜色得到下面的结果:

	FragColor = texture(Texture, v_texCoord) * vec4(v_Color, 1);

在这里插入图片描述

六、 OpenGL 中的纹理单元

上面的 采样器 sampler2D 定义为了 uniform 变量,我们可以使用 glUniform1i 来指定纹理采样器对应的纹理单元,从而在一个片元着色器中设置多个纹理。

纹理单元,又称之为纹理映射单元Texture mapping unit,TMU)是现代图形处理器(GPU)的部件。从历史上看,它是一个独立的物理处理器 TMU能够旋转,调整大小和扭曲位图图像(执行纹理采样),以作为纹理放置在给定3D模型的任意平面上。此过程称为纹理制图映射。

如果想要使用不同的纹理,就要激活不同的纹理单元,在进行纹理绑定之前,首先先激活想要使用的纹理单元:

glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);

这样,我们的texture对象就绑定到了 0 号纹理单元,纹理单元GL_TEXTURE0是默认激活的,因此之前只有一张纹理使用glBindTexture的时候,无需激活任何纹理单元。

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。

下面我们添加另外一个采样器:

uniform sampler2D texture1;
uniform sampler2D texture2;

然后我们再加载一张箱子的纹理:

unsigned int texture2;
	glGenTextures(1, &texture2);
	glBindTexture(GL_TEXTURE_2D, texture2);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	//load image2
	data = stbi_load("Asset/texture/container.jpg", &width, &height, &channels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
		std::cout << "Failed to load image!" << std::endl;
	stbi_image_free(data);

分别给两个采样器指定对应的纹理单元,这里我们使用 shader 类中的 set_int()来设置:

	triangle_shader.bind();
	triangle_shader.set_int("texture1", 0);
	triangle_shader.set_int("texture2", 1);

指定好纹理单元之后,在Render Loop 中使用两个纹理时分别激活对应的纹理单元:

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texture1);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texture2);

然后在 fragment shader中,我们将两个纹理采样得到的颜色进行混合:

FragColor = mix(texture(texture1, v_texCoord), texture(texture2, v_texCoord), 0.5);

运行查看结果:

在这里插入图片描述

七、纹理环绕和过滤

创建纹理对象好之后,还可以指定纹理环绕和滤波的格式,来指定当纹理坐标的设置超过(0,1)范围之外,纹理以什么样的形式输出以及以什么样的方式进行滤波处理。

7.1 纹理环绕

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

均通过函数glTexParameter来实现,其中前两行用于指定在水平和垂直方向的环绕方式,第二个参数如果是3D纹理还对应一个GL_TEXTURE_WRAP_R,然后第三个参数可以选择的内容有:

环绕方式描述
GL_REPEAT对纹理的默认行为。重复纹理图像。
GL_MIRRORED_REPEAT和GL_REPEAT一样,但每次重复图片是镜像放置的。
GL_CLAMP_TO_EDGE纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
GL_CLAMP_TO_BORDER超出的坐标为用户指定的边缘颜色。

四种效果如下:

在这里插入图片描述

另外,如果选择GL_CLAMP_TO_BORDER模式,你还可以指定边缘颜色:

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

在这里插入图片描述

7.2 纹理过滤

纹理坐标不依赖于分辨率(Resolution),它可以是任意浮点值,所以OpenGL需要知道怎样将纹素(Texel)映射到纹理坐标。当你有一个很大的物体但是纹理的分辨率很低的时候,需要指定一个纹理坐标对应的纹素。通过下面两行来指定 filter 的模式。

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是OpenGL默认的纹理过滤方式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。下图中你可以看到四个像素,加号代表纹理坐标。左上角那个纹理像素的中心距离纹理坐标最近,所以它会被选择为样本颜色:

img

GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。一个纹理像素的中心距离纹理坐标越近,那么这个纹理像素的颜色对最终的样本颜色的贡献越大。下图中你可以看到返回的颜色是邻近像素的混合色:

img

两种纹理过滤方式的视觉效果

img

GL_NEAREST产生了颗粒状的图案,我们能够清晰看到组成纹理的像素,而GL_LINEAR能够产生更平滑的图案,很难看出单个的纹理像素。GL_LINEAR可以产生更真实的输出。

八、OpenGL中的 MipMap

OpenGL使用一种叫做多级渐远纹理(Mipmap)来解决纹理缩小的问题,试想这样一个情况,假设有两个像素,一个像素对应了近处的物体,另外一个对应了远处的物体,那么这两个像素映射到纹理中之后,远处物体对应的像素会投射出一块更大的面积,(近大远小的原理,远处往往多个物体对应一个像素),可以看下面这幅图:
在这里插入图片描述

因此这个像素的纹理坐标对应到纹理图片中的一大块区域,因此其颜色很难对应准确,会产生类似条纹状的 artifact。
在这里插入图片描述

mipmap简单来说就是一系列的纹理图像,后一个纹理图像是前一个的四分之一。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。

img

OpenGL通过glGenerateMipmaps函数来创建对应纹理的 mipmap,那么如何使用?

同样使用函数glTexParameteri

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

过滤方式可以选择:

过滤方式描述
GL_NEAREST_MIPMAP_NEAREST使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
GL_LINEAR_MIPMAP_NEAREST使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
GL_NEAREST_MIPMAP_LINEAR在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
GL_LINEAR_MIPMAP_LINEAR在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样

Reference:
LearnOpenGL-纹理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/170711.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ES6 课程概述⑦

文章目录Vuex_State安装使用State在 Vue 组件中获得 Vuex 状态mapState 辅助函数Vuex_Getter通过属性访问通过方法访问mapGetters 辅助函数Vuex_Mutation在组件中提交 Mutation提交载荷&#xff08;Payload&#xff09;对象风格的提交方式使用常量替代 Mutation 事件类型Mutati…

Spring Boot(五十六):基于Redis的搜索栏热搜功能

1 功能要求 使用SpringBoot和redis实现一个简单的热搜功能&#xff0c;具备以下功能&#xff1a; 搜索栏展示当前登陆的个人用户的搜索历史记录&#xff0c;删除个人历史记录用户在搜索栏输入某字符&#xff0c;则将该字符记录下来 以zset格式存储的redis中&#xff0c;记录该…

Flink DataSet API和DataStream API 对于WordCount的演示

文章目录准备工作Flink DataSet APIFlink DataStream API结论准备工作 pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-insta…

如何多人配音一个作品?这3招帮你快速实现

大家平时喜欢听书吗&#xff1f;听书是一种既能释放双眼&#xff0c;又能降低压力的放松方式。那么大家平时在听书的时候&#xff0c;有没有碰到过一些多人配音的小说&#xff1f;大家有好奇过这样的小说是怎么来的吗&#xff1f;今天&#xff0c;教大家多人配音怎么制作的&…

请问想考软考,零基础的话,哪个证书最好考呢

可以直接考中级&#xff0c;软考中级中也有适合零基础报考的&#xff0c;中级的含金量也比初级的高&#xff0c;初级的用途不太大&#xff0c;建议直接中级。 系统集成项目管理工程师&#xff0c;软考中级比较热门的一个科目&#xff0c;零基础的也适合相比较容易通过。 软考…

Fisher确切概率基本原理详解

Fisher确切概率 基本原理 比较两组有效率是否有差异。 在周边合计不变的情况下&#xff0c;计算实际频率变动时的Pi&#xff08;概率&#xff09;。然后计算累积概率&#xff0c;依据检验水平做推断。 累积概率的计算 以a从小到大的概率排序 左侧概率&#xff1a;现有样本…

【SpringCloud17】SpringCloud Alibaba入门简介

1.为什么会出现SpringCloud Alibaba Spring Cloud Netflix项目进入维护模式官网 1.1 什么是维护模式 将模块置于维护模式&#xff0c;意味着 Spring Cloud 团队将不会再向模块添加新功能。我们将修复 block 级别的 bug 以及安全问题&#xff0c;我们也会考虑并审查社区的小型 …

shell处理多盘跑fio(minimal)的结果脚本编写

作为一个专业测试storage的测试人员&#xff0c;除了对服务器&#xff0c;硬盘熟悉之外&#xff0c;还要对测试工具fio特别熟悉才行。如果在OEM或者专门的HDD&SSD厂家测试&#xff0c;会经常看到测试脚本里边&#xff0c;开发喜欢用fio minimal 模式&#xff0c;这样解析lo…

【GD32F427开发板试用】利用SPI驱动ADS8354

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;STY 前言 本文期望通过板载硬件SPI外加DMA传输的方式来实现对全差分同步采样模数转换器ADS8354的控制&#xff0c;并且将采集数据进行初步脉冲…

2.1、进程的定义、组成、组织方式、特征

整体框架 1、进程的定义 程序\color{red}程序程序&#xff1a;就是一个指令序列 早期的计算机&#xff08;只支持单道\color{red}单道单道程序&#xff09; 引入多道\color{red}多道多道程序之后&#xff1a; 为了方便操作操作系统管理&#xff0c;完成各个程序并发执行、 引…

Go map 实现原理

Go map 实现原理 go map 源码路径在&#xff1a; src/runtime/map.gogo 源码结构 |– AUTHORS — 文件&#xff0c;官方 Go语言作者列表 |– CONTRIBUTORS — 文件&#xff0c;第三方贡献者列表 |– LICENSE — 文件&#xff0c;Go语言发布授权协议 |– PATENTS — 文件&…

如何解决Prometheus的数据回填问题

去年10月底的时候&#xff0c;我们的监控系统因为一个偶然的问题&#xff0c;出乎意料地发生了重大的故障&#xff0c;这次故障暴露了当前监控系统存在的一下重大隐患。故障背景及现象我们的监控系统基于Thanos构建&#xff0c;基本架构如下&#xff08;箭头表示数据流向&#…

Docker安装Mysql8.0主从复制

1使用portainer快速创建mysql 2.mysql-master version: 3.3 services:mysql-app:image: mysql:8.0container_name: mysqlrestart: alwaysports:- 3307:3306environment:MYSQL_ROOT_PASSWORD: 123456 # root用户的密码MYSQL_ROOT_HOST: % # 访问权限# MYSQL_USER: test …

[leetcode]刷题--关于位运算的几道题

&#xff08;1&#xff09;位运算的本质&#xff0c;其实是对二进制补码储存形式的修改。 位运算常见的运算符为 <<左移n个位置&#xff08;算数移位&#xff0c;符号位不变&#xff09; >>右移动n个位置&#xff08;采用直接丢弃末尾数字的方法&#xff0c;符号…

Android Raphael使用(专治native 内存泄漏)

1.前期准备 在项目根目录build.gradle中,添加仓库地址&#xff1a; allprojects {repositories {maven { url https://jitpack.io }} }2.案例实践 构建一个新的Library Module&#xff0c;其中build.gradle中添加依赖&#xff1a; dependencies {implementation com.github…

gitlab-runner搭建CI/CD

1. 背景 每次发布代码&#xff0c;需要连接服务器更新代码&#xff0c;进行部署&#xff0c;比较繁琐&#xff0c;浪费时间。方案有jenkins或gitlab-runner。由于代码仓库是gitlab并且只需要自动部署&#xff0c;不需要其他额外功能&#xff0c;这里选择使用gitlab-runner。 …

【React】三.React组件基础学习

目录 React组件介绍 React组件的两种创建方式 使用函数创建组件 函数组件 渲染函数组件 示例 使用类创建组件 抽离为独立的JS文件 步骤 问题记录 React事件处理 事件绑定 记录问题 事件对象 有状态组件和无状态组件 无状态组件&#xff08;木偶组件&#xff09;…

XSS(Cross Site Scripting)攻击简介

环境 Ubuntu 22.04IntelliJ IDEA 2022.1.3JDK 17.0.3.1Spring Boot 3.0.1Firefox 108.0.2 问题和分析 在IntelliJ IDEA中创建Spring Boot项目 test0116 &#xff0c;并选中 Spring Web 依赖。 在 src/main/java 下创建 MyController.java 如下&#xff1a; package com.ex…

Redis缓冲区不会还有人不知道吧?

1 简介 缓冲区&#xff0c;用一块内存空间暂时存放命令数据&#xff0c;以免因 数据和命令的处理速度&#xff1c;发送速度而导致数据丢失和性能问题。但缓冲区的内存空间有限&#xff0c;若持续&#xff1a; 往里写数据速度&#xff1e;从里读数据速度会导致缓冲区需越来越…

ATGM332D-5N卫星导航模块介绍

ATGM332D-5N卫星导航模块简介ATGM332D-5N系列模块是12X16 尺寸的高性能BDS/GNSS 全星座定位导航模块系列的总称。该系列模块产品都是基于中科微第四代低功耗GNSS SOC单芯片—AT6558&#xff0c;支持多种卫星导航系统&#xff0c;包括中国的BDS&#xff08;北斗卫星导航系统&…