OpenGL 简介

news2024/12/28 5:12:27

OpenGL 简介

GPU 接口规范

对于刚接触 OpenGL 的初学者,常常会有这样一个疑问: OpenGL 的源码在哪里,如何编译?
然而实际上 OpenGL 并不是一个软件实现,更多的是一个标准协议; OpenGL 更像是一种显卡驱动标准,由各个硬件厂家适配,各个硬件厂商根据 OpenGL 接口规范编撰对应的驱动.

换句话说,对于各个硬件厂商 OpenGL 确实是一个基于 GPU 的软件实现,但是对于普通的应用层开发者 OpenGL 就是一个由硬件厂商提供的驱动程序罢了,也就是为什么你找不到 OpenGL 源代码实现的原因了.

OpenGL 是操作 GPU 的其中一种方法,但绝不是唯一的途径; OpenGLKhronos 进行管理,在 Khronos 的官网下,你可以找到下面这张图:

Khronos_Brief

在这里,你可能看到一些熟悉的名称,比如 EGLglTFOpenCLOpenGL 以及 Vulkan等等,这里定义了许多标准规范;这里我们仅仅谈及真正直接操作GPU的标准协议,
也就是下图中 3D Graphics 的部分:

Khronos_Classification

然后呢,这就是全部了吗? 实际并不是, Khronos 更多像是一个联盟,有着许多成员如 AppleIntelAMDGoogleARMQualcommNvidia 等等成员.

所以,也就会出现"分道扬镳"的现象,比如:

微软下的 Direct X (D3D9、D3D11、D3D12),对标 OpenGL:

Dirext_X

苹果下的 Metal, 还是对标 OpenGL:

Metal

以及英伟达大名鼎鼎的 CUDA, 对标 OpenCL:

CUDA

可以看到关于一个 GPU 接口规范的指定,出现了很多不同的角色比如硬件产商、操作系统提供者等等;为什么会出现这么多种规范呢?其中一部分原因来自于某一种标准实现并不适用于某些场景,但还有其他一部分原因就是跟为什么有这么多中编程语言类似的原因.

GPU 接口分类

GPU(Graphic Processing Unit)按我的理解可以分为两大类 GL(Graphic Language) 和 CL(Compute Language):

GPU_Classification

本文讨论的重点在 OpenGL, 故实际上处于 GPUGL 分支.

题外话

实际上很多人会发现 VULKANOpenGL 非常的相似,虽然VULKAN 一方面是为了解决 OpenGL 设计之初留下的问题,但 VULKAN 个人认为另一个积极的意义是在于给予行业一个新的标准化机会;例如 VULKAN 的协议制定苹果就挺上心的 (毕竟"分道扬镳"对于开发者而言意味着成倍的开发成本,对于厂商则很难形成一个开发者愿意投入的生态).

OpenGL 设计结构

如果用一句话来描述 OpenGL 的话,我想应该是基于C/S结构设计的模板模式(设计模式里的那个);在 图形渲染管线 这节,主要介绍的是其模板设计,在 OpenGL 里我们称之为 PipeLine;在 C/S结构 这节,则介绍 OpenGL C/S 结构给 OpenGL 带来的一些对于初学者看起来可能觉得奇奇怪怪的东西.

图形渲染管线

下图是出自于 es_spec_3.2.pdfDataflow Model:

OpenGL_Pipeline

这张图描述的东西挺多,但对于初学 OpenGL 只需要关心红色标注部分即可.其他部分例如 Framebuffer 的一部分 (Default Framebuffer) 在本章中由 EGL 负责; 而 Compute Shader 则是 OpenGL 提供的 CL 功能,目前讲述的是 GL 的部分,故省略.

