LearnOpenGL-高级OpenGL-8.高级GLSL

news2024/11/8 22:32:50

本人初学者,文中定有代码、术语等错误,欢迎指正

文章目录

  • 高级GLSL
  • GLSL的内建变量
    • 在顶点着色器的内建变量
      • gl_PointSize
      • gl_VertexID
    • 在片段着色器的内建变量
      • gl_FragCoord
      • gl_FrontFacing
      • gl_FragDepth
  • 接口块
  • Uniform缓冲对象
    • Uniform块布局
    • 使用Uniform缓冲
      • 简介
      • 绑定点
      • 例子
    • Uniform缓冲对象比Uniform的优点

高级GLSL

我们将会讨论一些有趣的内建变量(Built-in Variable),管理着色器输入和输出的新方式以及一个叫做Uniform缓冲对象(Uniform Buffer Object)的有用工具。

GLSL的内建变量

  • 啥是内建变量

    在编写GLSL代码时,可以使用已经声明好了的变量就称为:内建变量,可供我们直接赋值使用。

  • 例子

    之前章节中遇到的:顶点着色器的输出向量gl_Position,和片段着色器的gl_FragCoord

在顶点着色器的内建变量

gl_PointSize

  • 简介

    • 每一个顶点都是一个图元,都会被渲染为一个点。

    • 可以通过OpenGL的glPointSize函数来设置渲染出来的点的大小,但我们也可以在顶点着色器中修改这个值。

    • GLSL定义了一个叫做gl_PointSize输出变量,它是一个float变量,你可以使用它来设置点的宽高(像素)。在顶点着色器中修改点的大小的话,你就能对每个顶点设置不同的值了。

  • 如何启用

    默认是禁用的,启用OpenGL的GL_PROGRAM_POINT_SIZE:

    glEnable(GL_PROGRAM_POINT_SIZE);
    
  • 使用例子

    将点的大小设置为裁剪空间位置的z值,也就是顶点距观察者的距离。的大小会随着观察者距顶点距离变远而增大

    void main()
    {
        gl_Position = projection * view * model * vec4(aPos, 1.0);    
        gl_PointSize = gl_Position.z;    
    }
    

    对每个顶点使用不同的点大小,会在粒子生成之类的技术中很有意思

gl_VertexID

  • 简介

    • 整型变量gl_VertexID储存了正在绘制顶点的当前ID

    • 只能对它进行读取

    • 当(使用glDrawElements)进行索引渲染的时候,这个变量会存储正在绘制顶点的当前索引

    • 当(使用glDrawArrays)不使用索引进行绘制的时候,这个变量会储存从渲染调用开始的已处理顶点数量

在片段着色器的内建变量

gl_FragCoord

  • 简介

    • gl_FragCoord是输入变量,能让我们读取当前片段的窗口空间坐标,并获取它的深度值

    • gl_FragCoord的z分量等于对应片段的深度值

    • gl_FragCoord的x和y分量是片段的窗口空间(Window-space)坐标,其原点为窗口的下角。

  • 例子

    通过利用片段着色器的gl_FragCoord,我们可以根据片段的窗口坐标,计算出不同的颜色。

    我们能够将屏幕分成两部分,在窗口的左侧渲染一种输出,在窗口的右侧渲染另一种输出。

    void main()
    {             
        if(gl_FragCoord.x < 400)
            FragColor = vec4(1.0, 0.0, 0.0, 1.0);// 红色
        else
            FragColor = vec4(0.0, 1.0, 0.0, 1.0);// 绿色  
    }
    

gl_FrontFacing

  • 简介

    • gl_FrontFacing是输入变量

    • 前置知识

      OpenGL能够根据顶点的环绕顺序来决定一个面是正向还是背向面

    • gl_FrontFacing作用

      如果我们不使用面剔除(不启用GL_FACE_CULL),那么gl_FrontFacing将会告诉我们当前片段是属于正向面的一部分还是背向面一部分

  • 例子

    我们可以这样子创建一个立方体,在内部和外部使用不同的纹理

    #version 330 core
    out vec4 FragColor;
    
    in vec2 TexCoords;
    
    uniform sampler2D frontTexture;
    uniform sampler2D backTexture;
    
    void main()
    {             
        if(gl_FrontFacing)
            FragColor = texture(frontTexture, TexCoords);// 正向
        else
            FragColor = texture(backTexture, TexCoords);// 背向
    }
    

    注意,如果你开启了面剔除,你就看不到箱子内部的面了,所以现在再使用gl_FrontFacing就没有意义了。

