LearnOpenGL-高级OpenGL-11.抗锯齿

news2024/10/7 0:11:09

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

文章目录

  • 抗锯齿
  • 多重采样
    • 锯齿产生原因
    • 多重采样方式
  • OpenGL的MSAA
    • 例子:提示GLFW
    • 离屏MSAA
      • 例子1:多重采样帧缓冲传送到屏幕上
      • 例子2:采样多重采样帧缓冲的纹理缓冲与后期效果

抗锯齿

  • 锯齿图像

    请添加图片描述

    这种现象被称之为走样/锯齿(Aliasing)

  • 抗锯齿方法

    • 超采样抗锯齿SSAA

      • 简介

        更高分辨率来渲染场景,当图像输出到帧缓冲中时,分辨率会采样到正常的分辨率

      • 缺点

        这样比平时要绘制更多的片段,它也会带来很大的性能开销

    • 多重采样抗锯齿MSAA(下一节介绍)

      借鉴SSAA背后的理念,并克服了SSAA的缺点

多重采样

锯齿产生原因

  • 什么是光栅器

    • 光栅器是位于最终处理过的顶点之后到片段着色器之前所经过的所有的算法与过程的总和。

    • 光栅器会将一个图元的所有顶点作为输入,并将它转换为一系列的片段。

  • 问题所在

    顶点坐标理论上可以取任意值,但片段不行,因为它们受限于你窗口的分辨率。

    顶点坐标与片段之间几乎永远也不会有一对一的映射,所以光栅器必须以某种方式来决定每个顶点最终所在的片段/屏幕坐标

  • 光栅器的处理方式

    如图:

    • 这屏幕像素的网格,每个像素的中心包含有一个采样点,它会被用来决定这个三角形是否遮盖了某个像素。
    • 红色的采样点被三角形所遮盖,在每一个遮住的像素处都会生成一个片段。
    • 在三角形边缘的一些部分也遮住了某些屏幕像素,但由于这个采样点并没有被三角形内部所遮盖,所以它们不会受到片段着色器的影响

    所以完整渲染后的三角形在屏幕上是:

    使用了不光滑的边缘来渲染图元,导致之前讨论到的锯齿边缘

多重采样方式

  • 引出

    为了解决上述的锯齿,多重采样所做的正是将单一的采样点变为多个采样点

    注意:它的工作方式并不是因为这4个采样点中有2个采样点被三角形覆盖,就需要运行2次片段着色器。

  • 工作方式

    • 无论三角形遮盖了多少个子采样点,(每个图元中)每个像素只运行一次片段着色器。
    • 个人认为的流程(为了更容易理解而分这样的步骤,但很大概率有误):
      • 每个片段的颜色会由顶点数据插值而得出,得出的颜色存储在每个片段的中心
      • 一个片段有4个采样点,这4个采样点的颜色都与片段中心的颜色一样
      • 当一个片段的4个采样点被三角形包围,则片段的最终颜色是四个采样点颜色相加除以4
      • 当一个片段有两个采样点被三角形包围,两个采样点在外面,则片段的最终颜色是会将两个采样点的颜色相加后除以4

    简单来说,一个像素中如果有更多的采样点被三角形遮盖,那么这个像素的颜色就会更接近于三角形的颜色。

    对于每个像素来说,越少的子采样点被三角形所覆盖,那么它受到三角形颜色的影响就越小

    三角形的不平滑边缘被稍浅的颜色所包围后,从远处观察时就会显得更加平滑了。

OpenGL的MSAA

如果我们想要在OpenGL中使用MSAA,我们必须要使用一个能在每个像素中存储大于1个颜色值的颜色缓冲(因为多重采样需要我们为每个采样点都储存一个颜色)。

所以,我们需要一个新的缓冲类型,来存储特定数量的多重采样样本,它叫做多重采样缓冲(Multisample Buffer)。

