[OpenGL] Transform feedback 介绍以及使用示例

一、简介

本文介绍了 OpenGL 中 Transform Feedback 方法的基本概念和代码示例。

二、Transform Feedback 介绍

1. Transform Feedback 简介

根据 OpenGL-wiki,Transform Feedback 是捕获由顶点处理步骤(vertex shader 和 geometry shader)生成的图元(Primitives)的过程,将这些图元的数据记录到缓冲区对象(Buffer Objects)中。这样可以保留物体的变换后渲染状态,(在GPU中)多次重新提交这些数据。

简单来讲,使用 Transform Feedback 可以将 vertex shader 和 geometry shader 处理后的数据存储到指定的 Buffer Objects 中,而不继续进行后续的 Clipper、Rasterizaer 和 Test & Blending 阶段。

Transform Feedback Buffer 在渲染管线中所处的位置如下图所示:
TB in render pipline

2. 使用 Transform feedback 可实现的功能

  1. 实现粒子系统:
    在粒子系统中,Transform Feedback 可以用来捕获粒子的状态信息(如位置、速度、颜色等),然后将这些数据存储到缓冲区中。在每一帧中,系统可以通过 Transform Feedback 来更新粒子的状态,从而避免在CPU中重新计算整个粒子系统。这种方式能有效地避免 CPU 参与数据更新,提高系统的性能。
  2. 进行GPU并行运算:
    使用 Transform Feedback,可以在 vertex shader 中执行并行计算任务,将计算结果直接存储在 Transform Feedback 对应的 buffer 中 (GPU 内存中)。
    例如,可以在 vertex shader 中执行物理计算(如模拟重力或其他力学运动),并将计算结果(例如新的顶点位置)存储在 buffer 中,作为新渲染 shader program 的输入,或者直接作为结果传输到 CPU 上。而不需要每次都在 CPU 计算再传输到 GPU 中。

三、使用示例

1. 使用 Transform Feedback 与 vertex shader

在本例中,使用 Transform feedback 方法在 GPU (vertex shader) 中进行并行运算,将输入的数据开平方后存储到 Transform feedback 对应的 buffer 中再传到 CPU 中进行打印输出。

1.1. 步骤

  1. 初始化 glfw, glad, 窗口
  2. 构建,编译 vertex shader
  3. 指定 Transform feedback object 的接收变量
  4. 链接 shader program
  5. 准备输入数据, VAO 和 VBO
  6. 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据
  7. 运行 shader 程序
    7.1 use shader program
    7.2 disable rasterizer
    7.3 begin transform feedback
    7.4 bind VAO
    7.5 draw
    7.6 end transform feedback
  8. 将 Transform feedback buffer 中的数据传输到 CPU
  9. 释放资源

