opengl 学习着色器

一.GLSL

        着色器是使用一种叫GLSL的类C语言写成的。GLSL着色器编码顺序:声明版本==》定义输入输出==》uniform==》main函数。每个着色器的入口点是main函数,在main函数中我们处理所有的输入变量,并将结果输出到输出变量中。如下图:

#version version_number
in type in_variable_name;
in type in_variable_name;

out type out_variable_name;

uniform type uniform_name;

int main()
{
  // 处理输入并进行一些图形操作
  ...
  // 输出处理过的结果到输出变量
  out_variable_name = weird_stuff_we_processed;
}

二.顶点着色器(补充) 

        顶点着色器的每个输入变量也叫顶点属性,我们能够声明的定点属性是有上限的,它通常由硬件决定。OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限:

int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: ";

三.GLSL数据类型

        GLSL包含默认基础数据类型:intfloatdoubleuintbool。GLSL也有两种容器类型,分别是向量(Vector)和矩阵(Matrix)。这里先介绍向量,后面在学矩阵。

四.向量

        GLSL中的向量是一个可以包含有2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(n代表分量的数量):

类型含义
vecn包含n个float分量的默认向量
bvecn包含n个bool分量的向量
ivecn包含n个int分量的向量
uvecn包含n个unsigned int分量的向量
dvecn包含n个double分量的向量

        一个向量的分量可以通过vec.x这种方式获取,这里x是指这个向量的第一个分量。你可以分别使用.x.y.z.w来获取它们的第1、2、3、4个分量。GLSL也允许你对颜色使用rgba,或是对纹理坐标使用stpq访问相同的分量。

        向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

        你可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可;然而,你不允许在一个vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:

vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

四.输入和输出

        着色器是许多独立的小程序,GLSL定义了inout关键字专门来实现这个目的,每个着色器使用这两个关键字设定输入和输出。只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。但在顶点和片段着色器中会有点不同。顶点着色器应该接收的是一种特殊形式的输入,否则就会效率低下。顶点着色器的输入特殊在,它从顶点数据中直接接收输入。它从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了,layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。片段着色器,它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

        如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。我们用lesson2的示例修改顶点着色器和片段着色器的代码,得到:

 测试 着色器之间的参数传递
// 注意我们如何把一个vec3作为vec4的构造器的参数
// 把输出变量设置为暗红色
const char *vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "out vec4 vertexColor;\n"
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(aPos, 1.0);\n"
    "   vertexColor = vec4(0.5, 0.0, 0.0, 1.0);\n"
    "}\0";

const char *fragmentShaderSource = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "in vec4 vertexColor;\n"
    "void main()\n"
    "{\n"
        "FragColor = vertexColor;\n"
    "}\n\0";
 测试 着色器之间的参数传递 end

五.Uniform关键字

        Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

下面演示如何设置uniform的值,我们让六边形的渲染颜色动起来。

 测试 Uniform
const char *fragmentShaderSource = "#version 330 core\n"
   "out vec4 FragColor;\n"
    "uniform vec4 customColor;\n"
   "void main()\n"
   "{\n"
   "FragColor = customColor;\n"
   "}\n\0";

///  测试 end
   // 着色器处理
        float timeValue = glfwGetTime();                            //获取运行的秒数
        float greenValue = (sin(timeValue) / 2.0f) + 0.5f;          //sin函数让颜色在0.0到1.0之间改变
        ///glGetUniformLocation查询uniform ourColor的位置值。
        /// 我们为查询函数提供着色器程序和uniform的名字(这是我们希望获得的位置值的来源)。
        /// 如果glGetUniformLocation返回-1就代表没有找到这个位置值。
        /// 最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,
        /// 但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。
        int vertexColorLocation = glGetUniformLocation(shaderProgram, "customColor");
        glUseProgram(shaderProgram);
        glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
        //glUseProgram(shaderProgram);
        // 着色器end

        先设置片段着色器的uniform值,我们在main函数中,渲染之前使用着色器,通过获取着色器的位置,然后设置颜色值。效果如下: 

live.csdn.net/v/364740​​​​​​

六.更多属性

        在lesson2里面,我们知道了解了VBO,VAO等关键字,这次我们学习如何设置顶点的颜色以及加入着色器中。

        1.先把顶点数组里面每个坐标后面添加一个颜色值。

    // 顶点输入六边形
    float vertices[] = {
        -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f,
        1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
        0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
        -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
        -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
    };

        2. 顶点着色器,使它能够接收颜色值作为一个顶点属性输入。需要注意的是我们用layout标识符来把aColor属性的位置值设置为1:

/// 测试将定点着色器添加颜色值
const char *vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "layout (location = 1) in vec3 aColor;\n"
    "out vec3 ourColor;\n"
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "   ourColor = aColor;\n"
    "}\0";

const char *fragmentShaderSource = "#version 330 core\n"
    "in vec3 ourColor;\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
        "FragColor = vec4(ourColor, 1.0f);\n"
    "}\n\0";
/// 测试 end

        3.计算VAO里顶点着色器解析的位置和颜色。

 // 解析顶点数据
    // 第一个参数: 0, 着色器的location = 0,对应这里的0
    // 第二个参数: 3, 顶点数据是3个坐标构成,这里是3
    // 第三个参数: 数据类型GL_FLOAT
    // 第四个参数: 是否标准化,标准化就会映射到0~1之间
    // 第五个参数步长: 表示每个顶点的所占空间的大小
    // 第六个参数: 代表偏移,默认位置为0
    //glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    // 启动顶点属性
    //glEnableVertexAttribArray(0);

    // 位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

         4.得到的效果如下:

 这是原作者给出的图像解释:(原作者是3角形,我们是六边形,下面的颜色数量为6)

这个图片可能不是你所期望的那种,因为我们只提供了3个颜色,而不是我们现在看到的大调色板。这是在片段着色器中进行的所谓片段插值(Fragment Interpolation)的结果。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。
基于这些位置,它会插值(Interpolate)所有片段着色器的输入变量。比如说,我们有一个线段,上面的端点是绿色的,下面的端点是蓝色的。如果一个片段着色器在线段的70%的位置运行,它的颜色输入属性就会是一个绿色和蓝色的线性结合;更精确地说就是30%蓝 + 70%绿。

这正是在这个三角形中发生了什么。我们有3个顶点,和相应的3个颜色,从这个三角形的像素来看它可能包含50000左右的片段,片段着色器为这些像素进行插值颜色。如果你仔细看这些颜色就应该能明白了:红首先变成到紫再变为蓝色。片段插值会被应用到片段着色器的所有输入属性上。

七.编写我们自己的着色器(重点来啦)

         编写、编译、管理着色器是件麻烦事。在着色器主题的最后,我们会写一个类来让我们的生活轻松一点,它可以从硬盘读取着色器,然后编译并链接它们,并对它们进行错误检测,这就变得很好用了。这也会让你了解该如何封装目前所学的知识到一个抽象对象中。

编写定点和片段着色器代码文件:

#version 330 core
layout (location = 0) in vec3 aPos;
out vec4 vertexColor;
void main()
{
   gl_Position = vec4(aPos, 1.0);
   vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
}
#version 330 core
out vec4 FragColor;
uniform vec4 customColor;
void main()
{
	FragColor = customColor;
}

 封装着色器类:

shader.h

#pragma once
// 包含glad来获取所有的必须OpenGL头文件
#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>


class Shader
{
public:
    // 程序ID
    unsigned int ID;

    // 构造器读取并构建着色器
    Shader(const char* vertexPath, const char* fragmentPath);
    // 使用/激活程序
    void use();
    // uniform工具函数
    void setFloat(const std::string &name, float value) const;
};

shader.cpp

