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

OpenGL 4.0的Tessellation Shader(细分曲面着色器)

细分曲面着色器(Tessellation Shader)处于顶点着色器阶段的下一个阶段,我们可以看以下链接的OpenGL渲染流水线的图:Rendering Pipeline Overview。它是由ATI在2001年率先设计出来的。


目录

  • 细分曲面着色器
  • 细分曲面Patch
  • 细分曲面控制着色器
    • 生成输出patch顶点
    • 细分曲面控制着色器变量
  • 控制细分曲面
    • 四边形细分曲面
    • 等值线细分曲面
  • 旁通细分曲面控制着色器
  • 细分曲面图元生成
    • 抽象patch
  • 细分曲面计算着色器
    • 指定图元生成域
    • 指定生成图元的面部朝向
    • 指定细分曲面坐标的空间
    • 额外的细分曲面计算着色器layout选项
    • 指定一个顶点的位置
    • 细分曲面计算着色器变量
  • 参考资料
  • 完整工程项目代码

细分曲面着色器

直到这个阶段,对于操作几何图元而言,只有顶点着色器对我们可用。尽管使用顶点着色器可以使用不少图形技术,不过顶点着色器也确实存在一些限制。一个就是它们在执行过程中无法创建额外的几何图形。它们仅仅更新与它们当前所处理的顶点相关的数据。而且,它们甚至无法访问在当前图元中其它顶点的数据。

为了解决那些问题,OpenGL流水线含有几个其它着色器阶段来打破这些限制。我们这里所要介绍的细分曲面着色器可以使用一个新的几何图元类型,称为 patch(斑点、碎片),来生成一个三角形网格。

细分曲面着色器在OpenGL流水线中增添了两个着色器阶段来生成一个几何图元的网格。比起在使用顶点着色器时不得不指定所有线与三角形来形成自己的模型,在使用细分曲面时,一开始指定一个 patch,它是一列排好序的顶点。当一个 patch 被渲染时,细分曲面控制着色器Tessellation Control Shader) 先执行,对 patch 顶点进行操作,并指定从 patch 中应该生成多少几何图形。细分曲面控制着色器是可选的,我们后面会看到,如果不用它的话需要使用哪些条件。在细分曲面控制着色器完成之后,第二个着色器——细分曲面计算着色器Tessellation Evaluation Shader)使用细分曲面坐标来放置所生成的顶点,并且将它们发送到光栅化器,或发送到几何着色器做进一步处理。


细分曲面Patch

细分曲面过程并不对OpenGL典型的几何图元(点、线和三角形)进行操作,而是使用一个新的图元(在OpenGL 4.0版本中新增的),称为 patchpatch 由流水线中所有活动的着色阶段处理。相比起来,其它图元类型仅仅被顶点、片段和几何着色器处理,而旁通细分曲面阶段。实际上,如果有任一细分曲面着色器是活跃的,那么传递任何其它几何类型会产生一个 GL_INVALID_OPERATION 错误。相反地,如果企图渲染一个 patch 而没有任何细分曲面着色器(明确地说,是一个细分曲面计算着色器;我们会看到细分曲面控制着色器是可选的),那么将也会得到一个 GL_INVALID_OPERATION 错误。

patch 仅仅是传入到OpenGL的一列顶点列表,该列表在处理期间保存它们的次序。当用细分曲面与 patch 进行渲染时,使用像 glDrawArrays() 这样的渲染命令,并指定从绑定的顶点缓存对象(VBO)将被读出的顶点的总数,然后为该绘制调用进行处理。当用其它的OpenGL图元进行渲染时,OpenGL基于在绘制调用中所指定的图元类型而隐式地知道要使用多少顶点,比如使用三个顶点来绘制一个三角形。然后,当使用一个 patch 时,需要告诉OpenGL顶点数组中要使用多少个顶点来组成一个 patch,而这可以通过使用 glPatchParameteri() 进行指定。由同一个绘制调用(drawcall)所处理的所有指定的 patch,它们的尺寸(即每个patch的顶点个数)将是相同的。

void glPatchParameteri(GLenum pname, GLint value);
/*** 使用value来指定一个patch中的顶点个数。pname必须设置为GL_PATCH_VERTICES。* 如果value小于零或大于GL_MAX_PATCH_VERTICES,将会产一个GL_INVALID_ENUM的错误。* 一个patch的默认顶点个数是三。如果一个patch的顶点个数小于参数value值,那么该patch将被忽略,从而不会有几何图形产生。
*/