1.2. 代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>#include <iostream>
// 用于处理窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
// 用于处理用户输入的函数
void processInput(GLFWwindow *window);// 指定窗口默认width和height像素大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;/****** vertex shader 代码 ******/
const char *vertexShaderSource = R"(
#version 330 core
layout (location = 0) in float inputValue;
out float outputValue;
void main()
{outputValue = sqrt(inputValue);
}
)";
/************************************/int main()
{/****** 1.初始化glfw, glad, 窗口 *******/// glfw 初始化 + 配置 glfw 参数glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// glfw 生成窗口// (由于我们只使用 Transform feedback 进行并行运算,因此实际上不生成窗口也可以)GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){// 检查是否成功生成窗口,如果没有成功打印出错信息并且退出std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}// 设置窗口window的上下文glfwMakeContextCurrent(window);// 配置window变化时的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 使用 glad 加载 OpenGL 中的各种函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}/************************************//****** 2.构建,编译 vertex shader ******/// vertex shaderunsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// 检查是否成功编译 vertex shaderint 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;}// 生成 shader 程序unsigned int shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);/****** 3. 指定 Transform feedback object 的接收变量  ******/// 该步骤必须在 glLinkProgram() 之前进行// 因为使用 Transform feedback 会改变 shader 程序的渲染流程,因此需要先指定 Transform feedback 的接收变量,告诉// OpenGL 应该如何链接、编译 shader 程序,再进行 glLinkProgram()const char *feedbackVaryings[] = {"outputValue"}; // 我们希望接收 vertex shader 的输出变量 out float outputValue// glTransformFeedbackVaryings() 函数的参数解释:// (1). 目标 shader program// (2). 目标接收变量的个数,即 feedbackVaryings 数组中 字符串的个数// (3). 目标接收的变量名字符串数组// (4). 接收变量的存储目标. GL_INTERLEAVED_ATTRIBS, 表示所有输出变量都输出到 一个 buffer 中,// GL_SEPARATE_ATTRIBS 表示 输出变量输出到不同的 buffer 中. 此处我们选择将输出存储到 一个 buffer 中glTransformFeedbackVaryings(shaderProgram, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);/************************************//****** 4. 链接 shader program  ******/glLinkProgram(shaderProgram); // 链接 shader program// 检查是否成功链接 vertex shader 和 fragment shaderglGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}glDeleteShader(vertexShader);/************************************//****** 5.准备输入数据, VAO 和 VBO *******/float inputData[] = {0.0f, 0.5f, 1.0f};unsigned int VBO, VAO;glGenVertexArrays(1, &VAO); // 生成一个VAO对象glGenBuffers(1, &VBO);      // 生成一个VBO对象glBindVertexArray(VAO); // 绑定VAOglBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBOglBufferData(GL_ARRAY_BUFFER, sizeof(inputData), inputData,GL_STATIC_DRAW); // 将vertices中的数据复制到刚刚绑定的VBO buffer中去,VBO buffer是GPU内存上的一块区域// glVertexAttribPointer() 用于指定顶点属性如何从绑定的 VBO 中读取数据,// 此时绑定的 buffer 是 VBO,因此location=0 处的顶点属性就从VBO中读数据 glVertexAttribPointer()// 需要6个参数,每个参数的含义如下:// (1). 指定要配置的顶点属性, 即 shader 中指定 数据 location 值,对于position部分,此处填入 0// (2). 指定顶点属性的大小,shader中我们将VBO中的数据传给了一个 float 类型的变量,// 每个顶点数据只有一个 float, 因此此处填 1// (3). 指定数据的类型,用于使用的是浮点型,因此此处填入 GL_FLOAT// (4). 指定是否自动对数据进行归一化,我们不需要自动诡异化,因此此处填入 GL_FALSE// (5). 指定VBO中顶点数据组之间的间隔,可以手动设置间隔,例如 sizeof(float),// 也可以填 0 让 Opengl 自动设置// (6). 指定读取VBO数据起始位置偏移,单位为字节,这里填入 (void *)(0*sizeof(float)) 即可glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0,(void *)(0 * sizeof(float))); // 设置如何读取VBO中数据glEnableVertexAttribArray(0);                       // 启用 location = 0 处的顶点属性glBindBuffer(GL_ARRAY_BUFFER, 0);                   // 解绑VBO/************************************//****** 6. 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据  ******/// 新建一个 buffer 用于作为 Transform feedback object 的 buffer,用来接收 计算后的输出结果GLuint tbo;glGenBuffers(1, &tbo);glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo);glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(inputData), nullptr, GL_STATIC_READ);// 将 tbo 绑定到 TFO 的 0号卡槽上, 此处不对卡槽机制进行过多的讲解glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);/************************************//****** 7. 运行 shader 程序  ******/// 7.1 use shader programglUseProgram(shaderProgram);// 关闭 光栅化阶段, 只使用 vertex shader 和 geometry shader (如果有的话) 阶段// 7.2 disable rasterizerglEnable(GL_RASTERIZER_DISCARD);// 开始绘制 Transform feedback// 此处只有 vertex shader,因此 glBeginTransformFeedback() 中指定的 图元 需要跟 glDrawArray() 中的相同// 7.3 begin transform feedbackglBeginTransformFeedback(GL_POINTS);// 7.4 bind VAOglBindVertexArray(VAO); // 绑定VAO,指定当前使用的VAO对象// 7.5 drawglDrawArrays(GL_POINTS, 0,3); // 输入数据数据只有三个 float,因此可以将每个数据分配给一个 point ,在 vertex shader 中进行计算处理// 7.6 end transform feedbackglEndTransformFeedback(); // 结束 Transform feedback// 开启 光栅化阶段glDisable(GL_RASTERIZER_DISCARD);glBindVertexArray(0); // 解绑 VAOglFlush(); // 刷新 gl 命令 buffer 保证前面的命令都运行完成/************************************//****** 8. 将 Transform feedback buffer 中的数据传输到 CPU   ******/// 接收 Transform feedback 对应的 buffer (tbo) 中的数据GLfloat feedback[3];glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);for (int i = 0; i < 3; i++){printf("%f\n", feedback[i]);}/************************************//****** 9.释放资源 ******/// 释放之前申请的 VBO, VAO 资源和 shader 程序glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);// glfw 释放 glfw使用的所有资源glfwTerminate();/************************************/return 0;
}// 用于处理用户输入的函数
void processInput(GLFWwindow *window)
{// 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}// 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
// 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{glViewport(0, 0, width, height);
}

