前言

本文主要介绍了什么是VBO/VAO,为什么需要使用VBO/VAO以及如何使用VBO和VAO。

VBO

什么是VBO

VBO(vertex Buffer Object):顶点缓冲对象。是在显卡存储空间中开辟的一块区域,在显卡存储空间中开辟一块区域,用于存放顶点的各类属性信息。如顶点坐标、纹理坐标、顶点颜色等数据。

在渲染时直接从显VBO去取数据而不必与CPU进行数据交换。

为什么需要使用VBO

将顶点数据保存在内存中,在调用glDrawArrays或者glDrawElements等绘制方法前需要调用相应的方法将数据送入显存,I/O开销大,性能不够好。

若采用顶点缓冲区对象存放顶点数据,则不需要在每次绘制前都将顶点数据复制进显存,而是在初始化顶点缓冲区对象时一次性将顶点数据送入显存,

每次绘制时直接使用显存中的数据,可以大大提高渲染性能。

如何使用VBO

  • 使用函数glGenBuffers和一个缓冲ID生成一个VBO对象:
unsigned int VBO;
glGenBuffers(1, &VBO);
  • 使用函数glBindBuffer绑定顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER

OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。

glBindBuffer(GL_ARRAY_BUFFER, VBO);
  • 使用函数glBufferData把定义好的顶点数据复制到缓冲的内存中:
// vertices表示顶点数组
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数。它的第一个参数是目标缓冲的类型,其中VBO代表是的GL_ARRAY_BUFFER。第二个参数指定传输数据的大小(以字节为单位),用一个简单的sizeof计算出顶点数据大小就行。

第三个参数是我们希望发送的实际数据。第四个参数指定了我们希望显卡如何管理给定的数据,它有三种形式:

GL_STATIC_DRAW :数据不会或几乎不会改变。

GL_DYNAMIC_DRAW:数据会被改变很多。

GL_STREAM_DRAW :数据每次绘制时都会改变。

一般情况下位置数据不会改变,每次渲染调用时都保持原样,所以它的使用类型一般是GL_STATIC_DRAW。如果一个缓冲中的数据将频繁被改变,那么使用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,这样就能确保显卡把数据放在能够高速写入的内存部分。

  • 用完后使用函数glDeleteBuffers删除缓冲区

今天我们以之前绘制四边形的实践为例子,使用VBO的方式来实现四边形的绘制:Opengl ES之四边形绘制

我们的目标是灵活使用Opengl绘制一个蓝色的四边形...

普通常规的绘制这里就不多说了,后续可以看代码,或者回顾之前的四边形绘制的文章,这里主要介绍搭配VBO的两种绘制方式:

首先它们使用的顶点着色器和片段着色器都是一样的,都是:

// 顶点着色器
static const char *ver = "#version 300 es\n"
"in vec4 aColor;\n"
"in vec4 aPosition;\n"
"out vec4 vColor;\n"
"void main() {\n"
" vColor = aColor;\n"
" gl_Position = aPosition;\n"
"}"; // 片元着色器
static const char *fragment = "#version 300 es\n"
"precision mediump float;\n"
"in vec4 vColor;\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = vColor;\n"
"}";
  1. 顶点坐标与颜色值坐标分离的方式(数组结构)

先看顶点数据与颜色数据:


// 使用绘制两个三角形组成一个矩形的形式(三角形带)
// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形
const static GLfloat VERTICES[] = {
0.5f,-0.5f, // 右下
0.5f,0.5f, // 右上
-0.5f,-0.5f, // 左下
-0.5f,0.5f, // 左上
}; // vbo颜色
const static GLfloat COLOR_ICES[] = {
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
};

VBO数据与绑定:

  // vbo
glGenBuffers(3,vbo);
glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);
glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES),VERTICES,GL_STATIC_DRAW); // 颜色
glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);
glBufferData(GL_ARRAY_BUFFER,sizeof(COLOR_ICES),COLOR_ICES,GL_STATIC_DRAW);

主要绘制代码:

     // 使用VBO的方式绘制,顶点与颜色分开
// glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);
// glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,(void *)0);
// // 启用顶点数据
// glEnableVertexAttribArray(positionHandle);
//
// glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);
// glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,0,(void *)0);
// // 启用颜色顶点数据
// glEnableVertexAttribArray(colorHandle);
// // 解除绑定
// glBindBuffer(GL_ARRAY_BUFFER,0);
  1. 顶点坐标与颜色值坐标结合的方式(结构数组)

顶点数据与颜色数据混合打乱:

const static GLfloat VERTICES_AND_COLOR[] = {
0.5f,-0.5f, // 右下
// 颜色
0.0f,0.0f,1.0f,1.0f,
0.5f,0.5f, // 右上
// 颜色
0.0f,0.0f,1.0f,1.0f,
-0.5f,-0.5f, // 左下
// 颜色
0.0f,0.0f,1.0f,1.0f,
-0.5f,0.5f, // 左上
// 颜色
0.0f,0.0f,1.0f,1.0f,
};

VBO数据绑定:

    // vbo
glGenBuffers(3,vbo);
glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);
glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES),VERTICES,GL_STATIC_DRAW); // 颜色
glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);
glBufferData(GL_ARRAY_BUFFER,sizeof(COLOR_ICES),COLOR_ICES,GL_STATIC_DRAW); // 顶点与颜色混合,先顶点坐标,再颜色坐标
glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);
glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES_AND_COLOR),VERTICES_AND_COLOR
,GL_STATIC_DRAW);

主要绘制代码:

    // VBO绘制 顶点坐标与颜色坐标一起
// glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);
// // stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float
// glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);
// // 启用顶点数据
// glEnableVertexAttribArray(positionHandle);
// // stride 步长 每个颜色坐标之间相隔6个数据点,数据类型是float,颜色坐标索引从2开始
// glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );
// // 启用颜色顶点数据
// glEnableVertexAttribArray(colorHandle);
// // 解除绑定
// glBindBuffer(GL_ARRAY_BUFFER,0);

它们的运行结果都是成功绘制了一个蓝色的四边形:

性能

在上面我们使用了多种不同的组合方式进行了四边形的绘制,其中有将顶点坐标和颜色坐标分离写在两个不同的数组的方式,也有将顶点坐标和颜色坐标组合写在同一个数组,然后使用步长(stride)和偏移量(*pointer)参数控制的方式进行绘制,那么这两种方式那种性能更佳呢?

其中将各种顶点坐标分离在不同数组的写法又成为数组结构,而将各种顶点坐标合并成一个数组的写法又称为结构数组,在《OPENGL ES 3.0编程指南》一书中作者指出,结构数组的写法性能更好。

在大部分情况下,答案是结构数组。

原因是,每个顶点的属性数据可以顺序方式读取,这最有可能造成高效的内存访问模式。

VAO

什么是VAO

VAO(vertex Array Object):顶点数组对象。

注意: VAO是OpenGL ES 3.0之后才推出的新特性, 所以在使用VAO前需要确定OpenGL ES的版本是否是3.0之后的版本。

顶点数组对象可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中。

一个顶点数组对象会储存以下这些内容:

  • glEnableVertexAttribArray和glDisableVertexAttribArray的调用。
  • 通过glVertexAttribPointer设置的顶点属性配置。
  • 通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象。

在上面VBO的介绍中我们知道每次在绘制的时候都需要频繁地绑定与解绑VBO,每次绘制还需要取出VBO中的数据进行赋值之后才能进行绘制渲染。当数据量大的时候,重复这些操作就会变得很繁琐。通过VAO就可以简化这个过程,因此VAO可以简单理解成VBO的管理者,避免在帧绘制时再去手动操纵VBO,VAO不能单独使用,

需要搭配VBO使用。

对于GPU来说VBO就是一堆数据,但是这堆数据怎么解析使用,需要glEnableVertexAttribArray等相关函数在每次绘制的时候告诉GPU,那么VAO的作用就是简化这个过程的,只需要在初始化的时候将这些解析逻辑与VAO绑定一次即可,

然后每次在绘制的时候只需绑定对应的VAO,而不必每次再绑定VBO,然后去告诉GPU如何解析相关数据了,可以说是一个性能的优化了。

