C++和OpenGL实现3D游戏编程【连载14】——VBO、VAO和EBO应用
🔥C++和OpenGL实现3D游戏编程【目录】
1、本节实现的内容
我们从一开始学OpenGL到现在,OpenGL的图形绘图必须在glBegin()和glEnd()函数之间完成,在此基础之上,才能进行后续操作功能。但是我们今天要讨论一下OpenGL图形绘制的模式,从之前的立即渲染模式,开始转向核心模式,从而大幅提升显示性能。下图就是通过VBO、VAO和EBO应用画出的一个矩形,矩形虽然简单,但它的意义却不凡。
2、立即渲染模式
OpenGL的绘图的函数模式很多,比如绘制点的模式GL_POINTS、直线GL_LINES 、三角形GL_TRAINGLES、四边形GL_QUADS等等。OpenGL的绘图必须在glBegin()和glEnd()函数之间完成,这种方式使我们简单、直观的进行了基本几何绘图函数。像下面这个绘制三角形的操作:
//绘画一个三角形glBegin( GL_TRIANGLES );glVertex3f(-1.0f,-0.5f,-2.0f);glVertex3f( 1.0f,-0.5f,-2.0f);glVertex3f( 0.0f,0.5f,-2.0f);glEnd();
客观的说,这种绘图方式最主要的特点就是看起来比较简单,容易理解,就是我们所说的立即显示模式。但是这种方式也有缺点,就是画图形的显示效率较低。原因是在于glVertex函数每次调用只把一个顶点(坐标数据)从客户端(CPU或内存)传输到显示处理设备(GPU),而这个传输的过程相对于GPU处理数据的过程是很慢的。如果显示的图形数量不多的情况下,不会出现问题,但后期随着绘制图形的越来越复杂,位置的图形数量越来越多,需要使用的glVertex函数的调用次数就会越来越多,这将直接导致传统的立即显示模式执行效率有所下降。
3、什么是GPU和显存
首先,我们需要了解一下GPU和显存的基本概念。GPU全称图形处理器(Graphics Processing Unit),是一种专门用于处理图像和视频信息的微处理器。它拥有强大的并行处理能力,可以高效地执行复杂的图形渲染任务,GPU是显卡的核心部件。显存,也就是显卡里的内存。显卡的GPU访问显存,比访问内存(CPU里的内存区域)要快很多。而且显卡做渲染运算,一般都是访问显存的数据,再对数据进行运算,并把运算结果保存在显存中。所以一般,我们为了提示游戏性能,需要先把数据先从内存一次性传输到显存中去,后期由GPU对显存中数据进行显示和渲染。
4、核心模式下图形渲染方式
了解以上铺垫知识了以后,我们就明白此后我们将尽可能少在使用立即显示模式去显示图形。当我们使用OpenGL绘制图形时,需要将顶点的位置、颜色等信息传递给显卡,让显卡进行绘制,为了提高效率,我们需要把数据先从内存一次性传输到显存中去,后期由GPU对显存中数据进行显示和渲染。该操作主要包括以下内容:
-
VBO(Vertex Buffer Object)是用来存储这些顶点数据的特殊缓冲区,可以将数据保存在显卡中,避免频繁地从CPU传输数 据到显卡,提高绘制效率。
-
VAO(Vertex Array Object)则是用来管理这些顶点数据的设置的。绘制一个图形通常需要多个顶点属性(比如位置、颜色等),VAO帮助我们预先指定这些属性在VBO中的排列方式,避免在每次绘制时重新设置属性,简化绘制代码。
-
EBO(Element Buffer Object)是用来存储图元的索引数据的缓冲区,比如三角形的三个顶点索引。它可以帮助我们避免重复存储顶点数据,只需存储少量的索引数据就可以描述复杂的图形,提高绘制效率。
综合来说,VBO、VAO和EBO是帮助我们优化OpenGL绘制图形的工具。它们的组合可以让我们更高效地绘制图形,让图形的渲染速度更快,性能更好。也就是说从现在开始,我们开始去了解GPU的操作相关知识,这才是游戏设计的灵魂所在。
5、VBO(Vertex Buffer Object)顶点缓冲对象
VBO(Vertex Buffer Object)顶点缓冲对象,是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法向量,顶点颜色数据等。在渲染时,可以直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高。所以可以理解为VBO就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个VBO,每个VBO在OpenGL中有它的唯一标识ID,这个ID对应着具体的VBO的显存地址,通过这个ID可以对特定的VBO内的数据进行存取操作。比如我们有以下三个顶点数据信息:
//自定义顶点float vertices[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f};
以上三个顶点我们是通过数组保持的,后期发送到顶点缓冲区,也会是连续顶点的存储模式,每一个点(VERTEX)的位置坐标数据包含x,y,z三个分量,每个点位置信息存储占用为12个字节。这些点位置信息之间没有空隙(或其他值),在数组中紧密排列。如下图:
当然,数据信息也可能包含了颜色和纹理坐标信息:
顶点缓冲对象(VBO)就像OpenGL中的其它对象一样,这个缓冲有一个独一无二的ID,所以我们可以使用glGenBuffers函数和一个缓冲ID生成一个VBO对象。随后glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数。它的第一个参数是目标缓冲的类型:当前绑定到GL_ARRAY_BUFFER目标上的顶点缓冲对象。第二个参数指定传输数据的大小(以字节为单位),用一个简单的sizeof计算出顶点数据大小就行。第三个参数是我们希望发送的实际数据。
//生成VBO对象,缓冲ID为VBOunsigned int VBO;//使用元素缓冲对象EBOglGenBuffers(1, &EBO); //第一个就是缓冲对象的类型,第二个参数就是要绑定的缓冲对象的名称glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//数据传入缓冲内存中,GL_STATIC_DRAW:数据不会或几乎不会改变; GL_DYNAMIC_DRAW:数据会被改变很多glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//设置完属性,解绑VBO glBindBuffer(GL_ARRAY_BUFFER, 0);
VBO是CPU和GPU传递信息的桥梁,我们把数据存入VBO是在CPU上操作,VBO会自动将数据送至GPU。送至GPU不需要任何人为操作。关于VBO另外补充解释:使用VBO管理内存(GPU上储存顶点数据),使用VBO好处就是将内存一次性发送大批数据到显卡,而不是每次发送一个顶点数据。从CPU把这些数据发送到显卡相对较慢,所以只要可能就尝试尽量一次性尽可能多的数据。发送到显卡的内存后,顶点着色器几乎能立即访问顶点,过程比较快。
Buffer Object,就是由OpenGL维护的一块显存区域。比如说在一块显存为2G的显卡里,分配了128K大小的内存区域给OpenGL使用,这个128K大小的内存区域,就叫一个Buffer Object。
我们已经把顶点数据发送给了GPU,但OpenGL还不知道它该如何解释内存中的顶点数据。也就是说,GPU内的这块显存区域里是紧密连续的一个个数据,OpenGL并不知道从哪里到哪里是一个顶点的数据,从哪里到哪里是这个顶点的RGB值(如果有的话),因此要显示这些顶点,我们还需要顶点数组对象(Vertex Array Object, VAO)配合使用。同时,我们也该告诉OpenGL,该如何将顶点数据链接到顶点着色器的属性上,着色器部分我们下节课在详细描述。
VBO是顶点缓冲对象,是CPU和GPU中间的桥梁,开发者只需要将数据存入VBO中,VBO自动将数据送入GPU中。而VBO传输的只是一堆数字,GPU怎么解释这些顶点数据,就需要VAO来解释。
6、VAO(Vertex Array Object)顶点数组对象
VAO(Vertex Array Object)顶点数组对象,可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,刚刚设置的所有状态(顶点坐标属性、颜色属性、纹理坐标属性等)都将同时绑定并存储在一个VAO中,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。
//生成VAOglGenVertexArrays(1, &VAO);//绑定VAO,从绑定之后起,我们应该绑定和配置对应的VBO和属性指针,之后解绑VAO,供之后使用glBindVertexArray(VAO);//第一个参数GLsizei是要生成的缓冲对象的数量,第二个GLuint是要输入用来存储缓冲对象名称的数组glGenBuffers(1, &VBO);//绑定到目标对象,VBO变成了一个顶点缓冲类型,第一个就是缓冲对象的类型,第二个参数就是要绑定的缓冲对象的名称glBindBuffer(GL_ARRAY_BUFFER, VBO);//数据传入缓冲内存中,GL_STATIC_DRAW:数据不会或几乎不会改变; GL_DYNAMIC_DRAW:数据会被改变很多glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//启用顶点属性layout(location = 0),顶点属性默认是禁用的。供着色器使用,着色器操作下节课详细介绍glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//设置完属性,解绑VBO glBindBuffer(GL_ARRAY_BUFFER, 0);//配置完VBO及其属性,解绑VAO,后期显示图元的时候再次绑定VAO glBindVertexArray(0);
在 OpenGL 中,使用顶点数组对象(VAO)可以方便地管理顶点属性的状态。绑定 VAO、设置顶点属性、然后解绑 VAO 的顺序是为了确保顶点属性的设置被正确地记录在 VAO 中,从而简化渲染代码并提高效率。OpenGL的核心模式要求我们使用VAO,所以它知道该如何处理我们的顶点输入。以下就是通过VAO显示对应VBO中三角形的方法:
//绑定VAOglBindVertexArray(VAO);//从VAO中读取数据画出三角形glDrawArrays(GL_TRIANGLES, 0, 3);//使用完毕后解绑VAOglBindVertexArray(0);
如果你像我这样成功绘制出了这个三角形或矩形,那么恭喜你,你成功地通过了现代OpenGL入门的拦路虎之一,绘制你自己的第一个三角形或矩形,因为这些操作确实很抽象。当然我们这里暂时没有使用着色器,下节课我们会详细介绍着色器内容。如果我们绑定VAO失败,OpenGL会拒绝绘制任何东西。
7、EBO(Element Buffer Object)元素缓冲对象
EBO(Element Buffer Object)元素缓冲对象,也叫索引缓冲对象(Index Buffer Object,IBO)。要解释元素缓冲对象的工作方式最好还是举个例子:假设我们不再绘制一个三角形而是绘制一个矩形。我们可以绘制两个三角形来组成一个矩形(OpenGL主要处理三角形)。这会生成下面的顶点的集合:
GLfloat vertices[] = {// 第一个三角形0.5f, 0.5f, 0.0f, // 右上角0.5f, -0.5f, 0.0f, // 右下角-0.5f, 0.5f, 0.0f, // 左上角// 第二个三角形0.5f, -0.5f, 0.0f, // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f // 左上角};
可以看到,有几个顶点叠加了。我们指定了右下角和左上角两次!一个矩形只有4个点而不是6个顶点,这样就产生50%的额外开销。当我们有包括上千个三角形的模型之后这个问题会更糟糕,这会产生一大堆浪费。更好的解决方案是只储存不同的顶点,并设定绘制这些顶点的顺序。这样子我们只要储存4个顶点就能绘制矩形了,之后只要指定绘制的顺序就行了。首先,我们先要定义(不重复的)顶点,和绘制出矩形所需的索引:
GLfloat vertices[] = {0.5f, 0.5f, 0.0f, // 右上角0.5f, -0.5f, 0.0f, // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f // 左上角};GLuint indices[] = { 0, 1, 3, // 第一个三角形,注意索引从0开始1, 2, 3 // 第二个三角形,注意索引从0开始};
创建元素缓冲对象与VBO类似,我们先绑定EBO然后用glBufferData把索引复制到缓冲里。同样,EBO的使用和VBO类似,我们会把这些EBO的相关操作放在VAO绑定和VAO解绑操作调用之间,只不过这次我们把缓冲的类型由VBO的GL_ARRAY_BUFFER改变成为GL_ELEMENT_ARRAY_BUFFER。
//使用元素缓冲对象EBOglGenBuffers(1, &EBO); //第一个就是缓冲对象的类型,第二个参数就是要绑定的缓冲对象的名称glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//数据传入缓冲内存中,GL_STATIC_DRAW:数据不会或几乎不会改变; GL_DYNAMIC_DRAW:数据会被改变很多glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
最后一件要做的事是用glDrawElements来替换glDrawArrays函数,表示我们要从索引缓冲区渲染三角形。使用glDrawElements时,我们会使用当前绑定的索引缓冲对象中的索引进行绘制:
//绑定VAOglBindVertexArray(VAO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glDrawElements(GL_TRIANGLES,sizeof(indices),GL_UNSIGNED_INT,0);//使用完毕后解绑VAOglBindVertexArray(0);
这部分操作很难,因为在绘制第一个三角形之前你需要了解很多知识。幸运的是我们现在已经越过了这个障碍,接下来的教程会比较容易理解一些。
8、绑定和解绑顺序
我们通常绑定和解绑有一定的顺序,创建顺序:VAO->VBO->EBO, 解绑顺序:VAO->VBO->EBO。需要注意的是,当VAO处于活动状态时,不能解绑EBO, 因为绑定的元素缓冲区对象存储在VAO中。其实VAO每次使用都需要调用glBindVertexArray,它会直接解绑旧的对象并且绑定一个新的,因此通常情况下,我们不需要通过调用 glBindVertexArray(0) 来解绑VAO,当然VBO也是这样。所以在使用中,无需过度在意是否解绑了VAO和VBO,通常是不需要解绑的。
9、核心模式渲染实例
我们以上讲解了和核心模式下VBO、VAO和EBO的相关知识,所举得实例相对较为分散,那么我们现在就接合以上三部分知识,完成个简单的综合实例,生成一个矩形,直观的感受一下核心模式渲染实例。
//自定义VAO对象,缓冲ID为VAO,当然这里VAO就是一个变量名,名字可以自己随便起,比如VaoExample等等unsigned int VAO;//自定义VBO对象,缓冲ID为VBOunsigned int VBO;//自定义VBO对象,缓冲ID为VBOunsigned int EBO;
自定义顶点数据
//定义顶点对象float vertices[] = {//位置信息 //颜色信息 -5.0f, 0.0f, 6.0f, 1.0f,0.0f,0.0f, 5.0f, 0.0f, 6.0f, 0.0f,1.0f,0.0f, 5.0f, 10.0f, 6.0f, 0.0f,0.0f,1.0f, -5.0f, 10.0f, 6.0f, 1.0f,1.0f,1.0f };unsigned int indices[]={0, 1, 3, // 第一个三角形,注意索引从0开始1, 2, 3 // 第二个三角形,注意索引从0开始};
生成并绑定VAO
//生成VAOglGenVertexArrays(1, &VAO);//绑定VAO,从绑定之后起,我们应该绑定和配置对应的VBO和属性指针,之后解绑VAO,供之后使用glBindVertexArray(VAO);
生成并绑定VBO
//第一个参数是要生成的缓冲对象的数量,第二个GLuint是要输入用来存储缓冲对象名称的数组glGenBuffers(1, &VBO);//绑定到目标对象,第一个就是缓冲对象的类型,第二个参数就是要绑定的缓冲对象的名称glBindBuffer(GL_ARRAY_BUFFER, VBO);//数据传入缓冲内存中,GL_STATIC_DRAW:数据不会或几乎不会改变; GL_DYNAMIC_DRAW:数据会被改变很多glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
生成并绑定EBO
//使用元素缓冲对象EBOglGenBuffers(1, &EBO); //第一个就是缓冲对象的类型,第二个参数就是要绑定的缓冲对象的名称glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//数据传入缓冲内存中,GL_STATIC_DRAW:数据不会或几乎不会改变; GL_DYNAMIC_DRAW:数据会被改变很多glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
这里是着色器相关设置,下节课详细讲解着色器
//启用顶点属性layout(location = 0),顶点属性默认是禁用的。供着色器使用,下节课详细讲解着色器glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);//启用顶点属性layout(location = 1),顶点属性默认是禁用的。供着色器使用,下节课详细讲解着色器glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
使用完毕后解绑相关对象
//配置完VBO及其属性后,解绑VAO,后期显示图元的时候再次绑定VAO glBindVertexArray(0);//设置完属性,解绑VBO glBindBuffer(GL_ARRAY_BUFFER, 0);
显示图像,需要先绑定VAO
//使用着色器,下节课详细介绍着色器//glUseProgram(shaderProgram);//绑定VAOglBindVertexArray(VAO); //不使用EBO方式绘制三角形,起始索引0,绘制顶点数量3//glDrawArrays(GL_TRIANGLES,0,3);//使用EBO方式绘制三角形,共绘制6个顶点glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);//解绑VAOglBindVertexArray(0); //不在使用着色器,下节课详细介绍着色器//glUseProgram(0);
通过以上过程,我们应该能够在三维空间中显示一个四边形,但是你会发现它并没有颜色,主要时由于本节课为了简化,我们暂未使用着色器去处理颜色信息,待下节课结合着色器进行学习后,就能显示四边形正常的颜色了。
10、总结
在介绍VBO、VAO和EBO三个要素时,大家有可能觉得很迷茫,很多教程中并没有说明为什么要由立即渲染模式转变为核心模式,搞不明白我们为什么原来用的好好的显示函数,我们为什么突然又要讲这VBO、VAO和EBO这三个全新的东西?本节课我就接合学习过程中的一些感触,简单解释了一下这个问题,希望能对您有所帮助。但也值得庆幸的是,特别是我们在随后的游戏设计时,已经开始慢慢从CPU接触到了GPU,游戏的意义从这里才开了一个新的纪元。而且在我们下节课讲到着色器时,会更加体会到OpenGL3.2之后3D世界重大变革的方向,开发者在VBO、VAO和EBO结合着色器的核心模式下进行开发,程序设计自由度更高,游戏设计的魅力又增加了许多。
【上一节】:🔥C++和OpenGL实现3D游戏编程【连载13】——多重纹理混合详解
【下一节】:🔥C++和OpenGL实现3D游戏编程【连载14】——着色器初步
相关文章:

C++和OpenGL实现3D游戏编程【连载14】——VBO、VAO和EBO应用
🔥C和OpenGL实现3D游戏编程【目录】 1、本节实现的内容 我们从一开始学OpenGL到现在,OpenGL的图形绘图必须在glBegin()和glEnd()函数之间完成,在此基础之上,才能进行后续操作功能。但是我们今天要讨论一下OpenGL图形绘制的模式&a…...

AI + 智能互助平台(一点杂想)
随着人工智能的火爆,各种AI产品的兴起,我发现在解决人们日常需求的AI工具还是比较少的,还是比较依赖上一代的搜索功能。 是不是可以有这样的一款产品,需求方在平台上发布需求,提供方在平台上发布能力,AI自动…...

其他浏览器可以联网,但edge不能联网
问题描述: 今早edge无法上网,检测网络连接正常,而且其他chrome,Firefox和360浏览器都可以上网。 解决方案: 注意:为防止是代理问题,可以在扩展中禁用后再试试 如果没有代理或者禁用代理也不…...

Redis 缓存淘汰策略:LRU 和 LFU 的缺点及解决方案详解
引言 Redis 是一款高性能的内存数据库,它的缓存淘汰机制是保障内存使用效率和应用性能的关键。为了在内存有限的情况下保证缓存数据的有效性,Redis 提供了多种缓存淘汰策略,其中 LRU(Least Recently Used,最近最少使用…...

软件工程pipeline梳理
文章目录 软件工程pipeline梳理为什么需要梳理软件工程的pipeline软件工程pipeline的概念与注意点软件工程pipeline中的最大挑战rethink相关资料 软件工程pipeline梳理 为什么需要梳理软件工程的pipeline 反思自己日常工作中的认知和行为。以算法/软件工程师为代表的技术工种往…...

npm运行时出现npm ERR! builtins is not a function报错!
项目场景: 项目运行时什么都没动都没改突然运行不起来了,报错 TypeError: builtins is not a function 代码什么都没动,不是代码问题,排查后只有可能是node和npm的问题,所以卸载掉node重装重启 解决方案: …...

2024年软件设计师中级(软考中级)详细笔记【5】软件工程基础知识上(分值10+)
第5章软件工程 目录 前言第5章 软件工程基础知识(上)(分值10)5.1 软件工程概述5.1.4 软件过程 5.2 软件过程模型5.2.1 瀑布模型 (Waterfall Model)5.2.2 增量模型5.2.3 演化模型5.2.4 喷泉模型(Water Fountain Model&a…...

C++:vector(题目篇)
文章目录 前言一、只出现一次的数字二、只出现一次的数字 II三、只出现一次的数字 III四、杨辉三角五、删除有序数组中的重复项六、数组中出现次数超过一半的数字七、电话号码的字母组合总结 前言 今天我们一起来看vector相关的题目~ 一、只出现一次的数字 只出现一次的数字…...

JS 怎么监听复制事件 并获取复制内容 并修改复制文本内容
需求背景: 需要禁用部分文本内容的复制事件,并且在复制事件发生时,将复制的文本内容通过接口传给后端。 上代码: // 使用Dom获取需要操作禁用时间的元素let element: any document.getElementById(test1);// 为该元素添加 copy 事…...

安卓使用.9图实现阴影效果box-shadow: 0 2px 6px 1px rgba(0,0,0,0.08);
1.安卓实现阴影效果有很多种,一般UX设计会给以H5参数box-shadow: 0 2px 6px 1px rgba(0,0,0,0.08);这种方式提供背景阴影效果,这里记录一下实现过程 2.界面xml源码 <?xml version"1.0" encoding"utf-8"?> <layout xmlns…...

CSS3-Day1
CSS3圆角 border-radius CSS3盒阴影 box-shadow CSS3边界图片 border-image CSS3 background-clip属性 padding-box 沿着边框填充 content-box 在边框外面 CSS3 线性渐变 线性渐变 - 从上到下(默认情况下)#grad { background-image: linear…...

网站集群批量管理-Ansible(ad-hoc)
1. 概述 1. 自动化运维: 批量管理,批量分发,批量执行,维护 2. 无客户端,基于ssh进行管理与维护 2. 环境准备 环境主机ansible10.0.0.7(管理节点)nfs01 10.0.0.31(被管理节点)backup10.0.0.41(被管理节点) 2.1 创建密钥认证 安装sshpass yum install -y sshpass #!/bin/bash ##…...

github学生认证(Github Copilot)
今天想配置一下Github Copilot,认证学生可以免费使用一年,认证过程中因为各种原因折腾了好久,记录一下解决方法供大家参考。 p.s.本文章只针对Github学生认证部分遇到的问题及解决方法,不包括配置copilot的全部流程~ 1、准备工作…...

【SQL调优指南--附带实例】
以下是50个SQL调优的例子,每个例子都附带了可执行的SQL语句: 删除重复记录: DELETE FROM table_name WHERE id NOT IN (SELECT MIN(id) FROM table_name GROUP BY col1, col2);使用索引来加速查询: ALTER TABLE table_name ADD…...

Java基础(下)
泛型 Java 泛型(Generics) 是 JDK 5 中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。 编译器可以对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型 ArrayList<Person> persons new Arra…...

【python】极简教程1-何为程序
程序可以简单地理解为一系列执行运算的指令。这些运算可以是数学计算、符号运算(如检索或替换文档中的内容)或图形运算(如处理图像或播放视频)。 不同编程语言的基础指令大致相同,包括: 输入:从键盘、文件、网络或其他设备获取数据。输出:将数据显示在屏幕上、保存到文…...

【Transformer】Selective Attention Improves Transformer
这篇论文主要介绍了一种新方法——选择性注意力(Selective Attention),用于改善Transformer模型的性能和效率。 🤓 摘要 无关元素在注意力机制中的存在会降低模型性能。论文提出了一种无需额外参数的简单调整方法,即…...

博客项目自动化测试(一)
1. 确认博客系统的环境搭建 http://49.235.129.183:8080/java109_blog_system/blog_list.html,即可访问我的小项目; 2. 确定测试用例 测试用例如下所示: 3. 关于登录的测试用例 3.1 初始化和退出浏览器 代码如下: package Blo…...

电商商品API接口系列(商品详情数据)商品比价、数据分析、自营商城上货
电商商品API接口系列中的商品详情数据接口,在商品比价、数据分析以及自营商城上货等方面发挥着重要作用。以下是对这些应用场景的详细分析: 一、商品详情数据接口概述 商品详情数据接口是电商平台上用于提供商品详细信息的API接口。这些接口允许开发者…...

排序算法总结(一)冒泡排序和选择排序
访问www.tomcoding.com网站,学习Oracle内部数据结构,详细文档说明,下载Oracle的exp/imp,DUL,logminer,ASM工具的源代码,学习高技术含量的内容。 冒泡排序 这个算法可以说是排序算法中最著名的…...

伺服电动缸
美国EXLAR原装K系列伺服缸 高精度运动,运动平稳,低噪音,高速度 向下翻动查看更多 力姆泰克伺服电动缸 k系列电动缸采用Exlar滚柱丝杠技术,提供多种不同性能等级的产品,可外配第三方电机。 通用型设计,无…...

深度学习中的logit到底是什么?
1. 问题 在做深度学习的过程中,经常会碰到logit。这个和在学校学的概率有出入,因而想弄明白这到底是个什么参数。 2. 使用logit的原因 定义几率(odds)和 logit 函数的主要原因在于使用了线性空间转换,使得非线性的概…...

idea使用记录
文章目录 1、idea调出maven窗口2、跳转到指定行 1、idea调出maven窗口 首先尝试菜单栏View→Tool Windows→Maven,如果没有maven那很有可能是idea没有识别到这是一个maven项目,此时可以尝试在项目的pom文件上右击,选择“add as maven projec…...

Python - HTTP servers
python的http.server模块用于HTTP服务器的功能,这个模块是python标准库的一部分,不需要pip install。 使用前需要import: import http.server 然后就可以编辑代码,使用此模块提供的接口,实现http server相关功能。 除…...

内网Debian\Ubuntu服务器安装dep包,基于apt-rdepends下载相关依赖
文章目录 背景一、下载依赖二、拷贝到内网三、 使用dpkg安装可能会遇到的问题 背景 由于生产服务器是Debian\Ubuntu系统且在内网环境(不联网),需要使用拷贝deb格式的包使用dpkg的方式进行安装。所以,需要现在联网的环境中将所需的…...

大模型——如何实现超长多轮对话
在自然语言处理的领域中,多轮对话系统是构建智能化交互应用的关键。无论是聊天机器人、虚拟助手,还是客户服务系统,能够保持连贯的对话并记住上下文信息是用户体验的核心。然而,大规模语言模型(如GPT等)的对…...

大数据面试-笔试SQL
一个表table: c_id u_id score;用SQL计算每个班级top5学生的平均分(腾讯) select class_id,avg(score) as score_avg from (select *,row_number() over(partition by class_id order by score desc) as score_rank from table ) t1 where t…...

希尔排序和直接插入排序
因为排序这些比较复杂点我就分几期给大家来讲~~~ 直接插入排序 直接插入排序是一种简单的排序算法,主要用于对少量数据进行排序。其基本思想是将待排序的元素逐个插入到已经排好序的部分中,从而形成一个有序序列。 具体步骤如下: 初始化&…...

IDEA 配置 Git 详解
本文将介绍在IntelliJ IDEA 中如何配置Git 没有安装配置 Git 的可以参考我的这篇文章:安装配置 Git 一、操作环境及准备 1.win 10 2.已安装且配置了Git 3.有Gitee账户 4.安装了IntelliJ IDEA 2023.2.1 5.全程联网 二、配置步骤 2.1 配置git 1.采用全局设置&…...

Docker 部署 Redis 监控系统实战:Redis Exporter 与 Prometheus 完整配置指南
Docker 部署 Redis 监控系统实战:Redis Exporter 与 Prometheus 完整配置指南 文章目录 Docker 部署 Redis 监控系统实战:Redis Exporter 与 Prometheus 完整配置指南一 缓存简述二 redis exporter 部署三 环境变量配置四 修改文件权限五 验证 exporter …...