1.3. 运行结果

0.000000
0.707107
1.000000

2. 使用 Transform Feedback 与 vertex + geometry shader

在本例中,使用 Transform feedback 方法在 vertex shader 和 geometry shader 中进行并行运算,在 vertex shader 中将输入的数据开平方,然后在 geometry shader 中将一份数据分别加0, 加1和加2,变为三份数据。最后存储到 Transform feedback 对应的 buffer 中再传到 CPU 中进行打印输出。

2.1. 步骤

  1. 初始化 glfw, glad, 窗口
  2. 构建,编译 vertex shader , geometry shader
  3. 指定 Transform feedback object 的接收变量
  4. 链接 shader program
  5. 准备输入数据, VAO 和 VBO
  6. 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据
  7. 运行 shader 程序
    7.1 use shader program
    7.2 disable rasterizer
    7.3 begin transform feedback
    7.4 bind VAO
    7.5 draw
    7.6 end transform feedback
  8. 将 Transform feedback buffer 中的数据传输到 CPU
  9. 释放资源

2.2 代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>#include <iostream>
// 用于处理窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
// 用于处理用户输入的函数
void processInput(GLFWwindow *window);// 指定窗口默认width和height像素大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;/****** vertex shader 代码 ******/
const char *vertexShaderSource = R"(
#version 330 core
layout (location = 0) in float inputValue;
out float geoValue;
void main()
{geoValue =  sqrt(inputValue);
}
)";/****** geometry shader 代码 ******/
const char *geometryShaderSource = R"(
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices=3) out;
in float[] geoValue;
out float outputValue;
void main()
{for(int i=0; i<3; i++){outputValue = geoValue[0] + i;EmitVertex();}EndPrimitive();
}
)";/************************************/int main()
{/****** 1.初始化glfw, glad, 窗口 *******/// glfw 初始化 + 配置 glfw 参数glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// glfw 生成窗口// (由于我们只使用 Transform feedback 进行并行运算,因此实际上不生成窗口也可以)GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){// 检查是否成功生成窗口,如果没有成功打印出错信息并且退出std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}// 设置窗口window的上下文glfwMakeContextCurrent(window);// 配置window变化时的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 使用 glad 加载 OpenGL 中的各种函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}/************************************//****** 2.构建,编译 vertex shader, geometry shader ******/// vertex shaderunsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// 检查是否成功编译 vertex shaderint 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;}// geometry shaderunsigned int geometryShader = glCreateShader(GL_GEOMETRY_SHADER);glShaderSource(geometryShader, 1, &geometryShaderSource, NULL);glCompileShader(geometryShader);// 检查是否成功编译 geometry shaderglGetShaderiv(geometryShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(geometryShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::GEOMETRY::COMPILATION_FAILED\n" << infoLog << std::endl;}// 生成 shader 程序unsigned int shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, geometryShader);/****** 3. 指定 Transform feedback object 的接收变量  ******/// 该步骤必须在 glLinkProgram() 之前进行// 因为使用 Transform feedback 会改变 shader 程序的渲染流程,因此需要先指定 Transform feedback 的接收变量,告诉// OpenGL 应该如何链接、编译 shader 程序,再进行 glLinkProgram()const char *feedbackVaryings[] = {"outputValue"}; // 我们希望接收 vertex shader 的输出变量 out float outputValue// glTransformFeedbackVaryings() 函数的参数解释:// (1). 目标 shader program// (2). 目标接收变量的个数,即 feedbackVaryings 数组中 字符串的个数// (3). 目标接收的变量名字符串数组// (4). 接收变量的存储目标. GL_INTERLEAVED_ATTRIBS, 表示所有输出变量都输出到 一个 buffer 中,// GL_SEPARATE_ATTRIBS 表示 输出变量输出到不同的 buffer 中. 此处我们选择将输出存储到 一个 buffer 中glTransformFeedbackVaryings(shaderProgram, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);/************************************//****** 4. 链接 shader program  ******/glLinkProgram(shaderProgram); // 链接 shader program// 检查是否成功链接 vertex shader 和 fragment shaderglGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(geometryShader);/************************************//****** 5.准备输入数据, VAO 和 VBO *******/float inputData[] = {0.0f, 0.5f, 1.0f};unsigned int VBO, VAO;glGenVertexArrays(1, &VAO); // 生成一个VAO对象glGenBuffers(1, &VBO);      // 生成一个VBO对象glBindVertexArray(VAO); // 绑定VAOglBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBOglBufferData(GL_ARRAY_BUFFER, sizeof(inputData), inputData,GL_STATIC_DRAW); // 将vertices中的数据复制到刚刚绑定的VBO buffer中去,VBO buffer是GPU内存上的一块区域// glVertexAttribPointer() 用于指定顶点属性如何从绑定的 VBO 中读取数据,// 此时绑定的 buffer 是 VBO,因此location=0 处的顶点属性就从VBO中读数据 glVertexAttribPointer()// 需要6个参数,每个参数的含义如下:// (1). 指定要配置的顶点属性, 即 shader 中指定 数据 location 值,对于position部分,此处填入 0// (2). 指定顶点属性的大小,shader中我们将VBO中的数据传给了一个 float 类型的变量,// 每个顶点数据只有一个 float, 因此此处填 1// (3). 指定数据的类型,用于使用的是浮点型,因此此处填入 GL_FLOAT// (4). 指定是否自动对数据进行归一化,我们不需要自动诡异化,因此此处填入 GL_FALSE// (5). 指定VBO中顶点数据组之间的间隔,可以手动设置间隔,例如 sizeof(float),// 也可以填 0 让 Opengl 自动设置// (6). 指定读取VBO数据起始位置偏移,单位为字节,这里填入 (void *)(0*sizeof(float)) 即可glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0,(void *)(0 * sizeof(float))); // 设置如何读取VBO中数据glEnableVertexAttribArray(0);                       // 启用 location = 0 处的顶点属性glBindBuffer(GL_ARRAY_BUFFER, 0);                   // 解绑VBO/************************************//****** 6. 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据  ******/// 新建一个 buffer 用于作为 Transform feedback object 的 buffer,用来接收 计算后的输出结果GLuint tbo;glGenBuffers(1, &tbo);glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo);// 注意!!! 由于我们使用 geometry shader 将一个输入变量(geoValue)变为三个输出(outputValue),// 因此 Transform feedback object 中用于接收数据的 buffer 大小应该为输入数据的 3 倍大glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(inputData) * 3, nullptr, GL_STATIC_READ);// 将 tbo 绑定到 TFO 的 0号卡槽上, 此处不对卡槽机制进行过多的讲解glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);/************************************//****** 7. 运行 shader 程序  ******/// 7.1 use shader programglUseProgram(shaderProgram);// 7.2 disable rasterizer// 关闭 光栅化阶段, 只使用 vertex shader 和 geometry shader (如果有的话) 阶段glEnable(GL_RASTERIZER_DISCARD);// 7.3 begin transform feedback// 开始绘制 Transform feedback// 注意!!! 由于此时使用了 geometry shader, glBeginTransformFeedback() 中的图元应该与 geometry shader 的输出一致,// 尽管 geometry shader 中的 out 图元为 triangle_strip,// 但是 transform feedback 依旧独立地存储每个 triangle 的顶点数据,// 而不是使用 strip 对数据进行压缩glBeginTransformFeedback(GL_TRIANGLES);// 7.4 bind VAOglBindVertexArray(VAO); // 绑定VAO,指定当前使用的VAO对象// 7.5 drawglDrawArrays(GL_POINTS, 0,3); // 输入数据数据只有三个 float,因此可以将每个数据分配给一个 point ,在 vertex shader 中进行计算处理// 7.6 end transform feedbackglEndTransformFeedback(); // 结束 Transform feedback// 开启 光栅化阶段glDisable(GL_RASTERIZER_DISCARD);glBindVertexArray(0); // 解绑 VAOglFlush();            // 刷新 gl 命令 buffer 保证前面的命令都运行完成/************************************//****** 8. 将 Transform feedback buffer 中的数据传输到 CPU   ******/// 接收 Transform feedback 对应的 buffer (tbo) 中的数据GLfloat feedback[9];glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);for (int i = 0; i < 9; i++){printf("%f\n", feedback[i]);}/************************************//****** 9.释放资源 ******/// 释放之前申请的 VBO, VAO 资源和 shader 程序glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);// glfw 释放 glfw使用的所有资源glfwTerminate();/************************************/return 0;
}// 用于处理用户输入的函数
void processInput(GLFWwindow *window)
{// 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}// 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
// 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{glViewport(0, 0, width, height);
}