如何使用VAO

  • 首先调用函数glGenVertexArrays生成VAO
unsigned int VAO;
glGenVertexArrays(1, &VAO);
  • 调用函数glBindVertexArray绑定VAO
glBindVertexArray(VAO);
// 管理VBO,让VAO记住VBO的数据如何解析使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);

然后在绘制的时候使用函数glBindVertexArray(VAO)使用即可。

  • 退出时通过函数glDeleteVertexArrays删除VAO
glDeleteVertexArrays

主要绑定代码:

    // VAO
glGenVertexArrays(1,&vao);
glBindVertexArray(vao);
// VAO与VBO关联
glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);
// stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float
glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);
// 启用顶点数据
glEnableVertexAttribArray(positionHandle);
// stride 步长 每个颜色坐标之间相隔6个数据点,数据类型是float,颜色坐标索引从2开始
glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );
// 启用颜色顶点数据
glEnableVertexAttribArray(colorHandle);
// 解除绑定
glBindBuffer(GL_ARRAY_BUFFER,0); glBindVertexArray(0);

主要使用绘制代码:

   // VBO与VAO配合绘制
// 使用vao
glBindVertexArray(vao);
// 4个顶点绘制两个三角形组成矩形
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
glUseProgram(0);
// vao解除绑定
glBindVertexArray(vao);

下面是完整代码:

VBOVAOOpengl.h

static const int NUM_VBO = 3;

class VBOVAOOpengl:public BaseOpengl{

public:
VBOVAOOpengl();
virtual ~VBOVAOOpengl();
virtual void onDraw();
private:
GLint positionHandle{-1};
GLint colorHandle{-1};
GLuint vbo[NUM_VBO];
GLuint vao{0}; };

VBOVAOOpengl.cpp