例子:提示GLFW

  • 由于我们项目用例glfw,而glfw给了我们这个功能,我们所要做的只是提示(Hint) GLFW,我们希望使用一个包含N个样本的多重采样缓冲。

  • 代码

    int main()
    {
        // glfw: initialize and configure
        // ------------------------------
        glfwInit();
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        // 重点:提示使用多重采样
        glfwWindowHint(GLFW_SAMPLES, 4);
    #ifdef __APPLE__
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    #endif    
        // glfw window creation
        // 创建窗口时,每个屏幕坐标就会使用一个包含4个子采样点的颜色缓冲了
        GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
        // 多重采样都是默认启用的,所以这个调用可能会有点多余,但显式地调用一下会更保险一点。
        glEnable(GL_MULTISAMPLE);
    
  • 效果

离屏MSAA

  • 什么是离屏

    渲染到一个不是默认帧缓冲被叫做离屏渲染(Off-screen Rendering)。

  • 如何实现离屏MSAA

    • 将场景渲染到我们自己的缓冲中
    • 想要实现MSAA的话必须要自己动手生成多重采样缓冲
  • 如何创建多重采样缓冲附加到帧缓冲中

    • 创建多重采样纹理缓冲附件

      glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
      glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE);
      glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
      
    • 将多重采样纹理缓冲附件附加到帧缓冲上

      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0);
      

例子1:多重采样帧缓冲传送到屏幕上

  • 代码

    float vertices[] = {
        -0.5f, -0.5f, -0.5f,
    	......
    };
    // cube
    unsigned int cubeVBO, cubeVAO;
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &cubeVBO);
    glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBindVertexArray(cubeVAO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    
    // 重点在这:自定义帧缓冲进行离屏渲染//
    // 1.1创建帧缓冲
    unsigned int framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    // 1.2创建多重采样纹理缓冲附件
    unsigned int textureColorBufferMultiSampled;
    glGenTextures(1, &textureColorBufferMultiSampled);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB, SCR_WIDTH, SCR_HEIGHT, GL_TRUE);// 重点在这
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
    // 1.3将此多重采样纹理缓冲附件附加到当前绑定的帧缓冲中
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);
    
    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        ......
        // 1.渲染场景到自定义的帧缓冲中
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_DEPTH_TEST);
    
        glBindVertexArray(cubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    
        // 2.将自定义帧缓冲多重采样颜色缓冲的图像传给复制给 默认 的帧缓冲
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);// 源:自定义的帧缓冲
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);          // 目标:默认的帧缓冲
        glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        ......
    }
    
  • 效果

    请添加图片描述