gl_FragDepth

  • 简介

    • 前面介绍的:gl_FragCoord

      是输入变量,能让我们读取当前片段的窗口空间坐标,并获取它的深度值,但是它是一个只读(Read-only)变量。

      我们不能修改片段的窗口空间坐标,所以修改片段的深度值需要用到目前介绍的gl_FragDepth。

    • gl_FragDepth

      是输出变量,我们可以使用它来在着色器内设置片段的深度值

  • 例子

    gl_FragDepth = 0.0; // 这个片段现在的深度值为 0.0
    

    如果片段着色器没有写入值到gl_FragDepth,它会自动取用gl_FragCoord.z的值。

  • 缺陷

    • 只要我们在片段着色器中对gl_FragDepth进行写入,OpenGL就会(像深度测试小节中讨论的那样)禁用所有的提前深度测试(Early Depth Testing)。

      提前深度测试是:

      • 硬件属性
      • 提前深度测试允许深度测试在片段着色器之前运行
      • 即:在运行片段着色器时会根据深度测试, 是舍弃当前片段还是运行片段着色器程序渲染这个片段
    • 它被禁用的原因是

      OpenGL无法在片段着色器运行之前得知片段将拥有的深度值,因为片段着色器可能会完全修改这个深度值。

  • 解决缺陷

    从OpenGL 4.2起,我们仍可以对两者(写入gl_FragDepth 与 提前深度测试)进行一定的调和。

    方法:在片段着色器的顶部使用深度条件(Depth Condition)重新声明gl_FragDepth变量:

    layout (depth_<condition>) out float gl_FragDepth;
    

    condition可以为下面的值:

    条件描述
    any默认值。提前深度测试是禁用的,你会损失很多性能
    greater你只能让深度值比gl_FragCoord.z更大
    less你只能让深度值比gl_FragCoord.z更小
    unchanged如果你要写入gl_FragDepth,你将只能写入gl_FragCoord.z的值

    通过将深度条件设置为greater或者less,OpenGL就能假设你只会写入比当前片段深度值更大或者更小的值了。这样子的话,当深度值比片段的深度值要小的时候,OpenGL仍是能够进行提前深度测试的。

    • 个人理解(可能有误)

      • 声明写入gl_FragDepth的值只能更大

        写入gl_FragDepth后

        当前渲染的片段深度值铁定大于深度缓冲中的深度值,不管放到多大,当前片段一定被丢弃,依旧进行提前深度测试

      • 声明写入gl_FragDepth的值只能更小

        写入gl_FragDepth后:

        当前渲染的片段深度值铁定小于深度缓冲中的深度值,不管放到多小,当前片段一定不会被丢弃,依旧进行提前深度测试

  • 解决缺陷例子

    #version 420 core // 注意GLSL的版本!
    out vec4 FragColor;
    layout (depth_greater) out float gl_FragDepth;// 只会更大
    
    void main()
    {             
        FragColor = vec4(1.0);
        // 当前渲染的片段深度值铁定**大于**深度缓冲中的深度值,所以能提前深度测试
        gl_FragDepth = gl_FragCoord.z + 0.1;
    }  
    