2.3 结果

0.000000
1.000000
2.000000
0.707107
1.707107
2.707107
1.000000
2.000000
3.000000

四、参考

[1.] OpenGL-Transform Feedback
[2.] OpenGL–Transform feedback示例解析
[3.] OpenGL-wiki-Transform Feedback

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

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

相关文章

【Linux】—简单实现一个shell(myshell)

大家好呀&#xff0c;我是残念&#xff0c;希望在你看完之后&#xff0c;能对你有所帮助&#xff0c;有什么不足请指正&#xff01;共同学习交流哦&#xff01; 本文由&#xff1a;残念ing原创CSDN首发&#xff0c;如需要转载请通知 个人主页&#xff1a;残念ing-CSDN博客&…

什么是Apache日志?为什么Apache日志分析很重要?

Apache是全球最受欢迎的Web服务器软件&#xff0c;支持约30.2%的所有活跃网站。凭借其可靠性、灵活性和强大的功能&#xff0c;Apache数十年来一直是互联网的中坚力量。 一、Apache Web服务器的工作原理 Apache Web服务器的工作原理如下&#xff1a; 接收HTTP请求&#xff1…

如何使用 uni-app 构建直播应用程序?

使用uni-app构建直播应用程序涉及前端和后端的开发&#xff0c;以及音视频处理技术的选择。下面我将概述一个典型的直播应用架构&#xff0c;并详细说明如何在uni-app中实现关键功能。 直播应用架构 前端&#xff08;uni-app&#xff09;&#xff1a;负责用户界面展示、互动逻…