例子2:采样多重采样帧缓冲的纹理缓冲与后期效果

  • 目的

    使用帧缓冲的多重采样缓冲纹理附件,做像是后期处理这样的事情

  • 说明

    我们不能直接在片段着色器中使用多重采样缓冲的纹理。

    但我们能做的是将多重采样缓冲位块传送到一个没有使用多重采样缓冲纹理附件的FBO(帧缓冲对象)中。

    然后用这个FBO的普通颜色缓冲附件来做后期处理,从而达到我们的目的。

    流程是:

    1. 一个带有多重采样缓冲附件的帧缓冲
    2. 一个带有普通颜色缓冲附件的普通帧(临时)缓冲
    3. 将帧缓冲的多重采样缓冲图像附件复制给临时帧缓冲的颜色缓冲附件中
    4. 将临时帧缓冲的颜色缓冲附件当做纹理给着色器采样(注意:需要先切回屏幕默认的帧缓冲,才能输出图像到屏幕上)
  • 代码

    glsl

    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec2 aTexCoords;
    
    out vec2 TexCoords;
    
    void main()
    {
        TexCoords = aTexCoords;
        gl_Position = vec4(aPos, 1.0);
    }
    
    #version 330 core
    out vec4 FragColor;
    in vec2 TexCoords;
    
    uniform sampler2D screenTexture;
    
    const float offset = 1.0 / 300.0;
    
    void main(){ 
        // 1.原本已经是平滑的了,不变输出即平滑的
        // vec3 col = texture(screenTexture, TexCoords).rgb;
    	// FragColor = vec4(vec3(col), 1.0);
    
        // 2.进行后期:模糊
        vec2 offsets[9] = vec2[](
            vec2(-offset,  offset), // 左上
            vec2( 0.0f,    offset), // 正上
            vec2( offset,  offset), // 右上
            vec2(-offset,  0.0f),   // 左
            vec2( 0.0f,    0.0f),   // 中
            vec2( offset,  0.0f),   // 右
            vec2(-offset, -offset), // 左下
            vec2( 0.0f,   -offset), // 正下
            vec2( offset, -offset)  // 右下
        );
        // 改变这个数组
        float kernel[9] = float[](
            1.0 / 16, 2.0 / 16, 1.0 / 16,
            2.0 / 16, 4.0 / 16, 2.0 / 16,
            1.0 / 16, 2.0 / 16, 1.0 / 16  
        );
        vec3 sampleTex[9];
        for(int i = 0; i < 9; i++){
            sampleTex[i] = vec3(texture(screenTexture, TexCoords.st + offsets[i]));// TexCoords.st = TexCoords.xy
        }
        vec3 col = vec3(0.0);
        for(int i = 0; i < 9; i++){
            col += sampleTex[i] * kernel[i];// 周围颜色乘以相应的权重并加起来就是核效果
        }
        FragColor = vec4(col, 1.0);
    }
    // 后期:边缘检测可能会导致锯齿,可以再进行模糊的核滤镜可减少锯齿
    

    cpp

    Shader shader("assest/shader/4高级OpenGL/6.11.1.抗锯齿-cube.vs", "assest/shader/4高级OpenGL/6.11.1.抗锯齿-cube.fs");
    Shader screenShader("assest/shader/4高级OpenGL/6.11.2.抗锯齿-采样帧缓冲的颜色附件.vs", "assest/shader/4高级OpenGL/6.11.2.抗锯齿-采样帧缓冲的颜色附件.fs");
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,
       ......
    };
    // 渲染quad的顶点数据
    float quadVertices[] = {   
        // 位置       // 纹理坐标
        -1.0f,  1.0f,  0.0f, 1.0f,
        -1.0f, -1.0f,  0.0f, 0.0f,
        1.0f, -1.0f,  1.0f, 0.0f,
    
        -1.0f,  1.0f,  0.0f, 1.0f,
        1.0f, -1.0f,  1.0f, 0.0f,
        1.0f,  1.0f,  1.0f, 1.0f
    };
    // cube
    unsigned int cubeVBO, cubeVAO;
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &cubeVBO);
    glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBindVertexArray(cubeVAO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // quad
    unsigned int quadVBO, quadVAO;
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
    glBindVertexArray(quadVAO);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
    glEnableVertexAttribArray(1);
    // 重点代码:自定义帧缓冲进行离屏渲染
    // 1.1创建帧缓冲
    unsigned int framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    // 1.2创建多重采样纹理缓冲
    unsigned int textureColorBufferMultiSampled;
    glGenTextures(1, &textureColorBufferMultiSampled);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB, SCR_WIDTH, SCR_HEIGHT, GL_TRUE);// 重点在这
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
    // 1.3将此多重采样纹理缓冲附加到当前绑定的帧缓冲中
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);;
    
    // 2.1创建一个临时的自定义帧缓冲,以便能将多重采样的图像进行采样后期处理
    unsigned int intermediateFBO;
    glGenFramebuffers(1, &intermediateFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO);
    // 2.2创建一个纹理缓冲
    unsigned int screenTexture;
    glGenTextures(1, &screenTexture);
    glBindTexture(GL_TEXTURE_2D, screenTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // 2.3将此纹理缓冲附加到当前绑定的帧缓冲中
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0);
    // 2.4检查是否附加成功
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        std::cout << "错误:帧缓冲不完整" << std::endl;
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);// 解绑
    
    // 设置shader采样的纹理单元为0
    screenShader.use();
    screenShader.setInt("screenTexture", 0);
    // render loop
    while (!glfwWindowShouldClose(window))
    {
        ......
        // 只有真正渲染场景的shader需要变换矩阵,而screenshader只需要采样纹理输出颜色
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        shader.use();
        shader.setMat4("projection", projection);
        shader.setMat4("view", view);
        shader.setMat4("model", glm::mat4(1.0f));
    	// 重点代码//
        // 1.渲染场景到自定义的帧缓冲中
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_DEPTH_TEST);
    
        glBindVertexArray(cubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    
        // 2.将自定义帧缓冲多重采样纹理缓冲的图像复制给临时帧缓冲的普通纹理缓冲中
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);    // 源:带有多重采样纹理缓冲附件的帧缓冲
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO);// 目标:带有普通纹理缓冲附件的帧缓冲
        glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // 3.初始化,先绑定屏幕默认的帧缓冲,才能输出图像到屏幕上)
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // 为了线框渲染时能看见:线框是黑色的,窗口为白色的才看得见
        glClear(GL_COLOR_BUFFER_BIT);
        glDisable(GL_DEPTH_TEST); 			// 禁用深度测试,这样屏幕空间四边形不会因深度测试而被丢弃。
    
        // 4.绘制quad,将临时帧缓冲的颜色缓冲附件当做纹理给着色器采样
        screenShader.use();
        glBindVertexArray(quadVAO);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, screenTexture);// 颜色缓冲附件作为0号纹理单元的纹理
        glDrawArrays(GL_TRIANGLES, 0, 6);
    	......
    }
    
  • 采样纹理时不加模糊后期效果

  • 采样纹理时加了模糊后期

    请添加图片描述

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

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