上图就是 OpenGL 渲染管线整体的数据流图,仔细观察图中的左下方可以发现, OpenGL 按照颜色对不同的部分进行了区分,例如 Fixed Function Stage 以及 Programmable Stage 等等; 对于大部分的开发者而言需要关注 Fixed Function Stage 中的 Vertex Shader 以及 Fragement Shader,以及理解 Fixed Function Stage 中的 RasterizationPer-Fragement Operations 以及 Tessellation Primitive Gen 等步骤.某些 Fixed Function Stage 对于开发者而言属于不可控制的节点,但是只有充分理解其行为才能在其他可编程部分写出自己想要的程序.

题外话: 为什么要引入 Pipeline 的概念呢?

Each pipeline is controlled by a monolithic object created from a description of all of the shader

stages and any relevant fixed-function stages. Linking the whole pipeline together allows the

optimization of shaders based on their input/outputs and eliminates expensive draw time state

validation. (from vulkan spec)

C/S结构

谈起 OpenGL C/S 结构,就不得不提 OpenGL 对于开发者的一个强约束限制,就是: 只能在 OpenGL 所在的 context 线程里操作 OpenGL 的接口.

语言上可能很难说清楚这个 C/S 到底是什么结构,但是图例可能会相对清楚,例如使用 EGL 来创建 OpenGL context 时,在 eglInitialize 调用前后,可以看到这样有意思的现象:

EGL_BEFORE
EGL_AFTER

在前后两个接口调用前,程序的堆栈发生了明显的变化,这些多出来的线程主要就是用来辅助 OpenGL Server 运行的;实际上直到调用EGL接口eglMakeCurrent前,都是不能调用 OpenGL 接口的.其实也很好理解, Server 都没运行起来, Client 怎么能请求呢?

实际上,很多人说创建 OpenGL context 是为了给 OpenGL 提供一个渲染使用的画布;在我看来这种说法固然没有什么错误,不过我觉得更大一部分的贡献是在于运行起 OpenGL Server. 创建 OpenGL context 有很多种方式,例如使用 EGLSDLGLFWQT等等非常多种;为什么存在这种多种创建方式的主要原因在于: OpenGL 希望自己与硬件无关,而创建 OpenGL context 往往需要涉及到具体的硬件信息,比如这个 context 是运行在哪一个 Display 上 (OpenGL 标准里有针对于此详细的信息,有兴趣可以自己翻阅下).

在嵌入式上, EGLOpenGL ES 以及 GLSL ES 往往是一起使用的三剑客; 在这里实际上已经简单涉及到了 EGL 了,受限于篇幅本章就不再展开 EGL 相关的细节.

由于 OpenGL 只是一个规范而非实现,所以上面的现象在不同硬件平台,不同版本里面的现象可能是不同的;OpenGL 标准里也有明确写明,这要能保证接口调用的结果是符合预期的,OpenGL 不关注具体实现.

那这种 C/S 结构对于我们使用 OpenGL 有什么影响呢? 最大的影响就是在于我们只能在 OpenGL context 所在的线程调用 OpenGL 的接口,详细阅读 EGL 标准你会知道所有 OpenGL 接口都存在一个阴式的入参,就是 OpenGL Context; 这种接口设计广泛存在于 OpenGL 的各种接口,在深入学习 OpenGL 之后应该会更加有感触,这里由于篇幅也不再展开了.

渲染管线

在这节里,会对 图形渲染管线 小结中部分出现的环节进行描述,旨在理解一个完整 OpenGL 渲染管线的运行流程以及其逻辑.

背景补充

在真正描述渲染管线前,需要知道以下几个常识:

  • 两点确定一条直线
  • 三点确定一个三角形
  • n边形可以由 (n-2) 个三角形组成

OpenGL 中,所有图形均有点(point)、线(Line Segment)、三角形(Triangle)组成;换句话说对于 OpenGL 而言最基础的元素是,在 OpenGL 里称之为顶点(Vertex),对应的可编程节点则是 Vertex Shader.

而另外一个可编程节点则是 Fragment Shader;在非常早期的时候 Fragment Shader 其实是叫做 Pixel Shader; OpenGLPixel 替换为了 Fragment 的一个原因是在于 Pixel 强调的是一个点的概念而 Fragment 则是代表一个面的概念;在实际屏幕显示时,实际上对应的是一块块像素块,而像素块并不一定是正方形,所以后来修改为 Fragment 强调面的概念.对于初学者而言,可能 Pixel Shader 会更加容易理解.