Spring Security 6 系列之一 - 开篇入门

之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级为6.3.0&#xff0c;关键是其风…

好用的工单系统,适用于各种场景

项目名称 smart 工单系统 项目简介 Smart-API 工单系统是基于 Go 语言开发的后台管理系统&#xff0c;前后端分离&#xff0c;采用 Gin 框架作为后端&#xff0c;Vue.js 和 Element UI 作为前端。系统旨在实现对工单管理的高效处理&#xff0c;支持任务分配、执行监控、…

docker 搭建自动唤醒UpSnap工具

1、拉取阿里UpSnap镜像 docker pull crpi-k5k93ldwfc7o75ip.cn-hangzhou.personal.cr.aliyuncs.com/upsnap/upsnap:4 2、创建docker-compose.yml文件&#xff0c;进行配置&#xff1a; version: "3" services:upsnap:container_name: upsnapimage: crpi-k5k93ldwf…

7.Linux - 安装MySQL、Tomcat、Nginx、RabbitMQ、Redis

Linux - 安装MySQL 文章目录 Linux - 安装MySQL一、MySQL 5.71.1 安装1.2 配置 二、MySQL 8.x2.1 安装2.2 配置 三、Tomcat安装3.1 安装 JDK3.2 Tomcat&#xff08;整的不行&#xff09; 四、Nginx4.1 安装 五、RabbitMQ5.1 安装 六、Redis6.1 安装 一、MySQL 5.7 1.1 安装 我…

python | linux | ModuleNotFoundError: No module named ‘WFlib‘ |找不到模块

问题&#xff1a; (base) beautyby521-7:~/Website-Fingerprinting-Library-master$ bash scripts/NetCLR.sh Traceback (most recent call last):File "/home/beauty/Website-Fingerprinting-Library-master/exp/pretrain.py", line 8, in <module>from WFli…