接口块

  • 简介

    作用:方便我们组合顶点着色器传入到片段着色器的这些输入/输出变量。(顶点位置、法线等顶点属性)

  • 例子说明什么是接口块

    • 输出

      #version 330 core
      layout (location = 0) in vec3 aPos;
      layout (location = 1) in vec2 aTexCoords;
      
      uniform mat4 model;
      uniform mat4 view;
      uniform mat4 projection;
      // 注意这里,out说明是输出块
      out VS_OUT// VS_OUT(大写)是块名
      {
          vec2 TexCoords;
      } vs_out;// vs_out(小写)是实例名
      
      void main()
      {
          gl_Position = projection * view * model * vec4(aPos, 1.0);    
          vs_out.TexCoords = aTexCoords;
      }  
      
    • 输入

      #version 330 core
      out vec4 FragColor;
      // 注意这里,in说明是输入块
      in VS_OUT// VS_OUT(大写)是块名
      {
          vec2 TexCoords;
      } fs_in;// fs_in(小写)是实例名
      
      uniform sampler2D texture;
      
      void main()
      {             
          FragColor = texture(texture, fs_in.TexCoords);   
      }
      
      • 块名:VS_OUT

        顶点着色器与片段着色器的块名一致

      • 实例名:

        顶点着色器:vs_out

        片段着色器:fs_in

      只要两个接口块的名字一样,它们对应的输入和输出将会匹配起来。

Uniform缓冲对象

  • 之前使用Uniform缺陷

    当使用多于一个的着色器时,尽管大部分的uniform变量都是相同的,我们还是需要不断地设置它们。

    • 具体说明

      比如一个场景有正方体、原体,他们两个用了个着色器分别渲染,这两个着色器都有一个uniform mat4 project属性,代表都需要一个摄像机的投影矩阵,这样渲染前两个着色器分别需要设置上传一次这个project uniform,共次。

  • 使用Uniform缓冲对象

    允许我们定义一系列在多个着色器中相同的全局Uniform变量。

    当使用Uniform缓冲对象的时候,我们只需要设置相关的uniform一次

    tips:可以类别为编程语言中类中的普通变量static变量

  • 例子

    glsl

    #version 330 core
    layout (location = 0) in vec3 aPos;
    
    // Uniform缓冲对象
    layout (std140) uniform Matrices
    {
        mat4 projection;
        mat4 view;
    };
    
    uniform mat4 model;
    
    void main()
    {
        gl_Position = projection * view * model * vec4(aPos, 1.0);
    }
    