GLSL

OpenGL 整个渲染管线中存在可编程节点, OpenGL 将它们统称为 Shader. 值得注意的是,并不是所有可编程节点都是需要的;在大多数场景下,我们只需要处理 Vertex ShaderFragment Shader 就足够了.

为了实现可编程的这个目标, OpenGL 需要搭配一门 GPU 编程语言使用,即 GLSL(OpenGL Shading Language); 实际上对于大部分 GPU 开发者而言,最终的主要任务就是编写和优化 GLSL, 高效的 GLSL 能够节约 GPU 的使用资源以及缩短 GPU 的处理时间.

在本章中由于篇幅也不打算展开 GLSL 的细节描述,在之后以案例的感性形式给出一个 GLSL 的使用.这里给出一个示例的 GLSL 写法,对应亮度擦除(转场)的实现:

# vertex
attribute vec3 Position;
attribute vec2 iUv;
varying   vec2 oUV;
uniform mat4 WorldViewProj;

void main(void)
{
    oUV         =  iUv;
    gl_Position =  WorldViewProj * vec4(Position, 1.0);
}
# fragment
varying vec2 oUV;
uniform sampler2D TexA;
uniform sampler2D TexB;
uniform sampler2D TexL;
uniform float Progress;

void main(void)
{
    float softness = 0.03f;
    float luma = texture(TexL, oUV).x;
    float time = mix(0.0f, 1.0f + softness, Progress);

    vec4 acolor = texture(TexA, oUV);
    vec4 bcolor = texture(TexB, oUV);

    if (luma <= time - softness)
    {
        gl_FragColor = bcolor;
    }
    else if (luma >= time)
    {
        gl_FragColor = acolor;
    }
    else
    {
        float alpha = (time - luma) / softness;
        gl_FragColor = mix(acolor, bcolor, alpha);
    }
}

GLSL 中,变量简单地将可以分为 attributevaryinguniform 三种. 由于历史的原因 attribute 也称之为 in,而 varying 则称之为 output.可以在 GLSL 标准中找到下列表述:

Not all language constructs present in earlier versions of the language are available in later

versions e.g. attribute and varying qualifiers are present in v1.00 but not v3.00. However, the

functionality of GLSL ES 3.20 is a super-set of GLSL ES 3.10.

可以看到目前 GLSL 更推崇使用 in 代替废弃的 attribute,而使用 out 去代替废弃的 varying;但是个人认为它们还是尤其意义的,尤其是对于初学者而言,例如 OpenGL 可只有 glBindAttribLocation 接口,可没有 glBindInputLocation;为了做一下兼容处理,一般会在 GLSL 程序前写入一段宏定义:

#if __VERSION__ >= 130
    #define attribute in
    #define varying out
#endif

如果我们将 Shader 理解为一个函数,那么就存在入参和出参,分别对应于 attributevarying;这里可以抛出一个问题, attribute 入参是如何传递进去的? 要回答这个问题,需要回到 OpenGL Pipeline 的数据流图:

OpenGL_Pipeline

在整个 PipeLine 运行之初存在一个 Vertex Puller 的环节,Vetex Shader 中的 attribute 就是从这里传递进去的;那么 Fragment Shaderattribute呢? 仔细观察上面 vertexfragment 的代码以及结合 varying 这个单词的含义,就能找到答案.注意对你而言,只有 Vetex Shader 的入参是你真正自己传递进去的.

GLSL 中除了 attributevarying,还存在一个广泛使用的类型 uniform, 一般我们可以将其理解为常量即可; uniform 可以分为两大类,详细可看下 OpenGL ES 标准中的 7.6 Uniform Variables:

  • Named uniform blocks
  • Default uniform block