Maven 打包(system jar 和微服务父子项目)

jar包使用system 请务必减少使用system的频率&#xff0c;除非这个jar包中央仓库中没有 在文末的build标签下写入 < includeSystemScope >true</ includeSystemScope>即可 <?xml version"1.0" encoding"UTF-8"?> <project xmlns&…

AI Agent:重塑业务流程自动化的未来力量(2/30)

《AI Agent&#xff1a;重塑业务流程自动化的未来力量》 摘要&#xff1a;整体思路是先介绍 AI Agent 的基本情况&#xff0c;再深入阐述其实现业务流程自动化的方法和在不同领域的应用&#xff0c;接着分析其价值和面临的挑战&#xff0c;最后得出结论&#xff0c;为读者全面…

深入解析:选择最适合你的Whisper语音识别模型

在语音识别领域&#xff0c;Whisper系列模型因其卓越的性能和多语言支持而备受青睐。今天&#xff0c;我们将详细解析三种不同的Whisper模型Whisper-large-v3、Belle-whisper-large-v3-zh以及Whisper-large-v3-turbo&#xff0c;帮助你根据具体需求选择最合适的版本。 一、Whi…

【微服务】SpringBoot 国际化适配方案使用详解

目录 一、前言 二、国际化概述 2.1 微服务中的国际化是什么 2.1.1 国际化概念 2.1.2 为什么需要国际化 2.2 微服务中常用的国际化方法 2.2.1 资源文件分离 2.2.2 使用国际化框架 2.2.3 使用动态模板 2.2.4 使用数据库存储 2.2.5 API设计结合配置中心 三、SpringBoot…

海量数据库使用操作

海量数据库-vastbase G100使用操作 文章目录 海量数据库-vastbase G100使用操作使用vsql本地连接数据库创建数据库功能描述 创建表管理表向表中插入数据更新表中数据 查看数据删除表中数据示例查看帮助信息查看数据库列举表查看表结构列举schema查看索引切换数据库 总结 使用vs…

什么是运算符重载?如何在 C++ 中进行运算符重载?运算符重载在面向对象编程中的好处是什么?

1) 什么是运算符重载&#xff1f;如何在 C 中进行运算符重载&#xff1f; 运算符重载是指为类&#xff08;或结构体&#xff09;中的运算符赋予新的意义&#xff0c;使其能够用于自定义类型&#xff08;如类对象&#xff09;的操作。C 允许用户通过定义特定的成员函数或友元函…

机器学习之 K-Means

1. 什么是 K-Means&#xff1f; K-Means 是一种常用的无监督学习算法&#xff0c;主要用于聚类分析任务。其目标是将数据集划分为 K 个不同的簇&#xff0c;使得同一簇内的数据点之间的相似性更高&#xff0c;而不同簇的数据点差异更大。K-Means 算法使用了一个简单而有效的迭…

上传文件时获取音视频文件时长和文本文件字数

获取音视频文件时长和文本文件字数 一、获取音视频文件时长二、计算文本文件字数 最近有个需求&#xff0c;要求上传文件时获取音视频文件时长和文本文件字数&#x1f436;。 发现这样的冷门资料不多&#xff0c;特做个记录。本文忽略文件上传功能&#xff0c;只封装核心的工具…

【AIGC】与模型对话:理解与预防ChatGPT中的常见误解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;模型的工作原理和用户期望差异人工智能模型的基本工作原理认知上的局限与误解用户期望与模型实际能力的差距精确理解用户意图的重要性实际应用中的建议 &…

AI视频配音技术创新应用与商业机遇

随着人工智能技术的飞速发展&#xff0c;AI视频配音技术已经成为内容创作者和营销人员的新宠。这项技术不仅能够提升视频内容的吸引力&#xff0c;还能为特定行业带来创新的解决方案。本文将探讨AI视频配音技术的应用场景&#xff0c;并讨论如何合法合规地利用这一技术。 AI视频…

信创改造-Spring Boot 项目部署至 TongWeb

打 war 包参考&#xff1a;https://blog.csdn.net/z1353095373/article/details/144330999

Htpp中web通讯发送post(上传文件)、get请求

一、正常发送post请求 1、引入pom文件 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5</version></dependency>2、这个是发送至正常的post、get请求 import org…