相关文章

【华为OD统一考试B卷 | 100分】太阳能板最大面积(C++ Java JavaScript Python)

题目描述 给航天器一侧加装长方形或正方形的太阳能板(图中的红色斜线区域),需要先安装两个支柱(图中的黑色竖条),再在支柱的中间部分固定太阳能板。 但航天器不同位置的支柱长度不同,太阳能板的安装面积受限于最短一侧的那根支柱长度。如图: 现提供一组整形数组的支柱高…

chatgpt赋能python:如何升级你的Python到最新版本

如何升级你的Python到最新版本 Python作为一种强大的编程语言&#xff0c;拥有广泛的用途&#xff0c;从网站开发到数据科学&#xff0c;都可以使用它来实现。然而&#xff0c;Python不断更新&#xff0c;每个新版本都带来了新的功能和改进&#xff0c;因此升级Python到最新版…

二叉树概念(二)

平衡二叉树 AVL树(Adelson-Velsky 和 Landis) 左子树和右子树的高度之差的绝对值小于等于1 C++ 中,可以直接使用 std::set 类作为平衡二叉树;Java 中,可以直接使用 TreeSet。在 Python 中,没有内置的库可以用来模拟平衡二叉树。 力扣 红黑树 (Red-Black Tree) 是一种二…

Shell脚本数组简介及运用

目录 一、数组简介 二、数组支持的数据类型 三、定义数组 四、获取数组某个索引处的值 五、遍历数组元素 六、获取数组长度 七、截取数组元素 八、数组的元素替换 1.临时替换 2.永久替换 九、删除元素或数组 十、数组追加元素 1.满元素数组追加 2.非满元素数组追…

【模块三:职业成长】39|能力维度四:如何从做技术到为企业创造生存优势?

你好&#xff0c;我是郭东白。今天这节课是架构师能力维度的第四部分&#xff0c;我们来继续探索架构师成长过程的能力跃迁。不过今天我们会连续讲两个跃迁&#xff1a;从跨域架构师到总架构师&#xff08;首席架构师&#xff09;的跃迁&#xff1b;从总架构师再到 CTO 的跃迁。…

浅尝 xen 虚拟化

前言 之前分享过使用 oVirt 部署私有虚拟化环境, oVirt 是基于 KVM 虚拟化开发了一系列的管理工具, 以 Web Console /CLI 的方式交付使用. 今天记录和分享的是在 VMware Workstation 中使用 Alpine 作为基础操作系统部署 xen 虚拟化环境的简单测试. xen 基本概念 xen 可以理…

chatgpt赋能python:Python的发音

Python的发音 Python是一种广泛使用的编程语言&#xff0c;用于web开发、数据分析、科学计算等众多领域。然而&#xff0c;Python这个词汇的发音在不同的地方却存在差异。在本文中&#xff0c;我们将介绍Python的发音&#xff0c;解决大家的困惑。 发音方式 Python在英语中常…

【数学建模系列】TOPSIS法的算法步骤及实战应用——MATLAB实现

文章目录 TOPSIS简介方法和原理数学定义数学语言描述现实案例 正负理想解定义实例 量纲 TOPSIS法的算法步骤1.用向量规范化的方法求得规范决策矩阵2.构成加权规范阵C(c~ij~)~m*n~3.确定正负理想解的距离4.计算各方案到正理想解与负理想解的距离5.计算各方案的综合评价指数6.排列…

STM32源码阅读记录之HAL库(SystemTick)