Uniform块布局

  • 引出:什么是Uniform块布局

    • Uniform块的内容是储存在一个缓冲对象中的,它实际上只是一块预留内存

      如上一节的

      layout (std140) uniform Matrices
      {
          mat4 projection;
          mat4 view;
      };
      

      这是一个Uniform块声明,但是不具有内容

      • mat4 projection;

        预留了一个4x4float数组大小的内存

      • mat4 view;

        预留了一个4x4float数组大小的内存

    • 需要指定内容

      因为这块内存并不会保存它具体保存的是什么类型的数据,我们还需要告诉OpenGL在内存的哪一部分对应着着色器中的哪一个uniform变量(即哪块内存数据是给projection、哪块内存数据是给view)。

      如何告诉,这就是Uniform块布局

      (可以类比之前:glVertexAttribPointer,来指定内存数组的顶点输入数据的哪一个部分对应顶点着色器哪一个顶点属性

  • 假设着色器中有以下的这个Uniform块:

    layout (std140) uniform ExampleBlock
    {
        float value;
        vec3  vector;
        mat4  matrix;
        float values[3];
        bool  boolean;
        int   integer;
    };
    

    我们需要知道的是每个变量的大小(字节)和(从块起始位置的)偏移量,来让我们能够按顺序将它们放进缓冲中。

    • 每个元素的大小都是在OpenGL中有清楚地声明的,而且直接对应C++数据类型,其中向量和矩阵都是float数组。
    • OpenGL没有声明的是这些变量间的间距(Spacing)
  • 前置了解:硬件自动偏移量与共享布局

    • 硬件自动定义了偏移量,GLSL会使用一个叫做共享(Shared)布局的Uniform内存布局

    • 使用共享布局时,GLSL是可以为了优化而对uniform变量的位置进行变动的,只要变量的相对顺序保持不变。

    • 能够使用像是glGetUniformIndices这样的函数来查询每个uniform变量的偏移量,从而计算获取这个uniform的位置进行上传数据。

      小结:glsl会改变uniform的位置,则需要使用glGetUniformIndices函数查询uniform的偏移量,这会产生非常多的工作量。

  • std140布局

    克服:硬件自动偏移量与共享布局的缺陷

    • 简介

      • std140布局声明了每个变量的偏移量都是由一系列规则所决定的,这显式地声明了每个变量类型的内存布局。由于这是显式提及的,我们可以手动计算出每个变量的偏移量。
      • 基准对齐量,它等于一个变量在Uniform块中所占据的空间(包括填充量(Padding)),这个基准对齐量是使用std140布局的规则计算出来的。(类型的大小,float:4、)
      • 对齐偏移量,它是一个变量从块起始位置的字节偏移量。
      • 一个变量的对齐字节偏移量必须等于基准对齐量的倍数。
    • 布局规则

      GLSL中的每个变量,比如说int、float和bool,都被定义为4字节量。每4个字节将会用一个N来表示。

      类型布局规则
      标量,比如int和bool每个标量的基准对齐量为N。
      向量2N或者4N。这意味着vec3的基准对齐量为4N。
      标量或向量的数组每个元素的基准对齐量与vec4的相同。
      矩阵储存为列向量的数组,每个向量的基准对齐量与vec4的相同。
      结构体等于所有元素根据规则计算后的大小,但会填充到vec4大小的倍数。
    • 例子

      layout (std140) uniform ExampleBlock
      {
                           // 基准对齐量       // 对齐偏移量
          float value;     // 4               // 0 
          vec3 vector;     // 16              // 16  (必须是16的倍数,所以 4->16)
          mat4 matrix;     // 16              // 32  (列 0)
                           // 16              // 48  (列 1)
                           // 16              // 64  (列 2)
                           // 16              // 80  (列 3)
          float values[3]; // 16              // 96  (values[0])
                           // 16              // 112 (values[1])
                           // 16              // 128 (values[2])
          bool boolean;    // 4               // 144
          int integer;     // 4               // 148
      }; 
      
      • 如vec3 vector;

        由于:一个变量的对齐字节偏移量必须等于基准对齐量的倍数

        本来:它的对齐偏移量是4的,但是它的基准对齐量是16,所以4需要向上增长到16为基准对齐量(16)的一倍

      通过在Uniform块定义之前添加layout (std140)语句,我们告诉OpenGL这个Uniform块使用的是std140布局。

使用Uniform缓冲

简介

我们已经讨论了如何在着色器中定义Uniform块,并设定它们的内存布局了,但我们还没有讨论该如何使用它们。

绑定点

在OpenGL上下文中,定义了一些绑定点(Binding Point),我们可以将一个Uniform缓冲(图中的右边)链接至它。

在创建Uniform缓冲之后,我们将它绑定到其中一个绑定点上,并将着色器中的Uniform块(图中的左边)绑定到相同的绑定点,把它们连接到一起。

  • Uniform块绑定到一个特定的绑定点中

    // 将shaderA中的Lights Uniform块的索引点链接为绑定点的2号索引上
    unsigned int lights_index = glGetUniformBlockIndex(shaderA.ID, "Lights");   
    glUniformBlockBinding(shaderA.ID, lights_index, 2);
    
    • glGetUniformBlockIndex

      用来获取Uniform块索引(Uniform Block Index),是着色器中已定义Uniform块的位置值索引

      接受一个着色器程序对象Uniform块的名称

    • glGetUniformBlockIndex

      • 第一个参数是一个着色器程序对象

      • 第二个参数是一个Uniform块索引和链接到的绑定点

    /*从OpenGL 4.2版本起,你也可以添加一个布局标识符,显式地将Uniform块的绑定点储存在着色器中,这样就不用再调用glGetUniformBlockIndex和glUniformBlockBinding了。下面的代码显式地设置了Lights Uniform块的绑定点。*/
    layout(std140, binding = 2) uniform Lights { ... };
    
  • 绑定Uniform缓冲对象到相同的绑定点上

    // 将uboExampleBlock缓冲链接为绑定点的2号索引上
    glBindBufferBase(GL_UNIFORM_BUFFER, 2, uboExampleBlock); 
    // 或
    glBindBufferRange(GL_UNIFORM_BUFFER, 2, uboExampleBlock, 0, 152);
    
    • glBindbufferBase

      一个绑定点索引和一个Uniform缓冲对象作为它的参数

    • glBindBufferRange

      • 除了绑定点索引与Uniform缓冲对象,还需要一个附加的偏移量和大小参数
      • 这样子你可以绑定Uniform缓冲的特定一部分到绑定点中。
      • 可以让多个不同的Uniform块绑定到同一个Uniform缓冲对象上
  • 向Uniform缓冲中添加数据

    glBufferSubData函数,用一个字节数组添加所有的数据,或者更新缓冲的一部分。

    glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
    int b = true; // GLSL中的bool是4字节的,所以我们将它存为一个integer
    // 将缓冲的144字节开始的4个字节填充为b 
    glBufferSubData(GL_UNIFORM_BUFFER, 144, 4, &b);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    

    对应的Uniform块,缓冲的144是boolean的对齐偏移量,4是boolean的基准对齐量

    layout (std140) uniform ExampleBlock
    {
                         // 基准对齐量       // 对齐偏移量
    	.....
        float values[3]; // 16              // 96  (values[0])
                         // 16              // 112 (values[1])
                         // 16              // 128 (values[2])
        bool boolean;    // 4               // 144
        .....
    }; 
    

例子

  • glsl

    #version 330 core
    layout (location = 0) in vec3 aPos;
    
    layout (std140) uniform Matrices
    {
        mat4 projection;
        mat4 view;
    };
    uniform mat4 model;
    
    void main()
    {
        gl_Position = projection * view * model * vec4(aPos, 1.0);
    }
    
  • 首先,我们将四个顶点着色器的Uniform块设置为绑定点0

    这4个着色器程序对象都使用这个顶点着色器,都各自具有一个uniform块,都链接到绑定点0号

    unsigned int uniformBlockIndexRed    = glGetUniformBlockIndex(shaderRed.ID, "Matrices");
    unsigned int uniformBlockIndexGreen  = glGetUniformBlockIndex(shaderGreen.ID, "Matrices");
    unsigned int uniformBlockIndexBlue   = glGetUniformBlockIndex(shaderBlue.ID, "Matrices");
    unsigned int uniformBlockIndexYellow = glGetUniformBlockIndex(shaderYellow.ID, "Matrices");  
    
    glUniformBlockBinding(shaderRed.ID,    uniformBlockIndexRed, 0);
    glUniformBlockBinding(shaderGreen.ID,  uniformBlockIndexGreen, 0);
    glUniformBlockBinding(shaderBlue.ID,   uniformBlockIndexBlue, 0);
    glUniformBlockBinding(shaderYellow.ID, uniformBlockIndexYellow, 0);
    
  • 我们创建Uniform缓冲对象本身

    unsigned int uboMatrices
    glGenBuffers(1, &uboMatrices);
    
    glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
    glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    
    glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * sizeof(glm::mat4));
    

    首先我们为缓冲分配了足够的内存,它等于glm::mat4大小的两倍。GLM矩阵类型的大小直接对应于GLSL中的mat4。接下来,我们将缓冲中的特定范围(在这里是整个缓冲)链接到绑定点0

    个Uniform对应个Uniform缓冲

  • 填充这个缓冲

    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
    glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
    // 向Uniform缓冲中添加数据,0位置开始,1个mat4大小,代表前个部分
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(projection));
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    

    这里我们将投影矩阵储存在Uniform缓冲的前半部分。

    我们会将观察矩阵更新到缓冲的后半部分:

    glm::mat4 view = camera.GetViewMatrix();           
    glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
    // 向Uniform缓冲中添加数据,1个mat4大小起始位置,1个mat4大小,代表后半部分
    glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view));
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    

    只需要设置一次

  • 现在要用4个不同的着色器绘制4个立方体,它们的投影和观察矩阵都会是一样的。

    glBindVertexArray(cubeVAO);
    shaderRed.use();
    glm::mat4 model;
    model = glm::translate(model, glm::vec3(-0.75f, 0.75f, 0.0f));  // 移动到左上角
    shaderRed.setMat4("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 36);        
    // ... 绘制绿色立方体
    // ... 绘制蓝色立方体
    // ... 绘制黄色立方体 
    

    唯一需要设置的uniform只剩model uniform了。在像这样的场景中使用Uniform缓冲对象会让我们在每个着色器中都剩下一些uniform调用。最终的结果会是这样的:

  • 效果

    请添加图片描述

Uniform缓冲对象比Uniform的优点

  • 一次设置很多uniform会比一个一个设置多个uniform要快很多。
  • 比起在多个着色器中修改同样的uniform,在Uniform缓冲中修改一次会更容易一些。
  • 你可以在着色器中使用更多的uniform。OpenGL限制了它能够处理的uniform数量,这可以通过GL_MAX_VERTEX_UNIFORM_COMPONENTS来查询。

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

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

相关文章

关于惠普M277打印机手动双面打印和自动双面打印设置

一&#xff0e;手动双面打印设置​​​​​​​ 1.键盘WINR&#xff0c;在运行框输入“control”&#xff0c;回车或者点击确定。 ​​​​​​​ 2.在控制面板找到设备和打印机&#xff0c;点击进去。 3.找到HP M277字样的打印机&#xff0c;右键选择打印机属性。 4.点击设…

有关部门信息表与员工信息表的常用SQL应用语句实现汇总

背景条件 已知有员工信息表&#xff08;emp&#xff09;和部门信息表&#xff08;dept&#xff09;&#xff0c;具体表的信息如下&#xff1a; 员工信息表emp&#xff1a; 列名类型其他备注empnoDECIMAL(4)主键员工编号enameVARCHAR2(10)员工姓名jobVARCHAR2(9)工种mgrDECIM…

实现第一个内核程序的Hello World

背景 在内核的开发中&#xff0c;总要先入个门。那么就要来编写第一个内核程序 入门 一个 module_init 程序是Linux内核模块的一部分&#xff0c;通过module_init 方法就能将程序载入内核。 module_init 方法需要以下步骤 编写module_init 的代码&#xff0c;并将其保存为…

异常值检验、方差分析

异常值检验 T-test 参考&#xff1a;1.ttest和ttest2 区别 2. ttest在 matlab 3.T test分布表 方差分析&#xff08;ANOVA&#xff09; Def: 方差分析&#xff08;analysis of variance, ANOVA&#xff09;是一种统计检验&#xff0c;用于检验两组或更多组样本的均值是否相…

Allegro如何关闭出线自动拐弯功能操作指导

Allegro如何关闭出线自动拐弯功能操作指导 在用Allegro进行PCB设计的时候,对单个pin进行出线的时候,会遇到走线一出pin就会自动拐弯,并不会按照鼠标轨迹来设计,如下图 期望的效果如下图 如何关闭走线自动拐弯功能,具体操作如下 点击Route

django-vue-admin使用

一、源码地址 注意&#xff0c;一定要使用这个地址。&#xff08;使用其他地址下载下来的感觉代码缺失&#xff0c;踩了大坑&#xff09; django-vue-admin: 基于RBAC模型的权限控制的一整套基础开发平台&#xff0c;前后端分离&#xff0c;后端采用 djangodjango-rest-frame…

QT实现 WebsocketServer端与WebsocketClient 端通信

概 述 WebSockets 是一种通过单个 TCP 连接提供全双工通信信道的 web 技术。2011年&#xff0c;IETF 将 WebSocket 协议标准化为 RFC 6455 。Qt 提供的 QWebSocket 既可以用于客户端应用程序&#xff0c;也可以用于服务端应用程序&#xff0c;接口大部分和 QTcpSocket 一致。 …

R语言机器学习方法在生态经济学领域中的应用

近年来&#xff0c;人工智能领域已经取得突破性进展&#xff0c;对经济社会各个领域都产生了重大影响&#xff0c;结合了统计学、数据科学和计算机科学的机器学习是人工智能的主流方向之一&#xff0c;目前也在飞快的融入计量经济学研究。表面上机器学习通常使用大数据&#xf…

MoviePy介绍

MoivePy是一个用于视频编辑的Python库&#xff0c;可以&#xff1a;剪切、拼接、标题插入、视频合成、视频处理和创建自定义效果。它支持Windows、Linux、Mac&#xff0c;源码地址&#xff1a;https://github.com/Zulko/moviepy&#xff0c;最新发布版本v1.0.3&#xff0c;lice…

Qt(C++)开发一款图片防盗用水印制作小工具

一、前言 文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文…

[小白教程] Javascript Callback以及Promise/async/await 一文通

一、最初 一切从 Javascript 是一门异步编程语言说起&#xff0c;比如这种最简单的&#xff1a; let n 0function f1() {setTimeout(function () {n}, 1000)}f1()console.log(n)可能直觉上会觉得最终n1&#xff0c;但实际上打印出来的是0&#xff0c;因为尽管调用了f1函数&am…

vue笔记——实现打印功能1

第一步&#xff1a;安装vue-print-nb,打开项目终端输入 npm install vue-print-nb --save 第二步&#xff1a;打开package.json文件&#xff0c;在dependencies中出现vue-print-nb&#xff0c;说明安装成功&#xff0c;如下图所示。 第三步&#xff1a; 方法一&#xff1a;全…

电子科技大学计算机系统结构复习笔记(二):指令系统

目录 前言 重点一览 指令集系统结构&#xff08;ISA&#xff09;的分类 分类依据 存储结构 区别 图示 通用寄存器系统结构分类 存储器寻址 概述 寻址方式 MIPS寻址模式 小结 操作数类型 指令操作 与指令编码 常用操作数类型 常用指令系统的操作 常用指令系统编…

【Pytorch基础教程40】DLRM推荐算法模型部署

note 文章目录 note一、DLRM模型1. 特征工程和embedding层2. butterfly shuffle3. 模型结构 二、模型部署Reference 一、DLRM模型 DLRM是2020年meta提出的工业界推荐算法模型&#xff0c;模型结构非常简单&#xff0c;也没用到什么attention机制等的东西&#xff0c;更多是注重…

权限维持-关于影子用户

前言 影子用户相信大家都是比较熟悉&#xff0c;是一种权限维持的好方法。 注&#xff1a;单机和域环境都可以使用&#xff0c;但是域中可能没有那么好用。 复现 一.本地 1. 正常的影子用户 我们在cmd命令中在生成用户时&#xff0c;在用户名后面加上$就可以 net user test$ …

【cfeng work】什么是SaaS? SaaS详细介绍

WorkProj 内容管理 SaaSSaaS的优势SaaS的注意项SaaS产品核心组件cfeng结合work理解SaaS 本文introduce SaaS的相关内容 昨天cfeng已经介绍过云原生了&#xff0c;其实就是应用在设计上就要围绕Cloud&#xff0c;代表技术就是容器化和微服务、DevOps和区别于传统瀑布模型的持续更…

【Linux】——多线程

目录 Linux线程概念 二级页表 线程的优点 线程的缺点 线程异常 线程的用途 Linux中的线程和进程 进程和线程 进程的多个线程共享 进程和线程的关系 Linux线程控制 POSIX线程库 线程创建 线程ID及地址空间布局 线程等待 线程终止 Linux线程概念 什…

华为OD机试真题 Java 实现【找终点】【2023 B卷 100分】,附详细解题思路

一、题目描述 给定一个正整数数组&#xff0c;设为nums&#xff0c;最大为100个成员&#xff0c;求从第一个成员开始&#xff0c;正好走到数组最后一个成员&#xff0c;所使用的最少步骤数。 要求&#xff1a; 第一步必须从第一元素开始&#xff0c;且1 < 第一步的步长 &…

初出茅庐的小李博客之CAN通信基础知识

CAN是什么&#xff1f; CAN 是 Controller Area Network 的缩写&#xff0c;中文是控制器局域网路,是 ISO 国际标准化的串行通信协议之一。 CAN:控制器局域网( Controller Area Network)属于现场总线的范畴&#xff0c;是一种有效支持分布式控制系统的串行通信网络 CAN是由德…

MySQL Windows 64位解压版安装

1、下载MySQL安装包 下载地址&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions) 选择安装版本&#xff1a;我选择的是5.1.43的版本&#xff0c;下载到本地&#xff0c;并解压到自己想要放的位置&#xff0c;比如&#xff1a;D:\soft 2、在D:\soft\m…