其中特别指出 Uniforms in the default uniform block are program object-specific state., 其实就是再说有些 uniform 类似于一个句柄,我们只能通过 GLSL 内置接口去操作,例如上面出现的 sampler2D 去操作;而另外的 uniform 而是开发者传递进去的.

其实这里我个人觉得将 uniform 描述为常量在初期是会造成一些理解上的偏差的. 例如 uniform 都是一个常量了,为什么我不能直接将其内嵌入代码即可? 这实际是在于 uniform 只是在一次 Pipeline 过程中才是常量;例如在上面的例子中,存在 uniform 变量 Progress, 实际程序运行的过程中它以 60hz 的速率,从 0.0f 到 1.0f 以 1.0f/60 步进的速率发生着改变,不然图像怎么会出现变化呢?

然后不同 Shader 存在着不同的内置变量,例如 Vertex 存在内置变量 gl_Position 对应着一个顶点的坐标, 而 Fragment 则存在内置变量 gl_FragColor 对应着一个像素点信息.

OpenGL 的坐标分为 w、y、z、w, 即非笛卡尔坐标,当 w 为 1 则可以理解为笛卡尔坐标系; 此外,坐标的取值分为为 [-1.0f, 1.0f], (0.0, 0.0) 代表中心坐标,也是为什么你会经常看到一个类似于 WorldViewProjuniform 变量, 世界坐标系投影.

这里又引出一个很有趣的问题,假设我们需要绘制一张 1080P 的图片; 那么需要两个三角形,对应6个坐标顶点, gl_Position 需要被赋值 6 次; 而 gl_FragColor 则需要被赋值 1080*1920 次. 这里可以停下来思考一个问题, vertex 中的 oUVfragment 中的 oUV 数量是一致的吗? uniform 变量 Progress 到底存在几个?

回答得了这个问题,就打开了 GPU GL 并行处理的大门; 这个问题将会在 Rasterization 栅格化中解释.

栅格化

对于开发者而言,传入的只是几个顶点坐标,而实际上渲染时肯定针对的是一块像素区域;从 VertexFragment 的过程实际上大体就是 Rasterization 栅格化.

在讲解 Rasterization 之前,给出一个案例的描述,可以思考一下为什么出来一张长这样子的图:

# vertex
attribute vec3 Position;
attribute vec2 iUv;
varying   vec2 oUV;
uniform mat4 WorldViewProj;

void main(void)
{
    oUV         =  iUv;
    gl_Position =  WorldViewProj * vec4(Position, 1.0);
}
# fragment
varying vec2 oUV;
uniform sampler2D TexA;
uniform sampler2D TexB;

void main(void)
{
    float val = (oUV.x + oUV.y) / 2;
    gl_FragColor = vec4(val, val, val, val);
}

并且在 Vertex Puller 中输入以下数据:

// Position(3 scalar)        iUv(2 scalar)
   // one rectangle
   0.0      0.0      0.0      0.0  0.0
   1920.0   0.0      0.0      1.0  0.0
   1920.0   1080.0   0.0      1.0  1.0
   // another rectangle
   0.0      0.0      0.0      0.0  0.0
   1920.0   1080.0   0.0      1.0  1.0
   0.0      1080.0   0.0      0.0  1.0

图例如下:

DrawRect

实际你在屏幕输出可以看到下面这样的效果:

RasterizationDemo

OpenGL 的基础元素有点(Point)、线(Line Segment)以及三角形(Triangle),其栅格化过程各有不同,在这里选取三角形(Triangle)进行表述(实际上大多数场景也只会用到Triangle).

此节所有描述均来自于 OpenGL ES 中的 13.7 Polygons.

对于上面演示的这个例子如果以 1080P 作为屏幕尺寸,那么右上三角形输入三个顶点(vertex)则应该输出 1080*1920/2 个像素点(fragment); 这个过程我们称之为 Rasterization 栅格化,对应于 OpenGL ES 标准中的 Fixed-Function Primitive Assembly and Rasterization.