问题 HAL_Delay是怎么做到可以延迟控制的? 分析记录 步骤01&#xff1a;看函数本身 void HAL_Delay(uint32_t Delay); /*** brief 此函数根据变量递增提供最小延迟&#xff08;以毫秒为单位&#xff09;。* note 在默认的实现中&#xff0c;SysTick计时器是时基的来源。它用…

【Leetcode -225.用队列实现栈 -232.用栈实现队列】

Leetcode Leetcode -225.用队列实现栈Leetcode -232.用栈实现队列 Leetcode -225.用队列实现栈 题目&#xff1a;仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 …

chatgpt赋能python:Python实践:如何升级pip

Python实践&#xff1a;如何升级pip Python作为一门高效的脚本语言&#xff0c;被广泛应用于数据分析、人工智能、Web开发等领域。而pip则是Python的包管理工具&#xff0c;是开发Python应用的必备工具。但是pip在使用过程中&#xff0c;有时候会出现版本不兼容或者出现漏洞等…

chatgpt赋能python:Python取两位小数的方法

Python取两位小数的方法 介绍 Python是一种高级编程语言&#xff0c;因其简洁易懂、跨平台等特点&#xff0c;被广泛应用于科学计算、数据分析、网站开发、人工智能等领域。在编写Python程序时&#xff0c;我们经常需要将浮点数精确到小数点后几位&#xff0c;比如精确到两位…

【0基础教程】Javascript 里的分组正则Capturing Groups使用方法及原理

一、从最简单开始 现有一个字符串&#xff1a; “1-apple” 需要提取出 1 和 apple 来&#xff0c;对应的正则表达式很简单&#xff1a; ^(\d)-(.)$ 对应的代码也比较简单&#xff1a; const str "1-apple"const regexp /^(\d)-(.)$/let match regexp.exec(str)c…

chatgpt赋能python:Python如何升序输出?从入门到实践!

Python如何升序输出&#xff1f;从入门到实践&#xff01; 作为一门高级编程语言&#xff0c;Python是目前应用广泛且最为流行的一门语言之一。它逐渐成为开发者的首选语言&#xff0c;因为它易学易用&#xff0c;可读性强&#xff0c;支持多种编程范式&#xff0c;提供了强大…

chatgpt赋能python:如何升级Python的pip版本

如何升级Python的pip版本 如果你使用Python来进行程序开发&#xff0c;那么你一定需要用到pip&#xff0c;它是Python的包管理器&#xff0c;用于安装和管理各种Python库。 不过&#xff0c;一旦你开始使用pip&#xff0c;你可能会遇到一个问题&#xff1a;你的pip版本可能会…

软考A计划-系统架构师-学习笔记-第二弹

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

前端vue基于原生check增强单选多选插件

前端vue基于原生check增强单选多选插件, 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id12979 效果图如下: #### 使用方法 使用方法 <!-- 多选组件 checkData&#xff1a;多选数据 change&#xff1a;多选事件 --> <ccCheckView…

FreeRTOS:软件定时器

目录 前言什么是硬件定时器什么是软件定时器 一、回调函数二、定时器的配置2.1软件定时器分类2.2定时器相关API函数2.2.1创建软件定时器2.2.2开启软件定时器2.2.3停止软件定时器2.2.4复位软件定时器 三、软件定时器实验3.1实验要求3.2实验代码 前言 什么是硬件定时器 CPU内部…

Limma | 三个组的差异分析怎么分析做呢!?~

1写在前面 高考结束了&#xff0c;不知道各位考生考的怎么样&#xff0c;这种时候总是几家欢喜几家忧&#xff0c;但这也是实现阶级流动的最佳机会。&#x1f914; 回想自己高考过去10几年了&#xff0c;不能说学了医后悔吧&#xff0c;只能说后悔至极&#xff0c;苦不堪言啊&a…

Linux -- 用户和组

目录 一、root用户&#xff08;超级管理员&#xff09; 1.1 切换用户命令&#xff1a;su 和 exit 命令 1.2 sudo命令 二、用户、用户组 2.1 用户组管理 2.1.1 创建用户组 2.1.2 删除用户组 2.2 用户管理 2.2.1 创建用户 2.2.2 删除用户 2.2.3 查看用户所属组 2.2.…