#include "shader.h"

Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
    // 1. 从文件路径中获取顶点/片段着色器
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    // 保证ifstream对象可以抛出异常:
    vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
    try
    {
        // 打开文件
        vShaderFile.open(vertexPath);
        fShaderFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
        // 读取文件的缓冲内容到数据流中
        vShaderStream << vShaderFile.rdbuf();
        fShaderStream << fShaderFile.rdbuf();
        // 关闭文件处理器
        vShaderFile.close();
        fShaderFile.close();
        // 转换数据流到string
        vertexCode   = vShaderStream.str();
        fragmentCode = fShaderStream.str();
    }
    catch(std::ifstream::failure e)
    {
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const char* vShaderCode = vertexCode.c_str();
    const char* fShaderCode = fragmentCode.c_str();

    // 2. 编译着色器
    unsigned int vertex, fragment;
    int success;
    char infoLog[512];

    // 顶点着色器
    vertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex, 1, &vShaderCode, NULL);
    glCompileShader(vertex);
    // 打印编译错误(如果有的话)
    glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(vertex, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 片段着色器
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment, 1, &fShaderCode, NULL);
    glCompileShader(fragment);
    glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(fragment, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    // 着色器程序
    ID = glCreateProgram();
    glAttachShader(ID, vertex);
    glAttachShader(ID, fragment);
    glLinkProgram(ID);
    // 打印连接错误(如果有的话)
    glGetProgramiv(ID, GL_LINK_STATUS, &success);
    if(!success)
    {
        glGetProgramInfoLog(ID, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }

    // 删除着色器,它们已经链接到我们的程序中了,已经不再需要了
    glDeleteShader(vertex);
    glDeleteShader(fragment);
}

void Shader::use()
{
    glUseProgram(ID);
}


void Shader::setFloat(const std::string &name, float value) const
{
    glUniform4f(glGetUniformLocation(ID, name.c_str()),  0.0f, value, 0.0f, 1.0f);
}

 在main函数中使用着色器类:

        真正实现的代码就两句:

           Shader ourShader("D:/opengl/learning-opengl/lesson3/createtriangle/shader.vs", "D:/opengl/learning-opengl/lesson3/createtriangle/shader.fs");

          // 测试从文件读取着色器
           ourShader.use();
           ourShader.setFloat("customColor", greenValue);

int main()
{
    // 告知opengl我们使用的版本和渲染模式
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // mac 下需要这句话
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    // end

    // 创建opengl窗口
    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // 创建上下文
    glfwMakeContextCurrent(window);
    // 渲染窗口自适应
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    // glad加载
    if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "failed to init glad!" << std::endl;
    }

    // // 构建顶点着色器
    // // 1.创建着色器对象
    // unsigned int vertexShader;
    // vertexShader = glCreateShader(GL_VERTEX_SHADER);
    // // 2.将着色器代码关联到对象上并编译
    // glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    // glCompileShader(vertexShader);
    // // 3.检查编译是否成功
    // int  success;
    // char infoLog[512];
    // glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    // if(!success)
    // {
    //     glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    //     std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    // }
    // // 构建片段着色器
    // unsigned int fragmentShader;
    // fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    // glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    // glCompileShader(fragmentShader);
    // glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    // if(!success)
    // {
    //     glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    //     std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    // }
    // // 编译完成之后链接到程序对象
    // unsigned int shaderProgram;
    // shaderProgram = glCreateProgram();
    // glAttachShader(shaderProgram, vertexShader);
    // glAttachShader(shaderProgram, fragmentShader);
    // glLinkProgram(shaderProgram);
    // glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    // // 判断是否链接程序成功
    // if(!success) {
    //     glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    // }
    // // 使用程序
    // //glUseProgram(shaderProgram);
    // // 删除着色器对象,在把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们
    // glDeleteShader(vertexShader);
    // glDeleteShader(fragmentShader);

    Shader ourShader("D:/opengl/learning-opengl/lesson3/createtriangle/shader.vs", "D:/opengl/learning-opengl/lesson3/createtriangle/shader.fs");

    // 顶点输入六边形
    float vertices[] = {
        -0.5f, 0.5f, 0.0f,
        0.5f,  0.5f, 0.0f,
        1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        -1.0f, 0.0f, 0.0f
    };

    // float vertices[] = {
    //     -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
    //     0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f,
    //     1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
    //     0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
    //     -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
    //     -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
    // };

    unsigned int indices[] = {0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5};
    // 建立缓冲对象
    unsigned int VBO;
    unsigned int VAO;
    unsigned int EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);
    glBindVertexArray(VAO);
    // 绑定到缓冲buffer
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // 刷入缓冲buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    // 解析顶点数据
    // 第一个参数: 0, 着色器的location = 0,对应这里的0
    // 第二个参数: 3, 顶点数据是3个坐标构成,这里是3
    // 第三个参数: 数据类型GL_FLOAT
    // 第四个参数: 是否标准化,标准化就会映射到0~1之间
    // 第五个参数步长: 表示每个顶点的所占空间的大小
    // 第六个参数: 代表偏移,默认位置为0
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    // 启动顶点属性
    glEnableVertexAttribArray(0);

    // // 位置属性
    // glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    // glEnableVertexAttribArray(0);
    // // 颜色属性
    // glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    // glEnableVertexAttribArray(1);

    // 解绑VAO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    // 等待用户关闭窗口
    while(!glfwWindowShouldClose(window))
    {
        processInput(window);
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // 着色器处理uniform
        float timeValue = glfwGetTime();                            //获取运行的秒数
        float greenValue = (sin(timeValue) / 2.0f) + 0.5f;          //sin函数让颜色在0.0到1.0之间改变
        // ///glGetUniformLocation查询uniform ourColor的位置值。
        // /// 我们为查询函数提供着色器程序和uniform的名字(这是我们希望获得的位置值的来源)。
        // /// 如果glGetUniformLocation返回-1就代表没有找到这个位置值。
        // /// 最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,
        // /// 但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。
        // int vertexColorLocation = glGetUniformLocation(shaderProgram, "customColor");
        // glUseProgram(shaderProgram);
        // glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
        // 着色器end
        //glUseProgram(shaderProgram);

        // 测试从文件读取着色器
        ourShader.use();
        ourShader.setFloat("customColor", greenValue);

        // 3. 绘制物体

        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
        // 双缓冲交换
        glfwSwapBuffers(window);
        // 响应各种交互事件
        glfwPollEvents();
    }
    // 释放资源
    glfwTerminate();
    return 0;
}