栅格化的本质是数据内插(interpolation),即如何使用三个顶点(vertex)内插出1080*1920/2 个像素点(fragment)?这里就要谈及内插公式,这里不想讲得过于复杂就附带上 OpenGL ES 中官方的描述,并做扼要的说明:

RasterizationPolygons1
RasterizationPolygons2

  • p : barycentric coordinates, 三角形中任意一点
  • abc : p 与三角形三个顶点相连接,可以分割出三个子三角形; abc就是子三角形和三角形的比例关系,故 a+b+c=1
  • associated datunm : 相关数据,比如内置变量 gl_Position,自定义输出变量 varying oUV
  • w : clip coordinate; 被剪辑过的坐标,这里简单可以理解为没有剪辑,传入是什么就是什么

结合下面这张图以及输入的值,找几个坐标好好算一算,就可以理解这个内插公式了:
RasterizationDemo

案例演示

素材源

TexA

TexA

TexB

TexB

TexL

TexL

Vertex

// Position(3 scalar)        iUv(2 scalar)
   // one rectangle
   0.0      0.0      0.0      0.0  0.0
   1920.0   0.0      0.0      1.0  0.0
   1920.0   1080.0   0.0      1.0  1.0
   // another rectangle
   0.0      0.0      0.0      0.0  0.0
   1920.0   1080.0   0.0      1.0  1.0
   0.0      1080.0   0.0      0.0  1.0

渐变转场

FadeVal 从 0.0 至 1.0 均匀变化.

Fade vertex shader

attribute vec3 Position;
attribute vec2 iUv;
varying   vec2 oUV;
uniform mat4 WorldViewProj;

void main(void)
{
    oUV         =  iUv;
    gl_Position =  WorldViewProj * vec4(Position, 1.0);
}

Fade fragemet shader

varying vec2 oUV;
uniform sampler2D TexA;
uniform sampler2D TexB;
uniform float FadeVal;

void main(void)
{
    gl_FragColor = mix(texture(TexA, oUV), texture(TexB, oUV), FadeVal);
}

渐变转场效果图

TransitionFade

亮度擦除转场

演示百叶窗效果,本质原理为查表;Progress 从 0.0 至 1.0 均匀变化.

Luma vertex shader

attribute vec3 Position;
attribute vec2 iUv;
varying   vec2 oUV;
uniform mat4 WorldViewProj;

void main(void)
{
    oUV         =  iUv;
    gl_Position =  WorldViewProj * vec4(Position, 1.0);
}

Luma fragment shader

varying vec2 oUV;
uniform sampler2D TexA;
uniform sampler2D TexB;
uniform sampler2D TexL;
uniform float Progress;

void main(void)
{
    float softness = 0.03f;
    float luma = texture(TexL, oUV).x;
    float time = mix(0.0f, 1.0f + softness, Progress);

    vec4 acolor = texture(TexA, oUV);
    vec4 bcolor = texture(TexB, oUV);

    if (luma <= time - softness)
    {
        gl_FragColor = bcolor;
    }
    else if (luma >= time)
    {
        gl_FragColor = acolor;
    }
    else
    {
        float alpha = (time - luma) / softness;
        gl_FragColor = mix(acolor, bcolor, alpha);
    }
}

亮度擦除转场效果图

TransitionLuma

结语

在写这篇文章之前我想了很久,最终我决定不去描述 OpenGL 的各个细节,比如 OpenGL 的接口定义、如何调用、如何从 CPU 向 GPU 传输数据等等; 想以概括性的方式描述一下 OpenGL 即可;如果想深入了解 OpenGL ,可以读读 ES 2.0知识串讲,个人非常喜欢(所以本章的写法侧重点也与此不同,不想重复).

此外,本章所描述的大部分实现你都可以从 MMP 这个仓库中找到,详细可以阅读下 调试.

MMP 是业余时我自己开发的一个SDK,目前主要参考了几个开源程序,比如 ppsspp (很有意思的开源模拟器)以及 obs; 个人觉得都是相当优秀的开源项目,值得学习以及投入.(顺便帮我点下 star,让我有更新的动力)

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

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