// 顶点着色器
static const char *ver = "#version 300 es\n"
"in vec4 aColor;\n"
"in vec4 aPosition;\n"
"out vec4 vColor;\n"
"void main() {\n"
" vColor = aColor;\n"
" gl_Position = aPosition;\n"
"}"; // 片元着色器
static const char *fragment = "#version 300 es\n"
"precision mediump float;\n"
"in vec4 vColor;\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = vColor;\n"
"}"; // 使用绘制两个三角形组成一个矩形的形式(三角形带)
// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形
const static GLfloat VERTICES[] = {
0.5f,-0.5f, // 右下
0.5f,0.5f, // 右上
-0.5f,-0.5f, // 左下
-0.5f,0.5f, // 左上
}; const static GLfloat VERTICES_AND_COLOR[] = {
0.5f,-0.5f, // 右下
// 颜色
0.0f,0.0f,1.0f,1.0f,
0.5f,0.5f, // 右上
// 颜色
0.0f,0.0f,1.0f,1.0f,
-0.5f,-0.5f, // 左下
// 颜色
0.0f,0.0f,1.0f,1.0f,
-0.5f,0.5f, // 左上
// 颜色
0.0f,0.0f,1.0f,1.0f,
}; // rgba
//const static GLfloat COLOR_ICES[] = {
// 0.0f,0.0f,1.0f,1.0f
//}; // vbo颜色
const static GLfloat COLOR_ICES[] = {
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
}; VBOVAOOpengl::VBOVAOOpengl() {
initGlProgram(ver,fragment);
positionHandle = glGetAttribLocation(program,"aPosition");
colorHandle = glGetAttribLocation(program,"aColor");
// vbo
glGenBuffers(3,vbo);
glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);
glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES),VERTICES,GL_STATIC_DRAW); // 颜色
glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);
glBufferData(GL_ARRAY_BUFFER,sizeof(COLOR_ICES),COLOR_ICES,GL_STATIC_DRAW); // 顶点与颜色混合,先顶点坐标,再颜色坐标
glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);
glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES_AND_COLOR),VERTICES_AND_COLOR
,GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER,0); // VAO
glGenVertexArrays(1,&vao);
glBindVertexArray(vao);
// VAO与VBO关联
glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);
// stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float
glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);
// 启用顶点数据
glEnableVertexAttribArray(positionHandle);
// stride 步长 每个颜色坐标之间相隔6个数据点,数据类型是float,颜色坐标索引从2开始
glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );
// 启用颜色顶点数据
glEnableVertexAttribArray(colorHandle);
// 解除绑定
glBindBuffer(GL_ARRAY_BUFFER,0); glBindVertexArray(0); LOGD("program:%d",program);
LOGD("positionHandle:%d",positionHandle);
LOGD("colorHandle:%d",colorHandle);
} void VBOVAOOpengl::onDraw() {
// 清屏
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program); // 普通方式绘制
// /**
// * size 几个数字表示一个点,显示是两个数字表示一个点
// * normalized 是否需要归一化,不用,这里已经归一化了
// * stride 步长,连续顶点之间的间隔,如果顶点直接是连续的,也可填0
// */
// glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
// // 启用顶点数据
// glEnableVertexAttribArray(positionHandle);
//
// // 这个不需要glEnableVertexAttribArray
// glVertexAttrib4fv(colorHandle, COLOR_ICES); // ############################################# 分割线 #################################
// 使用VBO的方式绘制,顶点与颜色分开
// /**
// glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);
// glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,(void *)0);
// // 启用顶点数据
// glEnableVertexAttribArray(positionHandle);
//
// glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);
// glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,0,(void *)0);
// // 启用颜色顶点数据
// glEnableVertexAttribArray(colorHandle);
// // 解除绑定
// glBindBuffer(GL_ARRAY_BUFFER,0); // ############################################# 分割线 #################################
// VBO绘制 顶点坐标与颜色坐标一起
// glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);
// // stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float
// glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);
// // 启用顶点数据
// glEnableVertexAttribArray(positionHandle);
// // stride 步长 每个颜色坐标之间相隔6个数据点,数据类型是float,颜色坐标索引从2开始
// glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );
// // 启用颜色顶点数据
// glEnableVertexAttribArray(colorHandle);
// // 解除绑定
// glBindBuffer(GL_ARRAY_BUFFER,0); // ############################################# 分割线 ################################# // VBO与VAO配合绘制
// 使用vao
glBindVertexArray(vao);
// 4个顶点绘制两个三角形组成矩形
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
glUseProgram(0);
// vao解除绑定
glBindVertexArray(vao); // 禁用顶点
glDisableVertexAttribArray(positionHandle);
if(nullptr != eglHelper){
eglHelper->swapBuffers();
}
} VBOVAOOpengl::~VBOVAOOpengl() noexcept {
glDeleteBuffers(NUM_VBO,vbo);
glDeleteVertexArrays(1,&vao);
}

往期笔记

Opengl ES之EGL环境搭建

Opengl ES之着色器

Opengl ES之三角形绘制

Opengl ES之四边形绘制

Opengl ES之纹理贴图

关注我,一起进步,人生不止coding!!!

Opengl ES之VBO和VAO的更多相关文章

  1. Opengl ES之FBO

    FBO介绍 FBO帧缓冲对象,它的主要作用一般就是用作离屏渲染,例如做Camera相机图像采集进行后期处理时就可能会用到FBO.假如相机出图的是OES纹理,为了方便后期处理, 一般先将OES纹理通过F ...

  2. OpenGL ES 2: debugging, and improvements to VAO, VBO

    OpenGL ES 2: debugging, and improvements to VAO, VBO http://www.altdevblogaday.com/2013/10/12/opengl ...

  3. OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别

    OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及VAO区别 1.glVertex 最原始的设置顶点方法,在glBegin和glEnd之间 ...

  4. 基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)

    在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...

  5. 【OpenGL ES】关于VBO(Vertex Buffer Object)的一些坑——解析一些关于glBuffer的函数

    最近在写毕设的时候用到OpenGL ES中的VBO,由于对一些接口用到的变量不了解被坑得很惨,在此记录一下防止以后再被坑. 本文为大便一箩筐的原创内容,转载请注明出处,谢谢:http://www.cn ...

  6. 【C++ OpenGL ES 2.0编程笔记】8: 使用VBO和IBO绘制立方体 【转】

    http://blog.csdn.net/kesalin/article/details/8351935 前言 本文介绍了OpenGL ES 2.0 中的顶点缓冲对象(VBO: Vertex Buff ...

  7. OpenGL图形渲染管线、VBO、VAO、EBO概念及用例

    图形渲染管线(Pipeline) 图形渲染管线指的是对一些原始数据经过一系列的处理变换并最终把这些数据输出到屏幕上的整个过程. 图形渲染管线的整个处理流程可以被划分为几个阶段,上一个阶段的输出数据作为 ...

  8. [转]OpenGL图形渲染管线、VBO、VAO、EBO概念及用例

    直接给出原文链接吧 1.OpenGL图形渲染管线.VBO.VAO.EBO概念及用例 2.OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及 ...

  9. OpenGL ES中MRT应用

    Demo涵盖了OpenGL ES 3.0 的一系列新特性: 1.VAO和VBO 2.帧缓冲对象 3.MRT 效果: 代码: //yf's version #define STB_IMAGE_IMPLE ...

