[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 在渲染管线中所处的位置如下图所示:
2. 使用 Transform feedback 可实现的功能
- 实现粒子系统:
在粒子系统中,Transform Feedback 可以用来捕获粒子的状态信息(如位置、速度、颜色等),然后将这些数据存储到缓冲区中。在每一帧中,系统可以通过 Transform Feedback 来更新粒子的状态,从而避免在CPU中重新计算整个粒子系统。这种方式能有效地避免 CPU 参与数据更新,提高系统的性能。 - 进行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. 步骤
- 初始化 glfw, glad, 窗口
- 构建,编译 vertex shader
- 指定 Transform feedback object 的接收变量
- 链接 shader program
- 准备输入数据, VAO 和 VBO
- 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据
- 运行 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 - 将 Transform feedback buffer 中的数据传输到 CPU
- 释放资源
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. 步骤
- 初始化 glfw, glad, 窗口
- 构建,编译 vertex shader , geometry shader
- 指定 Transform feedback object 的接收变量
- 链接 shader program
- 准备输入数据, VAO 和 VBO
- 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据
- 运行 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 - 将 Transform feedback buffer 中的数据传输到 CPU
- 释放资源
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
相关文章:
[OpenGL] Transform feedback 介绍以及使用示例
一、简介 本文介绍了 OpenGL 中 Transform Feedback 方法的基本概念和代码示例。 二、Transform Feedback 介绍 1. Transform Feedback 简介 根据 OpenGL-wiki,Transform Feedback 是捕获由顶点处理步骤(vertex shader 和 geometry shader࿰…...
pytorch_fid 安装笔记
目录 torch安装: pytorch_fid安装 torch安装: pip install torch2.5.0 --index-url https://download.pytorch.org/whl/cu121 pytorch_fid安装 pip install pytorch_fid 安装后,torch也会自动安装,导致torch引用报错。...
SAM大模型实践(一)
参考着segment-geospatial 项目主页的介绍,尝试复现一下Example-satallite的案例。 Satellite - segment-geospatialhttps://samgeo.gishub.org/examples/satellite/ 过程当中遇到了一些坑给大家做点分享,主要有几种情况,一个是torch…...
数据结构 ——前缀树查词典的实现
数据结构 ——前缀树查词典的实现 一、前缀树的概念 前缀树是一种多叉树结构,主要用于存储字符串。每个节点代表一个字符,路径从根节点到叶节点表示一个完整的字符串。前缀树的关键特征是 共享前缀,也就是说,如果两个字符串有相…...
边缘智能创新应用大赛获奖作品系列一:智能边缘计算✖软硬件一体化,开启全场景效能革命新征程
边缘智能技术快速迭代,并与行业深度融合。它正重塑产业格局,催生新产品、新体验,带动终端需求增长。为促进边缘智能技术的进步与发展,拓展开发者的思路与能力,挖掘边缘智能应用的创新与潜能,高通技术公司联…...
修改ubuntu apt 源及apt 使用
视频教程:修改ubuntu apt 源和apt 使用方法_哔哩哔哩_bilibili 1 修改apt源 1.1 获取阿里云ubuntu apt 源 https://developer.aliyun.com/mirror/ubuntu?spma2c6h.13651102.0.0.3e221b11mqqLBC 1.2 修改apt 源 vim /etc/apt/sources.list deb https://mirrors.aliyun.com/ub…...
Kafka 磁道寻址过程详解
前言 Apache Kafka 是一款高吞吐、分布式的消息流平台,广泛应用于实时数据处理和事件驱动系统。在 Kafka 中,消息是存储在磁盘上的,这种高效的数据读写性能得益于 Kafka 独特的磁盘存储架构和寻址机制。本文将从 Kafka 的存储结构、磁道寻址…...
GEE+本地XGboot分类
GEE本地XGboot分类 我想做提取耕地提取,想到了一篇董金玮老师的一篇论文,这个论文是先提取的耕地,再做作物分类,耕地的提取代码是开源的。 但这个代码直接在云端上进行分类,GEE会爆内存,因此我准备把数据下…...
安防监控Liveweb视频汇聚融合平台助力执法记录仪高效使用
Liveweb平台可接入的设备除了常见的智能分析网关与摄像头以外 ,还可通过GB28181协议接入执法记录仪,实现对执法过程的全程监控与录像,并对执法轨迹与路径进行调阅回看。那么,如何做到执法记录仪高效使用呢? 由于执法记…...
酷盾安全:Edge SCDN边缘安全内容分发网络
在当今数字化迅猛发展的时代,互联网内容分发的高效与安全成为了企业不可忽视的重要课题。为了满足这一需求,酷盾安全推出了创新的Edge Secure Content Delivery Network(Edge Scdn)解决方案,它不仅融合了分布式DDoS防护…...
决策引擎技术
决策引擎(Decision Engine)是一种用于自动化决策过程的软件系统。它通常用于处理复杂的业务逻辑,根据输入的数据和预定义的规则或模型来做出决策。决策引擎在许多领域都有广泛的应用,如金融、保险、医疗、供应链管理等。 在Java中…...
Servlet学习中遇到的一些问题及解决
错误:JavaWeb-错误:类xxx不是Servlet 解决:可能是Tomcat版本不匹配导致,更换Tomcat版本解决问题 错误:在自定义的Servlet类中不能添加 WebServlet 注解 解决:可能是WebServlet版本不匹配,更换…...
oracle开窗函数笔记、over()笔记
文章目录 开窗函数、组函数、分析函数概念聚合函数和分析函数的区别partition by后面也可以跟多个字段 开窗函数一定要加 聚合函数、或分析函数吗,否则会报错lag()和lead()的用法lag和lead实战开窗函数可以和其他函数一起使用吗? TODO开窗函数中的count(1)是什么意…...
深度学习面试相关-2024.12.15记录
深度学习 面试相关- 2024.12.15记录 目录 深度学习 面试相关- 2024.12.15记录整体常问问题1数学基础1.1 概率统计1.2 线代 2机器学习算法2.1 深度学习算法2.2 机器学习算法 整体常问问题 https://www.nowcoder.com/discuss/353154899112304640 1数学基础 1.1 概率统计 htt…...
CSS|07 标准文档流
标准文档流 一、什么是标准文档流 在制作的 HTML 网页和 PS 画图软件画图时有本质上面的区别: HTML 网页在制作的时候都得遵循一个“流的规则:从左至右、从上至下。 使用 Ps 软件画图时可以在任意地方画图。 <!DOCTYPE html> <html lang"en"> <hea…...
1 JVM JDK JRE之间的区别以及使用字节码的好处
JDK jdk是编译java源文件成class文件的,我们使用javac命令把java源文件编译成class文件。 我们在java安装的目录下找到bin文件夹,如下图所示: 遵循着编译原理,把java源文件编译成JVM可识别的机器码。 其中还包括jar打包工具等。主要是针对…...
ubuntu安装8812au驱动却无法加载网卡的问题
驱动GIT地址 https://github.com/aircrack-ng/rtl8812au按照里面提示安装驱动 输入 sudo dkms status查看驱动是否安装成功 接入网卡,看看ifconfig能否输出网卡 如果不行 使用sudo dmesg -w插拔网卡看看输出 如果输出为: load module with unavailable key is …...
Eureka学习笔记-服务端
Eureka学习笔记 服务端 模块设计 Resources :这部分对外暴露了一系列的 Restful 接口。Eureka Client 的注册、心跳、获取服务列表等操作都需要调用这些接口。另外,其他的 Server 在同步 Registry 时也需要调用这些接口。Controller :这里提…...
LangChain
文章目录 一、LangChain 是什么?二、核心概念1. LLM Wrappers2. Prompt Templates3. Indexes4. Chains5. Agents 三、工作流程四、应用场景示例一:简单的语言模型调用示例二:使用Prompt Templates(提示模板)示例三&…...
搭建分布式Hive集群
title: 搭建分布式Hive集群 date: 2024-11-29 23:39:00 categories: - 服务器 tags: - Hive - 大数据搭建分布式Hive集群 本次实验环境:Centos 7-2009、Hadoop-3.1.4、JDK 8、Zookeeper-3.6.3、Mysql-5.7.38、Hive-3.1.2 功能规划 方案一(本地运行模…...
Scala的惰性求值:深入理解与实践
在编程中,我们经常需要处理那些计算成本高昂或者可能永远不会用到的值。在这种情况下,惰性求值(Lazy Evaluation)是一种非常有用的策略。它允许我们推迟计算,直到这些值真正需要被使用。Scala,作为一种多功…...
游戏引擎学习第54天
仓库: https://gitee.com/mrxiao_com/2d_game 回顾 我们现在正专注于在游戏世界中放置小实体来代表所有的墙。这些实体围绕着世界的每个边缘。我们有活跃的实体,这些实体位于玩家的视野中,频繁更新,而那些离玩家较远的实体则以较低的频率运…...
QT绘制同心扇形
void ChartForm::paintEvent(QPaintEvent *) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 设置抗锯齿painter.save();// 设置无边框(不需要设置QPen,因为默认是不绘制边框的)QPen pen(Qt::NoPen);// QPen pen…...
梳理你的思路(从OOP到架构设计)_浅尝架构师的滋味02
目录 1、 App开发者的职责:买主提供需求知识,App开发者帮他写代码 撰写的代码 撰写代码,将装配(扩充)到 2、 从生活中体会 基於軟硬整合觀點“两种知识” 编辑 1、 App开发者的职责:买主提供需求知识,App开发者帮…...
使用VLC 搭建 RTSP 服务器
第一步:打开 VLC ,媒体--->流 第二步:添加一个选择本地的文件,然后点击选择"串流" 第三步:确认你选择的文件,然后点击下一个 第四步: 配置 选择的视频文件使用哪种 流输出…...
什么是大型语言模型
大型语言模型简介 大型语言模型 (LLM) 是一种深度学习算法,可以使用非常大的数据集识别、总结、翻译、预测和生成内容。 NVIDIA 开发者计划 想要了解有关 NIM 的更多信息?加入 NVIDIA 开发者计划,即可免费访问任何基础设施云、数据中心或个…...
游卡,科锐国际,蓝禾,汤臣倍健,顺丰,途游游戏25秋招内推
游卡,科锐国际,蓝禾,汤臣倍健,顺丰,途游游戏25秋招内推 ①科锐国际25届秋招补录 人力资源类岗位,补录城市:上海,苏州,锦州;全日制公办本科及以上 25届应届毕业…...
Linux -- 线程控制相关的函数
目录 pthread_create -- 创建线程 参数 返回值 代码 -- 不传 args: 编译时带 -lpthread 运行结果 为什么输出混杂? 如何证明两个线程属于同一个进程? 如何证明是两个执行流? 什么是LWP? 代码 -- 传 args&a…...
【Linux】Linux内核启动流程分析
Linux 内核的启动流程要比 uboot 复杂的多,涉及到的内容也更多,因此我们大致的了解一下Linux 内核的启动流程即可。 Linux启动流程 启动过程可以分为以下几个主要步骤: 1.引导加载程序(Bootloader)阶段 Linux 内核的…...
【uniapp蓝牙】基于native.js链接ble和非ble蓝牙
【uniapp蓝牙】基于native.js链接ble和非ble蓝牙 uniapp不是仅支持低功耗蓝牙(基础蓝牙通讯不支持),有些可能需要基础蓝牙。我现在同步我的手机蓝牙列表低功耗,基础蓝牙都支持 /*** author wzj* 通用蓝牙模块封装* 搜索 ble 和非…...
OpenGL ES 03 加载3张图片并做混合处理
OpenGL ES 02 加载3张图片并做混合处理 什么是纹理单元纹理单元的作用使用纹理单元的步骤详细解释加载图片并绑定到到GPU纹理单元采样器的设置1.设置采样器变量的纹理单元编号,目的是为了告诉纹理采样器,从哪个纹理单元采集数据2.如果你没有显式地设置采…...
c++数据结构算法复习基础--13--基数算法
基数排序 - 桶排序 时间复杂度 O(n*d) – d为数据的长度 每次比较一位(个位、十位。。。),所以取值范围就为0-9。 根据该特点,设计桶的概念 – 0号桶、1号桶… 1、思想 1)找出最长的数字,确定要处理的…...
基于YOLOv5的行人与帽子检测与识别说明文档
基于YOLOv5的行人与帽子检测与识别说明文档 1. 任务的内容和目标 1.1 任务目标 在计算机视觉领域,头盔检测至关重要,主要用于判定图像或视频里的人是否佩戴头盔。于工业生产、建筑工地、交通出行(如摩托车与自行车骑行)等高危场…...
Mybatis——(2)
2.2 Mybatis 工具类(了解) 为了简化MyBatis的开发,可将MyBatis进一步封装。 import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apa…...
QT笔记- QSystemTrayIcon系统托盘功能完整示例
1. 创建托盘对象 // 创建托盘图标QSystemTrayIcon * trayIcon new QSystemTrayIcon(this);QIcon icon("://icon/test.png");trayIcon->setIcon(icon);trayIcon->show();trayIcon->connect(trayIcon, &QSystemTrayIcon::activated,this, &MainWindo…...
ElasticSearch08-分析器详解
零、文章目录 ElasticSearch08-分析器详解 1、分析器原理 Elasticsearch的分词器(Analyzer)是全文搜索的核心组件,它负责将文本转换为一系列单词(term/token)的过程,也叫分词。 (1ÿ…...
指针的深入讲解
本章重点: 字符指针数组指针指针数组数组传参和指针传参函数指针函数指针数组指向函数指针数组的指针回调函数 我们在指针的初阶的时候主要讲了: 1.指针就是变量,用来存放地址,地址唯一标识一块内存空间 2.指针的大小是固定4个…...
王佩丰24节Excel学习笔记——第十二讲:match + index
【以 Excel2010 系列学习,用 Office LTSC 专业增强版 2021 实践】 【本章小技巧】 vlookup与match,index 相结合使用match,index 结合,快速取得引用的值扩展功能,使用match/index函数,结合照相机工具获取照片 一、回顾…...
概率论得学习和整理28:用EXCEL画折线图,X轴数据也被当成曲线的解决办法
目录 1 折线图和散点图,对数据的处理差别 1.1 EXCEL画图的一些默认设置 1.2 多于2列的数据,也是如此 2 如果我们非要以第1列数据为X轴,做一个折线图呢?也能 2.1 首先,把第1列,想当成X轴的数据…...
387. 字符串中的第一个唯一字符
1,题目 给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。 2,代码 class Solution { public:int firstUniqChar(string s) {//记数排序int coutArr[26] {0};//统计字符出现…...
Oracle RAC最佳实践-优化私网连接
在 Oracle RAC 环境中,私网(Interconnect) 是节点之间通信和数据传输的关键部分。一直有个误解,认为私网(心跳网)只要能通随便什么交换机都可以,甚至有直连的,实际上私网的性能至关重要…...
[bug] StarRocks borker load意向之外的bug
意向之外,又清理之中 背景: StarRocks各方面碾压相同类型的数据库,最近我们要从生成HIVE导历史数据(ORC格式)到StarRocks,前期小测一下,在测试是没问题,上生产先导2个月的数据&…...
游戏AI实现-寻路算法(Dijkstra)
戴克斯特拉算法(英语:Dijkstras algorithm),又称迪杰斯特拉算法、Dijkstra算法,是由荷兰计算机科学家艾兹赫尔戴克斯特拉在1956年发现的算法。 算法过程: 1.首先设置开始节点的成本值为0,并将…...
9 OOM和JVM退出。OOM后JVM一定会退出吗?
首先我们把两个概念讲清楚 OOM是线程在申请堆内存,发现堆内存空间不足时候抛出的异常。 JVM退出的条件如下: java虚拟机在没有守护线程的时候会退出。守护线程是启动JVM的线程,服务于用户线程。 我们简单说下守护线程的功能: 1.日志的记录…...
Linux 端口操作
安装netstat yum -y install net-tools 检测端口占用 netstat -npl | grep "端口" 安装lsof lsof yum -y install lsof 检测端口占用 lsof -i :端口号 安装nc yum -y install nc 查看对方端口是否开放 nc -vz 对方ip 对方端口 安装telnet telnet yum -y in…...
【USB-HID】“自动化键盘“ - 模拟键盘输入
目录 【USB-HID】"自动化键盘" - 模拟键盘输入1. 前言2. 模拟键盘2.1 STM32CubeMX 配置2.2 修改代码配置2.3 发送按键信息 3. 接收主机Setup数据3.1 获取PC下发的数据 4. 总结 【USB-HID】“自动化键盘” - 模拟键盘输入 1. 前言 对于模拟键盘的实现,网…...
基于Spring Boot+Vue 的高校运动会管理系统
目录 1 绪论1.1研究背景1.2 研究意义1.3 相关开发技术简介1.3.1 Vue.js1.3.2 Spring Boot1.3.3 MySQL 2 系统分析2.1 需求分析2.1.1 功能需求2.1.2 非功能需求 2.2 系统可行性分析2.2.1 经济可行性2.2.2 技术可行性2.2.3 操作可行性 3 系统概要设计系统功能描述业务流程分析 4 …...
Linux应用程序中终止进程的几种方法
目录 1、正常退出进程的方法 1.1、exit(int status) 函数 1.2、_exit(int status) 函数 1.3、_Exit(int status) 函数 2、异常退出进程的方法 3、何时使用这些方法? 在 Linux 应用程序中,终止进程的方式有多种,通常取决于进程是否需要进…...
电脑文档损坏:原因剖析和修复方法
在使用电脑的过程中,许多用户可能会遇到文档突然提示损坏、无法打开的情况。这种情况的发生往往让人感到困惑,特别是当并未进行任何明显错误操作时。以下是一些常见的原因以及应对方法。 一、文档损坏的常见原因 1、非人为的异常操作: 在编…...
了解ARM的千兆以太网——RK3588
1. 简介 本文并不重点讲解调试内容,重点了解以太网在ARM设计中的框架以及在设备树以及驱动的一个整体框架。了解作为一个驱动开发人员当拿到一款未开发过的ARM板卡应该怎么去把网卡配置使用起来。 2. 基础知识介绍 在嵌入式ARM中实现以太网的解决方案通常有以下两种…...