相关文章

【系统集成项目管理工程师】项目进度管理

&#x1f4a5;十大知识领域&#xff1a;项目进度管理 主要考计算题 项目进度管理包括以下 7 个过程: 规划进度管理过程定义活动过程排列活动顺序过程估算活动资源过程估算活动持续时间过程制定进度计划过程控制进度过程 一、规划进度管理过程 制定政策、程序和文档以管理项目进…

亲测:腾讯云轻量应用服务器性能如何?

腾讯云轻量应用服务器性能评测&#xff0c;轻量服务器CPU主频、处理器型号、公网带宽、月流量、Ping值测速、磁盘IO读写及使用限制&#xff0c;轻量应用服务器CPU内存性能和标准型云服务器CVM处于同一水准&#xff0c;所以大家不要担心轻量应用服务器的性能&#xff0c;腾讯云百…

【CSS】13.页面切图和布局实现

页面切图和布局实现 1. 浮动布局 1.1 页面布局 LOGO 部分 NAV 布局 LEFT - SIDEBAR&#xff1a;左边栏布局 CONTENT&#xff1a;内容布局 RIGHT - SIDEBAR&#xff1a;右边栏布局 1.2 流式布局 块的默认布局叫做流式布局 但流式布局并不能满足对页面的需要&#xff0c…

(学习日记)2023.4.10

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

杰林码图像增强算法——超分辨率、图像放大、轮廓和色彩强化算法(二)

一、前言 2023-03-23我发布了基于加权概率模型&#xff08;杰林码的理论模型&#xff09;的图像颜色增强和轮廓预测的应用方法。效果还不太明显&#xff0c;于是我又花了2周的时间进行了技术优化。下面仅提供了x86下的BMP和JPG对应的lib和dll&#xff0c;本文中的算法属于我国…

为什么CPU需要时钟

为什么CPU需要时钟 为什么CPU需要时钟这样一个概念? 什么是时钟脉冲&#xff0c;CPU为什么需要时钟&#xff0c;时钟信号是怎么产生的&#xff1f; 上面这个图的方波就是一个脉冲&#xff0c;类比于人类的脉搏跳动。一个脉冲称之为CPU的一个时钟信号&#xff0c;或者时钟脉冲…

Melis4.0[D1s]:6.mango-MQ-R基于Melis移植lvgl

文章目录1.下载lvgl源码到《D1s-Melis/ekernel/drivers/hal/test/disp2》目录1.1 修改Makefile1.2 快速测试Makefile修改是否有效2.移植刷新显示内存函数dummy_flush_cb3.创建一个定时器调用lvgl心跳lv_tick_inc()4.pack打包出错&#xff1a;5.设置开机启动6.源码下载上一篇文章…

《类和对象》(上)

本文主要对类和对象有一个初步的了解。 文章目录前言1、类的引入和定义2、类的访问限定符及封装2.1 访问限定符2.2 封装3 、类的作用域4 、类的实例化5 、类对象的模型5.1 类对象的大小5.2 类对象存储方式6、this 指针6.1 引子6.2 特性6.3 this指针的一个问题前言 C语言是面向过…

【微信小程序】-- 分包 - 独立分包 分包预下载(四十五)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

VRIK+Unity XR Interaction Toolkit 实现VR上半身的追踪(附带VRM模型导入Unity方法和手腕扭曲的解决方法)

文章目录&#x1f4d5;第一步&#xff1a;配置 OpenXR XR Interaction Toolkit 的开发环境&#x1f4d5;第二步&#xff1a;导入人物模型⭐VRM 模型导入 Unity 的方法&#x1f4d5;第三步&#xff1a;配置 VRIK⭐给模型加上 VRIK 组件⭐将模型的头部和手部的位置作为 VR 追踪目…

如何用ChatGPT做设计?激发设计师们的灵感