效果同uniform的动画一样。截图如下:

        到这里基本就简单的学习了着色器。

八.学习地址:(感谢原作者的讲解)

着色器 - LearnOpenGL CN (learnopengl-cn.github.io)

九.demo地址:

learningOpengl: 一起学习opengl

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

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

相关文章

RocketMQ高可用架构涉及常用功能整理

RocketMQ高可用架构涉及常用功能整理 1. 集群高可用系统架构和相关组件1.1 架构说明1.2 相关概念说明1.3 消息模型1.3.1 点对点模型1.3.2 发布订阅模型1.3.3 消息过滤 2. rocketmq的核心参数3. rocketmq常用命令4. 事务性4.1 数据写入流程4.2 数据读流程4.3 事务消息 5. 疑问和…

QT_day2

1.思维导图 2.使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff…

哈希应用 | 布隆过滤器概念 | 代码实现 | 哈希切割

文章目录 1.布隆过滤器1.1.布隆过滤器的基本概念1.2.代码实现1.3.测试代码分析误判率1.4.布隆过滤器的优点1.5.关于几道面试题 关于位图&#xff1a;往期分析的 博客链接 1.布隆过滤器 1.1.布隆过滤器的基本概念 布隆过滤器的引出 位图使用1个比特位 直接定址法&#xff0c;…

深入浅出JVM(一)之Hotspot虚拟机中的对象

本篇文章思维导图 对象的创建 对象的创建可以分为五个步骤:检查类加载,分配内存,初始化零值,设置对象头,执行实例构造器 类加载检查 HotSpot虚拟机遇到一条new指令,会先检查能否在常量池中定位到这个类的符号引用,检查这个类是否类加载过 没有类加载过就去类加载类加载过就进…

中国 AI 开课速度直逼美国 AI 颠覆性创新速度

原文链接&#xff1a; 中国 AI 开课速度直逼美国 AI 颠覆性创新速度 今日热帖&#xff0c;有网友发帖称&#xff1a;Sora 和 ChatGPT 告诉我们&#xff0c;美国确实是遥遥领先&#xff0c;而且还越拉越远。 是不是遥遥领先暂且不说&#xff0c;但领先我们的确是事实。 主要是…

多任务互斥及队列

一.互斥的引入 在FreeRTOS中&#xff0c;互斥&#xff08;Mutex&#xff09;是一种用于保护共享资源的机制。互斥锁可以确保同一时间只有一个任务能够访问共享资源&#xff0c;从而避免了竞态条件和数据不一致的问题。 FreeRTOS中互斥的引入方法&#xff1a; 创建互斥锁&#…

【笔记】【算法设计与分析 - 北航童咏昕教授】绪论

算法设计与分析 - 北航童咏昕教授 文章目录 算法的定义定义性质 算法的表示自然语言编程语言伪代码 算法的分析算法分析的原则渐近分析 算法的定义 定义 给定计算问题&#xff0c;算法是一系列良定义的计算步骤&#xff0c;逐一执行计算步骤即可得预期的输出。 性质 有穷性确…

【Linux】git操作 - gitee

1.使用 git 命令行 安装 git yum install git 2.使用gitee 注册账户 工作台 - Gitee.com 进入gitee&#xff0c;根据提示注册并登录 新建仓库 仓库名称仓库简介初始换仓库 3.Linux-git操作 进入仓库&#xff0c;选择“克隆/下载” 复制下面的两行命令进行git配置 然后将仓库clo…

c语言经典测试题2

1.题1 我们来思考一下它的结果是什么&#xff1f; 我们来分析一下&#xff1a;\\是转义为字符\&#xff0c;\123表示的是一个八进制&#xff0c;算一个字符&#xff0c;\t算一个字符&#xff0c;加上\0&#xff0c;应该有13个&#xff0c;但是strlen只计算\0前的字符个数。所以…

快速学习springsecurity最新版 (版本6.2)---用户认证

