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 core

layout (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 core

layout (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 core

patch out vec4 data;

layout (vertices = 4) out;

void main(void)
{
    // Do something here

    data = 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 core

layout (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 core

layout (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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Node.js对ES6 及更高版本的支持

目录 1、简介 2、默认情况下什么特性随着 Node.js 一起发布? 3、有哪些特性在开发中? 4、移除这个标记(--harmony)吗 5、Node.js 对应 V8 引擎 1、简介 Node.js 是针对 V8 引擎构建的。通过与此引擎的最新版本保持同步&…

【HMS Core】Health Kit想要查看数据是来自用户的哪个设备,如何查看?

【问题描述1】 如何查看运动健康数据是来自用户的哪个设备? 【解决方案】 可以通过返回的数据中携带的dataCollectorId来查询提供数据的设备信息: 请求示例(以查询睡眠记录详情为例): 1、查询睡眠记录并关联睡眠状…

用友携国资国企走进浙江龙游,共探区县国资智慧监管新样板

近日,由龙游县国有资产经营有限公司指导,用友网络科技股份有限公司(以下简称:用友网络)主办的“成为数智企业 迈向高质量发展——2023走进龙游数智化观摩研讨会”在浙江龙游成功举办!全国近百位国资国企负责…

操作系统学习02

!!!由于感冒和出去玩,好几天没学这些计算机基础知识了!!! 抓紧跟上嘿嘿嘿 1、内存管理主要做了什么 操作系统的内存管理非常重要,主要负责下面这些事情: 内存的分配与…

shell脚本--函数

目录 一:shell函数定义 1.函数的含义 2.函数的优点 3.函数的格式 4.函数返回值 (1)return输出 (2)echo输出 二:函数传参 1.情景一 2.情景二 3.情景三 4.情景四 三:递归函数 1.递归函数定义 2.通过…

ASEMI代理ADUM3223ARZ-RL7原装ADI车规级ADUM3223ARZ-RL7

编辑:ll ASEMI代理ADUM3223ARZ-RL7原装ADI车规级ADUM3223ARZ-RL7 型号:ADUM3223ARZ-RL7 品牌:ADI /亚德诺 封装:SOIC-16 批号:2023 安装类型:表面贴装型 引脚数量:16 工作温度:-40C~125…

利用MQ事务消息实现分布式事务

MQ事务消息使用场景 消息队列中的“事务”,主要解决的是消息生产者和消息消费者的数据一致性问题。 拿我们熟悉的电商来举个例子。一般来说,用户在电商 APP 上购物时,先把商品加到购物车里,然后几件商品一起下单,最后…

2路 QSFP,40G 光纤的数据实时采集(5GByte/s 带宽)板卡设计原理图 -PCIE732

板卡概述 PCIE732 是一款基于 PCIE 总线架构的高性能数据传输卡,板卡具有 1 个 PCIex8 主机接口、2 个 QSFP40G 光纤接口,可以实现 2 路 QSFP 40G 光纤的数据实时采集、传输。板卡采用 Xilinx 的高性 能 Kintex UltraScale 系列 FPGA 作为实时处理器…

qiankun 微前端 demo(Vue2)

前言 这是我最近刚开始学微前端(qiankun框架)做的一个小demo,做的时候还是遇到很多问题的,在网上也是看了很多别人的Blog,最后也是磨出来了😂😂😂;这篇文章总统分为分为…

windows 编译 opencv

编译需要的基础工具 #cmake是配置构建工具,mingw是编译工具 cmake CMake是一款跨平台的编译管理工具,可以自动生成各种不同编译环境(如Makefile、Visual Studio Solution等),从而实现在不同平台上进行代码编译的目的…

Qwik 1.0 发布,全栈式 Web 框架

Qwik 是一个全栈式 Web 框架,Qwik 基于 React、Angular 和 Vue 等其他 Web 框架的概念,但以 JavaScript 流等更新的方法脱颖而出,允许以高性能向用户交付复杂的 Web 应用程序。 随着 Web 应用程序变得越来越大,它们的启动性能会下…

强烈推荐:一款中文AI问答、创作、绘画工具

前言 相信很多人已经听过ChatGPT这款人工智能机器人了,它能够根据用户输入的内容,自动生成智能回复。它使用自然语言处理技术,通过学习大量的文本资料,能够模拟人类的对话行为。它是由OpenAI开发的,一家非常伟大的人工…

Http知识

一、http协议 目前存在HTTP1.1(当前广泛运用的版本)、HTTP2.0和HTTP3.0协议,有以下的优点和缺点 1. HTTP1.1 优点:默认支持长连接,即在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的…

Flutter框架:从入门到实战,构建跨平台移动应用的全流程解析

第一章:Flutter框架介绍 Flutter框架是由Google推出的一款跨平台移动应用开发框架。相比其他跨平台框架,Flutter具有更高的性能和更好的用户体验。本章将介绍Flutter框架的概念、特点以及与其他跨平台框架的比较,以及Flutter开发环境的搭建和…

应急物流 | 灾后早期阶段多目标选址路径问题的混合元启发式算法

解读作者:李奡,闫同仁 A hybrid meta-heuristic algorithm for the multi-objective location-routing problem in the early post-disaster stage Tongren Yan, Fuqiang Lu, Suxin Wang, Leizhen Wang, Hualing Bi Journal of industrial and managem…

设计原则之【接口隔离原则】,我只做我能做的事

文章目录 一、什么是接口隔离原则二、实例三、总结接口隔离原则与单一职责原则的区别 一、什么是接口隔离原则 接口隔离原则(Interface Segregation Principle, ISP)是指用多个专门的接口,而不使用单一的总接口,客户端不应该依赖…

【中级软件设计师】—(下午题)试题三精讲总结(四十二)

【中级软件设计师】—(下午题)试题三精讲总结(四十二) 一、关系 二、UML中的图 A包含B,那么A执行操作前必须要先执行B 试题一(2021年下半年) 试题2(2021年上半年) 官方…

Docker中部署监控

Docker概念 一、部署Prometheus+grafana环境 1.1 、部署Prometheus+grafana环境 docker pull registry.cn-hangzhou.aliyuncs.com/lhrbest/lhrprometheus:1.0 docker tag registry.cn-hangzhou.aliyuncs.com/lhrbest/lhrprometheus:1.0 lhrbest/lhrprometheus:1.01.2 、创建镜…

linux 定时器

Linux 系统实现底半部的机制主要有tasklet,工作队列和软中断。 tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中断实现。 内核定时器也依靠软中断实现;内核中的延时是忙等待或者睡眠等待,为了充分利用CPU资源&#xff0c…

docker 搭建 Elasticsearch和Kibana 8.x版本

参考: docker入门:单机elasticsearch安装记录,保证无坑_8月日更_小鲍侃java_InfoQ写作社区 新建文件夹 同上文所述相同,需要在宿主机上挂载配置文件与数据文件。 mkdir -p /Users/louye/data/learn-data/elastic/config mkdir -p /Users/lo…