伴随着人工智能技术的迅猛发展&#xff0c;AI 工具在设计领域中的应用也越来越广泛。 当前&#xff0c;诸如ChatGPT等 AI 工具不仅可以进行自然语言处理&#xff0c;还可以应用于图像、视频等多种媒体领域&#xff0c;为设计师们提供了丰富的应用场景。 使用Chatgpt&#xff1…

开源了,我做了一个基于GPT的桌宠聊天系统:Pet-GPT!

前言 最近chatgpt的热度高居不下。作为一个深度成谜者&#xff0c;发现大部分开发者在调用GPT的时候要不就是基于Tauri做本地窗口外接网页&#xff0c;要不就是web直接展示。在沉思苦想一段时间后&#xff0c;才发现好像没啥什么人用pyqt做啊&#xff1f;特别是没人用桌面宠物…

浏览器:好用的浏览器插件,亲测好用

插件一、CSDN&#xff0c;推荐指数五颗星&#xff0c;强烈推荐 说明&#xff1a;超级好用的浏览器桌面&#xff0c;功能强大&#xff0c;操作简单&#xff0c;效率神器 插件二、AIX智能下载器 说明&#xff1a; 就这么简单的拖放即可实现保存图片&#xff0c;即使观看中的视频…

凌恩生物美文分享|好看又实用,多组学联合分析项目大揭秘!

基因层面的功能潜能有没有真的实现表达&#xff0c;表达量是高是低&#xff1f;下游合成的蛋白质行使了什么样得功能&#xff0c;代谢产物通过体循环到达靶器官&#xff0c;又是如何影响靶器官的工作运行的&#xff1f; 一个完整的生物学故事&#xff0c;中心法则贯穿始终&…

2023十大连锁店进销存软件排名(真实测评)

目前市面上很多连锁店进销存软件的排名都比较主观&#xff0c;结合自己实际工作过程中接触过的软件和在进销存软件多年的经验&#xff0c;给大家整理下2023十大连锁店进销存软件&#xff0c;希望能帮到正在选择连锁店进销存软件的老板&#xff01; 第一名&#xff1a;秦丝连锁联…

机器学习 | 实验三:逻辑回归和牛顿法

⭐ 对应笔记&#xff1a;牛顿法 &#x1f4da;描述 在本练习中&#xff0c;我们将使用牛顿法对分类问题实现逻辑回归。 &#x1f4da;数据 在本练习中&#xff0c;假设一所高中有一个数据集&#xff0c;该数据集表示40名被大学录取的学生和40名未被录取的学生。 每个(x(i),y…

pandas笔记:offset.DateOffset

进行date的偏移 class pandas.tseries.offsets.DateOffset 1 参数说明 n 偏移量表示的时间段数。 如果没有指定时间模式&#xff0c;则默认为n天。 normalize是否将DateOffset偏移的结果向下舍入到前一天午夜**kwds 添加到偏移量的时间参数 年&#xff08;years&#xff09…

软考软件设计师 数据库知识点笔记

概念数据模型 了解即可 结构数据模型 数据库的三级模式结构 外模式对应视图 概念模式对应的是数据库管理系统里面的基本表 内模式对应的是数据库里的一些存储文件 上图可直接背下面概念 两级映像 有内模式跟物理独立性相关&#xff0c;有外模式跟逻辑独立性相关 两级映像其…

MySQL主从复制原理剖析与应用实践

vivo 互联网服务器团队- Shang Yongxing MySQL Replication&#xff08;主从复制&#xff09;是指数据变化可以从一个MySQL Server被复制到另一个或多个MySQL Server上&#xff0c;通过复制的功能&#xff0c;可以在单点服务的基础上扩充数据库的高可用性、可扩展性等。 一、背…

人工智能AI简史

AI人工智能简史 最近学习AI&#xff0c;顺便整理了一份AI人工智能简史&#xff0c;大家参考&#xff1a; 1951年 第一台神经网络机&#xff0c;称为SNARC&#xff1b;1956年 达特茅斯学院会议&#xff0c;正式确立了人工智能的研究领域&#xff1b;1966年 MIT发明ELIZA人机心…