简介 ​ Spring Security 是 Spring 家族中的一个安全管理框架。目前比较主流的是另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富,但是shiro并不简便,这里轻量级安全框架更推荐国产安全框架satokensatoken官网 ​ 一般大型的项目都…

QT应用软件【协议篇】周立功CAN接口卡代码示例

文章目录 USBCAN系列CAN接口卡规格参数资料下载QT引用周立功的库安装sdk代码USBCAN系列CAN接口卡 USBCAN系列CAN接口卡兼容USB2.0全速规范,可支持1/2/4/8路CAN接口。采用该接口卡,PC机可通过USB连入CAN网络,进行CAN总线数据采集和处理,主要具备以下几大优势特点: 支持车载…

Matlab图像处理——图像编码解码

1.霍夫曼编码和解码 clear clc Iimread(lena.bmp); Iim2double(I)*255; [height,width]size(I); %求图像的大小 HWmatrixzeros(height,width); Matzeros(height,width); %建立大小与原图像大小相同的矩阵HWmatrix和Mat&#xff0c;矩阵元素为0。 HWmatrix(1,1)I(1,1); …

清洁力强的洗地机什么牌子最好?深度清洁的洗地机推荐

在相信很多人在做家务时&#xff0c;或许都会遇到一个尴尬的境地&#xff1a;虽然使用吸尘器清理了地面上的尘土和杂物&#xff0c;然后再使用拖把擦洗地板&#xff0c;但往往还是无法达到十分干净的效果。扫地机器人对于着色严重的垃圾&#xff0c;往往会出现越拖越脏的情况。…

Vue3之生命周期基础介绍

让我为大家介绍一下vue3的生命周期吧&#xff01; 创建阶段&#xff1a;setup 我们直接console.log就可以了 console.log("创建");挂载阶段&#xff1a;onBeforeMount(挂载前)、onMounted(挂载完毕) import { onBeforeMount, onMounted } from vue; // 挂载前 on…

【前端】Vue-Cli 快速创建Vue3项目及一些项目初始化相关

文章目录 前言1. 安装1.1 安装 Vue 脚手架1.2 创建项目1.3 本地运行项目 2. 推送到仓库2.1 远程仓库准备2.2 关于gitIgnore文件2.3 通过git推送至远程仓库 3. 补充与总结3.1 npm 版本是否要升级到最新&#xff1f;3.2 这个项目建议的各种版本3.3 一般前端gitIgnore文件3.4 推荐…

蚂蚁集团开始招聘华为鸿蒙应用研发工程师

早在2023年12月7日支付宝宣布将全面启动鸿蒙原生应用开发。华为表示&#xff0c;支付宝将基于HarmonyOS NEXT版本开发应用&#xff0c;给消费者带来全场景的新体验。头部应用伙伴的加入&#xff0c;大力推动了鸿蒙生态进一步完善。 就近期蚂蚁集团开始招聘华为鸿蒙应用研发工程…

【2024软件测试面试必会技能】Jmeter+ant+jenkins实现持续集成

jmeterantjenkins持续集成 一、下载并配置jmeter 首先下载jmeter工具&#xff0c;并配置好环境变量&#xff1b;参考&#xff1a;https://www.cnblogs.com/YouJeffrey/p/16029894.html jmeter默认保存的是.jtl格式的文件&#xff0c;要设置一下bin/jmeter.properties,文件内容…

成为高级性能测试:发现性能瓶颈掌握性能调优

当下云计算、大数据盛行的背景下&#xff0c;大并发和大吞吐量的需求已经是摆在企业面前的问题了&#xff0c;其中网络的性能要求尤为关键&#xff0c;除了软件本身需要考虑到性能方面的要求&#xff0c;一些硬件上面的优化也是必不可少的。 作为一名测试工作者&#xff0c;对…

SICTF Round#3 の WP

Misc 签到 SICTF{1f4ce05a-0fed-42dc-9510-6e76dff8ff53} Crypto [签到]Vigenere 附件内容&#xff1a; Gn taj xirly gf Fxgjuakd, oe igywnd mt tegbs mnrxxlrivywd sngearbsw wakksre. Bs kpimj gf tank, it bx gur bslenmngn th jfdetagur mt ceei yze Ugnled Lystel t…

书生·浦语大模型实战营-第六课笔记

1.评测追魂夺命三连问 2.主流大拿有话说-评测框架 3.友商最棒儿子最亲&#xff0c;好瓜都是王婆的 4.真枪实弹上战场 为了给平台省点电&#xff0c;我用了自家的电和自家的电脑进行评测。评测的模型也是之前在自己电脑上跑了3轮花费30多个小时的第四课作业微调的法律大模型。s…