要指定一个 patch,使用类型 GL_PATCHES 输入到任一OpenGL绘制命令。以下代码描述了发射两个 patch,每个 patch 含有四个顶点,然后通过 glDrawArrays 绘制命令进行渲染。

GLfloat vertices[][2] = {{-0.75f, -0.25f}, {-0.25f, -0.25f}, {-0.25f, 0.25f}, {-0.75f, 0.25f},{0.25f, -0.25f}, {0.75f, -0.25f}, {0.75f, -0.25f}, {0.75f, 0.25f}, {0.25f, 0.25f}
};glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(vPos, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glPatchParameteri(GL_PATCH_VERTICES, 4);
glDrawArrays(GL_PATCHES, 0, 8);

每个 patch 的顶点首先由当前绑定的顶点着色器处理,然后用于初始化数组 gl_in,这个变量在细分曲面控制着色器中被隐式地声明。gl_in 中的元素个数与由 glPatchParameteri() 所指定的 patch 大小相同。在一个细分曲面着色器内部,变量 gl_PatchVerticesIn 提供了 gl_in 中的元素个数(就好比使用 sizeof(gl_in) / sizeof(gl_in[0]) 进行查询)。


细分曲面控制着色器

一旦应用发射了一个 patch,细分曲面控制着色器就会被调用(如果有所绑定的话)并且负责完成以下行动:

  • 生成细分曲面输出 patch 顶点,传递到细分曲面计算着色器,以及更新任一每个顶点的,或每个 patch 的属性值,若有必要的话。
  • 指定细分曲面程度因子,控制图元生成器的操作。这些是特殊的细分曲面控制着色器变量,称为 gl_TessLevelInnergl_TessLevelOuter,并在细分曲面控制着色器中隐式声明。

我们将依次讨论这些行动的每一个。


生成输出patch顶点

细分曲面控制器使用由应用所指定的顶点——这些顶点我们称为输入 patch 顶点(作为顶点着色器的输出)——来生成一组新的顶点,这些新的顶点为输出 patch 顶点。它们存放在细分曲面控制着色器的 gl_out 数组中。细分曲面控制着色器在产出输出** patch** 顶点时,可以修改传递自应用的值(比如顶点属性),也可以创建或移除来自输入 patch 顶点中的顶点。

使用一个 layout 构造在细分曲面控制着色器中指定输出 patch 顶点的个数。下面语句描述了设置输出 patch 顶点的个数为16。

layout (vertices = 16) out;

layout 指示符中的 vertices 参数所设置的值做了两件事情:它设置了输出 patch 顶点 gl_out 的大小;并且指定了细分曲面控制着色器将被执行多少次:对每个输出 patch 顶点执行一次。

为了确定正在处理哪个输出顶点,细分曲面控制着色器可以使用 gl_InvocationID 变量。该变量最经常被用作为 gl_out 数组的一个索引。当一个细分曲面控制着色器在执行时,它具有对所有 patch 顶点数据的访问,包括输入顶点和输出顶点。这可能会导致发射一次着色器调用,该调用需要使用来自另一个着色器调用的数据值,但是那个着色器调用尚未发生。细分曲面控制着色器可以使用GLSL的 barrier() 函数,该函数使得对一个输入 patch 的所有控制着色器的执行,并等待所有这些着色器的执行到达那个函数的调用点,从而确保了可能要设置的所有数据值将被计算。

细分曲面控制着色器的一个普遍的习惯用法仅仅是将输入 patch 顶点输出到此着色器的外面。下面的例子描述了带有四个顶点的一个输出 patch

#version 410 corelayout (vertices = 4) out;void main(void)
{gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;// 下面设置细分曲面程度
}

细分曲面控制着色器变量

gl_in 数组实际上是一个结构体的数组,每个元素被定义为:

in gl_PerVertex {vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[];}gl_in[gl_PatchVerticesIn];

并且对于每个需要向下一个阶段传递的值(比如,向下传递到细分曲面计算着色器),需要进行相应地赋值。如上述代码片段所述,传递了 gl_Position 变量。

gl_out 数组具有相同的结构体成员,不过数组大小与 gl_in 不同,它是由 gl_PatchVerticesOut 来指定的。而这个值则是在细分曲面控制器中的 out 这一 layout 限定符中设置。此外,以下标量值用于确定正在被着色的图元和输出顶点:

  • gl_InvocationID:当前细分曲面着色器的输出顶点的调用索引
  • gl_PrimitiveID:当前输入 patch 的图元索引
  • gl_PatchVerticesIn:输入patch中的顶点个数,它作为 gl_in 数组变量中的元素个数
  • gl_PatchVerticesOut:输出 patch 中的顶点个数,它作为 gl_out 数组变量中的元素个数

如果我们需要额外的基于每个顶点的属性值,或为输入或为输出,那么这需要在我们的细分曲面控制着色器中将它们声明为 inout 数组。一个输入数组的大小需要与输入 patch 大小相同,或者可以被声明为缺省大小的,这样OpenGL将会为其所有值适当地分配空间。类似地,每个顶点的输出属性需要与输出 patch 中的顶点个数相一致,也可以为输出属性声明为缺省大小的。输出属性值将会被传递到细分曲面计算着色器,作为其输入属性值。

比如:

#version 410 corelayout (vertices = 4) out;in vec4 vertexCoeffs1[4];    // 我们假定指定一个输入patch含有4个顶点
in vec4 vertexCoeffs2[];     // 这里使用缺省大小的数组变量,OpenGL将会自动为其分配大小out vec2 vertexTexCoord1[4];    // 这里需要用上面out的layout (vertices)值一致
out vec2 vertexTexCoord2[];     // 这里使用缺省大小的数组变量,OpenGL将会自动为其分配大小void main(void)
{// Do something here
}

上面给出的是基于逐个顶点的属性值,它们可以用 gl_InvocationID 作为索引,不过要注意的是,gl_InvocationID 标识的是当前细分曲面着色器的输出顶点的调用索引。我们可以使用 patch 限定符来声明每个 patch 的输出变量。每个 patch 的变量不是以数组方式定义而是以普通单实例变量的方式来定义。当然,我们也可以将它们定义为数组。所有细分曲面控制着色器的调用看到的都是同一个 patch 变量。比如:

#version 410 corepatch out vec4 data;layout (vertices = 4) out;void main(void)
{// Do something heredata = vec4(1.0, 0.0, 0.0, 1.0);
}

这里,我们定义了一个标识符为 data 的 patch 输出变量。对于每次细分曲面控制着色器的调用,data 的值都被写为 vec4(1.0, 0.0, 0.0, 1.0)。因此,任一细分曲面控制着色器可以写基于每个 patch 的输出变量;实际上,所有细分曲面控制着色器的调用一般都将写到一个基于每个 patch 的变量。只要它们都写相同的值,那么一切都是良好的。


控制细分曲面

一个细分曲面控制着色器的另一个功能是指定对输出 patch 细分多少。然而我们还没详细地讨论细分曲面计算着色器,它们控制用于渲染的输出 patch 的类型,结果也就是细分曲面所发生的域。OpenGL支持三种细分曲面域:四边形,三角形,和等值线集合。这些通过细分曲面计算着色器中的 inlayout 进行指定。

细分曲面的数量通过指定两组值:内部和外部细分曲面程度来控制的。外部细分曲面的值控制域的周边是如何划分的,然后存放在一个隐式声明的名为 gl_TessLevelOuter 的含有四个元素的数组中。而内部细分曲面程度指定了域的内部如何进行划分,然后存放在一个名为 gl_TessLevelInner 的含有两个元素的数组中。所有细分曲面程度因子是浮点值,并且我们将会看到浮点值在细分曲面上以一个比特的效果。最后一点是,尽管隐式声明的细分曲面程度因子数组的维度是固定的,不过从那些数组所使用的值的个数依赖于细分曲面域的类型。下面来看看这两个OpenGL内建的细分曲面输出 patch 变量的声明:

patch out float gl_TessLevelOuter[4];
patch out float gl_TessLevelInner[2];

理解外部与内部细分曲面程度如何操作是让细分曲面做我们想要做的事情的关键。每个细分曲面程度因子指定了对一个区域划分多少条“线段”,以及生成多少细分曲面坐标与几何图元。这种划分如何完成根据不同域类型而有所不同。我们将依次讨论域的每种类型。


四边形细分曲面

使用四边形域可能是最直观的,因此我们先介绍这种类型。当输入 patch 形状为矩形时,这很有用,当我们可能使用二维的样条曲面时,比如 Bézier 曲面。四边形域使用所有内部和外部细分曲面程度来划分单位正方形。比如,如果我们用以下代码来设置细分曲面程度因子,那么OpenGL将会把四边形域细分为如下图所示的样子。

#version 410 corelayout (vertices = 4) out;void main(void)
{gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;// 下面设置细分曲面程度gl_TessLevelInner[0] = 3.0;     // 内部划分3条垂直区域,即内部新增2列顶点gl_TessLevelInner[1] = 4.0;     // 内部划分4条水平区域,即内部新增3行顶点gl_TessLevelOuter[0] = 2.0;     // 左边2条线段gl_TessLevelOuter[1] = 3.0;     // 下边3条线段gl_TessLevelOuter[2] = 4.0;     // 右边4条线段gl_TessLevelOuter[3] = 5.0;     // 上边5条线段
}

下图为上述细分曲面控制着色器可能会得到的几何图形样子。

黄线垂直划分绿线水平划分最终完整
quad_yellowquad_greenquad_dst

上图中,三条黄色线条表示三条垂直区域;四条绿色线条表示四条水平区域。

注意,外部细分曲面程度值对应于围绕周边的每条边的线段个数,而内部细分曲面程度指定了域的内部空间中水平与垂直方向上有多少个“区域”。使用虚线则是将整个域用三角形进行划分。域的三角形划分是依赖于实现的。实心圆点表示细分曲面坐标,每个坐标都会提供给细分曲面计算着色器,作为其输入。在四边形域的情况下,细分曲面坐标有两个坐标值 (u, v),两者值的范围都在 [0, 1] 范围内,并且每个细分曲面坐标将传递到细分曲面计算着色器中,作为它的一次调用。


等值线细分曲面

类似于四边形域,等值线域也生成 (u, v) 对作为给细分曲面计算着色器的细分曲面坐标。然而,等值线仅仅使用外部细分曲面程度的两个元素值来判定划分量(这里没有用到内部细分曲面程度)。

下面是设置等值线域的细分曲面程度:

gl_TessLevelOuter[0] = 6;      // 6条等值线
gl_TessLevelOuter[1] = 8;      // 每条等值线被划分为8条线段

下图为可能的结果图形:

patch_lines


旁通细分曲面控制着色器

正如我们所提到过的,细分曲面着色器往往只是一个直通着色器,将数据从输入直接到输出。在这种情况下,我们实际上可以使一个细分曲面着色器进行旁通,仅仅使用主机端OpenGL API来设置细分曲面程度因子。使用 glPatchParameterfv() 函数来设置内部与外部细分曲面程度。

void glPatchParameterfv(GLenum pname, const GLfloat *values);/*** 当没有细分曲面控制着色器时,设置内部与外部细分曲面程度。* 参数pname要么是GL_PATCH_DEFAULT_OUTER_LEVEL,要么是GL_PATCH_DEFAULT_INNER_LEVEL。* 当pname是GL_PATCH_DEFAULT_OUTER_LEVEL时,参数values必须是含有四个单精度浮点值的数组,指定四个外部细分曲面程度。* 类似地,当pname是GL_PATCH_DEFAULT_INNER_LEVEL时,values必须是含有两个单精度浮点值的数组,指定两个内部细分曲面程度。
*/

细分曲面图元生成

图元生成是一个固定功能阶段,负责从输入 patch 来创建一组新的图元。此阶段仅当一个细分曲面计算着色器在当前程序或程序流水线中活动时才会执行。图元生成受以下因素影响:

  • 细分曲面程度
  • 被细分的顶点的空间划分,这个由细分曲面计算着色器阶段进行定义。它可以是 equal_spacingfractional_even_spacingfractional_odd_spacing
  • 由后续细分曲面着色器所定义的图元类型,即 trianglesquadsisolines。细分曲面计算着色器也可以迫使细分曲面的生成作为一系列的点,而不是三角形或线。这个可以通过 point_mode 来指定。
  • 由后续细分曲面计算着色器所定义的图元生成次序,比如 cw(顺时针)或 ccw(逆时针)。

抽象patch

注意,图元生成不受细分曲面控制着色器中用户定义的输出影响(或当没有细分曲面控制着色器时,也不受顶点着色器的影响),不受细分曲面控制着色器的输出patch大小影响,也不受任一每个 patch 的细分曲面控制着色器输出的影响,而只受细分曲面程度的影响。细分曲面阶段的图元生成部分完全对实际的顶点坐标与其它 patch 数据是不可见的。

图元生成系统的目的是确定要生成多少个顶点,用哪个次序来生成它们,以及用哪种图元来构造它们。实际为这些顶点的每个顶点的数据,诸如位置、颜色等等,是通过细分曲面计算着色器来生成的,基于图元生成器所提供的信息。

因为这种对分,图元生成器对可以被认为是一个“抽象patch”的东东进行操作。它并不从细分曲面控制器的输出来看patch;它仅考虑一个抽象四边形、三角形或等值线块的细分曲面。

依赖于抽象 patch 类型,图元生成器计算不同数量的细分曲面程度并应用不同的细分曲面算法。每个所生成的顶点在一个抽象 patch 内具有一个规格化的位置(即,坐标范围在 [0, 1] 之内)。这个位置具有两个或三个分量,依赖于patch的类型。这些坐标通过内建的

in vec3 gl_TessCoord;

输入提供给细分曲面计算着色器。


细分曲面计算着色器

OpenGL细分曲面流水线的最后一个阶段就是细分曲面计算着色器执行。绑定的细分曲面计算着色器对图元生成器发射的每个细分曲面坐标逐个执行,并负责确定从细分曲面坐标所得到的顶点的位置。我们将看到,细分曲面计算着色器看上去与顶点着色器类似,将顶点变换到屏幕位置(除非细分曲面着色器的数据将被进一步由几何着色器来处理)。

配置一个细分曲面计算着色器的第一步是配置图元生成器,这通过使用一个 layout 指示符来完成。其参数指定了细分曲面域与后续所生成的图元的类型;实体图元的面部朝向(用于做面剔除);以及在图元生成期间如何应用细分曲面程度。


指定图元生成域

我们现在将描述细分曲面计算着色器的 in 这个 layout 的参数。首先,我们先谈论指定细分曲面域。我们之前已经提及过,一共有三种类型的域来生成细分曲面坐标:

  • quads——单位正方形中的一个矩形域;域坐标:带有范围在 [0, 1] 内的 u, v 值的一个个坐标对 (u, v)。
  • triangles——使用重心坐标的一个三角形域;域坐标:带有范围在 [0, 1] 内的a、b、c三个值的坐标 (a, b, c),这里 a + b + c = 1
  • isolines——跨单位正方形的一组线;域坐标:u 值范围在 [0, 1],v 值范围在 [0, 1) 范围的 (u, v) 坐标对。

指定生成图元的面部朝向

与OpenGL中任何填充的图元一样,所发射的顶点的次序决定了图元的脸部朝向。由于我们在这种情况下不直接发射顶点,而只是让图元生成器为我们来做,不过我们需要告诉图元生成器我们图元的右手旋转方向。在 layout 指示符中,指定 cw 为顺时针旋转,ccw 为逆时针。


指定细分曲面坐标的空间

此外,我们可以控制外部细分曲面程度的浮点值如何用在确定周边的细分曲面坐标生成上。(内部细分曲面程度受这些选项影响。)

  • equal_spacing——细分曲面程度被裁减到 [1, max] 范围内,然后取整到下一个最大整数值。
  • fractional_even_spacing——值被裁减到 [2, max] 范围内,然后取整到下一个最大偶整数值n。边然后被划分为 n-2 条等长部分,以及两个剩余部分,每个在一端,剩余部分长度可能比其它长度要短。
  • fractional_odd_spacing——值被裁减到 [1, max-1] 范围内,然后取整到下一个最大奇整数值n。边然后被划分为 n-2 条等长部分,以及两个剩余部分,每个在一端,剩余部分长度可能比其它长度要短。

额外的细分曲面计算着色器layout选项

最后,如果我们想输出点,而不是等值线或填充区域的话,我们可以应用 point_mode 选项。该选项将为每个由细分曲面计算着色器所处理的顶点渲染一单个点。

layout 指示符内的选项的次序不重要。下面例子描述的是一个生成三角形域的图元,使用相等空间,逆时针方向的三角形,但只渲染点,而不是互联的图元。

layout (triangles, equal_spacing, ccw, point_mode) in;

指定一个顶点的位置

从细分曲面控制着色器输出的顶点(即,在 gl_out 数组中的 gl_Position 的值)在计算着色器中的 gl_in 变量中可用。当它们与细分曲面坐标相结合时,可以用于生成输出顶点的位置。

细分曲面坐标在变量 gl_TessCoord 中提供给计算着色器。在下面例子中我们使用相等空间划分的四边形来渲染一个简单的 patch。在这个例子中,细分曲面坐标用于对表面进行上色,然后此例子也描述了如何计算顶点坐标。我们这里要注意的是,gl_in 中的 gl_Position 相当于原始的 patch 每个顶点的坐标,而 gl_TessCoord 则是经过细分曲面之后的新增细分曲面顶点,这些顶点的坐标值是被规格化在 [0, 1] 范围内的。我们通过以原 patch 的顶点坐标与当前处理的细分曲面顶点坐标做相应的插值计算来获得此细分曲面顶点最后输出的坐标值。我们在细分曲面计算着色器中可以访问所有 gl_in 数组的元素,即每次调用可以访问当前 patch 所有输入的顶点坐标。

#version 410 corelayout (quads, equal_spacing, ccw) in;out vec4 color;void main(void)
{float u = gl_TessCoord.x;float omu = 1 - u;    // omu为1减去"u"float v = gl_TessCoord.y;float omv = 1 - v;    // omv为1减去"v"color = gl_TessCoord; // color最后给到片段着色器时,值为(gl_TessCoord.x, gl_TessCoord.y, 0.0, 1.0)gl_Position = omu * omv * gl_in[0].gl_Position +u * omv * gl_in[1].gl_Position +u * v * gl_in[2].gl_Position +omu * v * gl_in[3].gl_Position;
}

细分曲面计算着色器变量

与细分曲面控制着色器类似,细分曲面计算着色器也有 gl_in 数组,它是一个结构体数组,每个元素如下定义:

in gl_PerVertex {vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[];
} gl_in[gl_PatchVerticesIn];

输出顶点的数据被存放在一个接口块中,如下定义:

out gl_PerVertex {vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[];
};

参考资料

  • 《OpenGL Programming Guide Eighth Edition – The Official Guide tyo Learning OpenGL, Version 4.3》
  • Tessellation

完整工程项目代码

OpenGL4.1 Tessellation Shader使用demo(基于macOS)

相关文章:

OpenGL 4.0的Tessellation Shader(细分曲面着色器)

细分曲面着色器(Tessellation Shader)处于顶点着色器阶段的下一个阶段,我们可以看以下链接的OpenGL渲染流水线的图:Rendering Pipeline Overview。它是由ATI在2001年率先设计出来的。 目录 细分曲面着色器细分曲面Patch细分曲面控…...

项目经理如何及时掌控项目进度?

延迟是指超出计划的时间,而无法掌控则意味着管理者对实际情况一无所知。 为了解决这些问题,我们需要建立好的制度和沟通机制。例如使用项目管理软件来跟踪进度、定期开会并避免沟通障碍等。 管理者可以建立相关制度: 1、建立进度记录制度。…...

HTML <applet> 标签

HTML5 中不支持 <applet> 标签在 HTML 4 中用于定义嵌入式小程序(插件)。 实例 一个嵌入的 Java applet: <applet code="Bubbles.class" width="350" height="350"> Java applet that draws animated bubbles. </applet&g…...

加密与解密

加密与解密 加密方式分类 加密方式主要分为两种 一种是对称加密一种是非对称加密 对称加密 对称和非对称两种方式主要说的是加密和解密两个过程。 如果对数据用一个钥匙进行了加密&#xff0c;那么&#xff0c; 你想成功读取到这个加密了的数据的话&#xff0c;就必须对这…...

京东金融Android瘦身探索与实践

作者&#xff1a;京东科技 冯建华 一、背景 随着业务不断迭代更新&#xff0c;App的大小也在快速增加&#xff0c;2019年~2022年期间一度超过了117M&#xff0c;期间我们也做了部分优化如图1红色部分所示&#xff0c;但在做优化的同时面临着新的增量代码&#xff0c;包体积一直…...

open3d-ml 读取SemanticKITTI Dataset

目录 1. 下载dataset 2. 读取并做可视化 3. 源码阅读 3.1 读取点云数据-bin格式 3.2 读取标注数据-.label文件 3.3 读取配置 3.4 test 3.5 train 1. 下载dataset 以SemanticKITTI为例。下载链接&#xff1a;http://semantic-kitti.org/dataset.html#download 把上面三…...

6.其他函数

1.时间日期类 -- current_date() 返回当前日期 -- date_add(date, n) 返回从date开始n天之后的日期 -- date_sub(date, n) 返回从date开始n天之前的日期 -- datediff(date1, date2) 返回date1-date2的日期差 -- year(date) 返回…...

2023年宜昌市中等职业学校技能大赛 “网络搭建与应用”竞赛题-1

2023年宜昌市中等职业学校技能大赛 “网络搭建与应用”竞赛题 一、竞赛内容分布 “网络搭建及应用”竞赛共分二个部分&#xff0c;其中&#xff1a; 第一部分&#xff1a;企业网络搭建部署项目&#xff0c;占总分的比例为50%&#xff1b; 第二部分&#xff1a;企业网络服…...

Linux权限划分的原则

考察的不仅是一个具体的指令&#xff0c;还考察对技术层面的认知。 如果对 Linux 权限有较深的认知和理解&#xff0c;那么完全可以通过查资料去完成具体指令的执行。更重要的是&#xff0c;认知清晰的程序员可以把 Linux 权限管理的知识迁移到其他的系统设计中。 权限抽象 一…...

PhotoScan拼接无人机航拍RGB照片

目录 背景 拼接步骤 1.新建并保存项目 2.添加照片 3.对齐照片 4.添加标记&#xff08;Markers&#xff09; 5.添加地面控制点 6.建立批处理任务 7.使用批处理文件进行批处理 8.导出DEM 9.导出DOM 背景 本文介绍使用地面控制点&#xff08;GCPs&#xff09;拼接​​…...

【设计模式】责任链模式的介绍及其应用

责任链的介绍 责任链模式是一种对象的行为模式。在责任链模式里&#xff0c;很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递&#xff0c;直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求&a…...

一些思考关于行业,关于方向,关于人生路线

一些碎碎念 选择与视角工程与科研平台与信息敢问路在何方 选择与视角 两年前的秋招时几乎速通了出现在学校招聘会上的几乎出现的每一个offer&#xff0c;那也是我人生第一次收获到如此多的肯定与选择&#xff0c;为此我在b站上上传了一期就业解读&#xff0c;作为一个冷门到几…...

fbx sdk的使用介绍

我们平时需要围绕fbx写一些小工具&#xff0c;虽说使用ascii格式的fbx可以直接进行字符串解析&#xff0c;并且网上也有一些基于ascii解析的开源库&#xff0c;但在制作一些通用的工具时&#xff0c;使用fbx sdk进行编写肯定是最好的。 1.下载fbx sdk和cmake 要用cmake生成vi…...

mvvm模式

mvvm是Model-View-ViewModel的缩写&#xff0c;是前端的一种架构模式 M - Model&#xff0c;模型 对应data数据 V - View&#xff0c;视图 对应用户界面&#xff0c;DOM元素 VM - ViewModel&#xff0c;视图模型 对应vue实例对象&#xff0c;是连接model和view的桥梁 …...

Spring/SpringBoot常用注解总结

为什么要写这篇文章&#xff1f; 最近看到网上有一篇关于 SpringBoot 常用注解的文章被转载的比较多&#xff0c;我看了文章内容之后属实觉得质量有点低&#xff0c;并且有点会误导没有太多实际使用经验的人&#xff08;这些人又占据了大多数&#xff09;。所以&#xff0c;自…...

2023 年第八届数维杯大学生数学建模挑战赛 B 题 节能列车运行控制优化策略

在城市交通电气化进程快速推进的同时&#xff0c;与之相应的能耗增长和负面效应也 在迅速增加。城市轨道交通中的快速增长的能耗给城轨交通的可持续性发展带来 负担。2018 年&#xff0c;北京、上海、广州地铁负荷占全市总负荷的 1.5%-2.5%,成为了 城市电网的最大单体负荷[1]。…...

【Swift】 NSButton的用法和示例

NSButton是macOS开发中常用的控件&#xff0c;用于创建按钮。它有许多用法和需要注意的事项&#xff0c;下面介绍其中的一些。 1. 创建按钮&#xff1a;使用init(frame:)或init(title:action:)初始化按钮 let button NSButton(frame: NSRect(x: 0, y: 0, width: 100, height…...

2023什么蓝牙耳机好?经销商盘点新手必入蓝牙耳机品牌

蓝牙耳机是除手机外我们使用频率最高的数码产品&#xff0c;我做蓝牙耳机经销商五年来&#xff0c;对各个品牌都有深入了解。近期看到很多新手们咨询什么蓝牙耳机好&#xff0c;我给大家盘点一下新手必看的五大蓝牙耳机品牌。 1.JEET Air 2蓝牙耳机 推荐理由&#xff1a;专为舒…...

MySQL基础(二十)MySQL的数据目录

1. MySQL8的主要目录结构 find / -name mysql1.1 数据库文件的存放路径 show variables like datadir; # /var/lib/mysql/1.2 相关命令目录 相关命令目录&#xff1a;/usr/bin 和/usr/sbin。 1.3 配置文件目录 配置文件目录&#xff1a;/usr/share/mysql-8.0&#xff08;命…...

低代码行业未来如何?大家都真的看好低代码开发吗?

低代码行业未来如何&#xff1f;大家都真的看好低代码开发吗&#xff1f; 是否一定需要开发人员&#xff1f;低代码和无代码平台会取代传统编程吗&#xff1f;低代码/无代码真的是未来吗&#xff1f; 无疑是需要且重要的。今天就来解答为什么低/零代码工具越来越受欢迎&#xf…...

mac m2芯片 安装 brew 和cocoapods

Homebrew的安装 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 这里可能会失败&#xff0c;如 git clone 时候报错 error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly before end of the underlyi…...

SingleR --细胞注释

文章目录 briefExample使用内置的 references使用其他注释好的数据集作为 reference singleR还提供了注释诊断的方法 brief Example The celldex package provides access to several reference datasets (mostly derived from bulk RNA-seq or microarray data)。 The Human…...

【结构与算法】—— 游戏概率常用算法整理 | 游戏中的常见概率设计分析

&#x1f4e2;博客主页&#xff1a;肩匣与橘&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由肩匣与橘编写&#xff0c;首发于CSDN&#x1f649;&#x1f4e2;生活依旧是美好而又温柔的&#xff0c;你也是✨ …...

WebRTC系列-适配GPUImage及其他视频处理改造

文章目录 1. GPUImage 的一些改动1.1 GPUImage数据输入源1.1 GPUImage数据输出源2.WebRTC摄像头采集类改造GPUImage使用OpenGL提供了很多的图像处理算法,包括最常用的美颜处理、水印等功能,这些基本的功能如何添加到WebRTC中,本文以美颜为例子,叙述主要的改造流程;同时也适…...

day43—选择题

文章目录 1.A,B两台机器都正常工作,B机器未监听任何端口.如果A机器向B机器80端口发送SYN包,会收到何种类型的回包&#xff08;D&#xff09;2.下列哪个IP地址可以分配给一台计算机&#xff08;D&#xff09;3.以下哪个ip不和10.11.12.91/28处于同一个子网&#xff08;D&#xf…...

<<和>>操作符、取地址重载、const关键字

文章目录 自定义类型<<和>>重载const关键字取地址重载&#xff08;类的默认构造函数&#xff09; 自定义类型<<和>>重载 在内置类型中&#xff0c;<<和>>可以自动识别 在自定义类型冲&#xff0c;运算符重载&#xff0c;<<和>&…...

数学模型,如何计算概率?

既然是数学模型,那应该如何计算呢? 最简单的方法,当然就是用统计学的方法去计算了,简单说来,就是靠输入的上下文进行统计,计算出后续词语的概率,比如「你吃了晚饭了吗」,「你吃了」后面按照概率,名词如「饭」或「晚饭」等概率更高,而不太可能是动词,如「睡」「睡觉…...

【Ehcache技术专题】「入门到精通」带你一起从零基础进行分析和开发Ehcache框架的实战指南(Spring整合ehcache)

带你一起从零基础进行分析和开发Ehcache框架的实战指南&#xff08;Spring整合ehcache&#xff09; 回顾一下Ehcache主要的特性 Spring框架所支持的第三方缓存Spring Cache的实现方式Spring Cache基本准备工作定义Ehcache配置文件启用Spring-CacheXML风格的xml代码 JavaConfig注…...

合肥市2023年度高校毕业生“双千培养工程”培训项目学员招募公告

为贯彻落实人社部实施促进高校毕业生等青年就业创业推进计划要求&#xff0c;提升高校毕业生就业技能&#xff0c;拟开展高校毕业生“双千培养工程”培训项目。根据工作计划安排&#xff0c;现面向高校和社会招募学员参加培训&#xff0c;培训方向为大数据应用、PythonAI人工智…...

重写Properties类,实现对properties文件的有序读写,数据追加,解决中文乱码

前言 *.properties文件&#xff0c;是 Java 支持的一种配置文件类型&#xff0c;并且 Java 提供了 properties 类来读取 properties 文件中的信息。文件中以键值对 "键值"的形式&#xff0c;存储工程中会多次重复使用的配置信息&#xff0c;通过“Properties”类来读…...