随机推荐

  1. 动手实践丨手把手教你用STM32做一个智能鱼缸

    摘要:本文基于STM32单片机设计了一款基于物联网的智能鱼缸. 本文分享自华为云社区<基于STM32+华为云IOT设计的物联网鱼缸[玩转华为云]>,作者: DS小龙哥 . 1. 前言 为了 ...

  2. 发明Linux的帕特里克

    Slackware Linux 是目前市场存活时间最长的 Linux 发行版之一,它基于一个叫做 SLS(Soft Landing Systems)的 Linux 项目而设计,易于使用和稳定. Sla ...

  3. MyBatis-通用Mapper-tk.mybatis的使用

    MyBatis-通用Mapper[更新中] tk.mybatis的使用 前言 使用MyBatis开发,如果是普通是同MyBatis进行开发,那么就需要在xml文件中编写大量的SQL.当数据库表结构发生 ...

  4. 清北学堂 2020 国庆J2考前综合强化 Day6

    目录 1. 题目 T1 双色球计数 题目描述 Sol 炼金术 题目描述 Sol T3 地铁大亨 题目描述 Sol T4 结束的派对 题目描述 Sol 算法 - 分治 1. 分治 2. 二分 3. 倍增 ...

  5. Vue 路由的简单使用(命名路由、query路由传参、params路由传参)

    1 # Vue 路由 2 # 1.理解:一个路由(toute)就是一组映射关系(key-value),多个路由需要路由器(router)进行管理 3 # 2.前端路由:key是路径,value是组件 ...

  6. "蔚来杯"2022牛客暑期多校训练营9 G Magic Spells【马拉车+哈希】

    四川今天又上热搜了,继南部疫情的未雨绸缪后,龙槽沟是真的倾盆大雨了.我没有兴趣虚伪矫情地对罹难的游人表达同情,因为人与人互不相通徒增谈资:我也没有兴趣居高临下地对擅闯的愚人表达不屑,因为你我皆为乌合之 ...

  7. 创新能力加速产业发展,SphereEx 荣获“中关村银行杯”『大数据与云计算』领域 TOP1

    8 月 9 日下午,2022 中关村国际前沿科技创新大赛"中关村银行杯"大数据与云计算领域决赛在北京市门头沟区中关村(京西)人工智能科技园·智能文创园落下了帷幕.SphereEx ...

  8. 每个开发人员都应该关注的7个优秀的GitHub仓库

    目录 1. FreeCodeCamp 2. Developer Roadmap 3. Awesome 4. Build Your Own X 5. Git Ignore 6. System Desig ...

  9. [CF1519C] Berland Regional (数论分块)

    题面 有 n 个学生和 n 所大学,每个学生在其中一所大学中学习,且各有一个能力值 s i s_i si​ . 某次组队打比赛的召集令会给一个数字 k ,表示团队数量.然后每所大学会先把自己的所有学生 ...

  10. LOJ#6089 小 Y 的背包计数问题 - DP精题

    题面 题解 (本篇文章深度剖析,若想尽快做出题的看官可以参考知名博主某C202044zxy的这篇题解:https://blog.csdn.net/C202044zxy/article/details/ ...