[OpenGL] Transform feedback 先容以及使用示例

瑞星  金牌会员 | 2024-12-15 17:55:32 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 858|帖子 858|积分 2574

一、简介

本文先容了 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. 代码

  1. #include <glad/glad.h>
  2. #include <GLFW/glfw3.h>
  3. #include <iostream>
  4. // 用于处理窗口大小改变的回调函数
  5. void framebuffer_size_callback(GLFWwindow *window, int width, int height);
  6. // 用于处理用户输入的函数
  7. void processInput(GLFWwindow *window);
  8. // 指定窗口默认width和height像素大小
  9. const unsigned int SCR_WIDTH = 800;
  10. const unsigned int SCR_HEIGHT = 600;
  11. /****** vertex shader 代码 ******/
  12. const char *vertexShaderSource = R"(
  13. #version 330 core
  14. layout (location = 0) in float inputValue;
  15. out float outputValue;
  16. void main()
  17. {
  18.     outputValue = sqrt(inputValue);
  19. }
  20. )";
  21. /************************************/
  22. int main()
  23. {
  24.     /****** 1.初始化glfw, glad, 窗口 *******/
  25.     // glfw 初始化 + 配置 glfw 参数
  26.     glfwInit();
  27.     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  28.     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  29.     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  30.     // glfw 生成窗口
  31.     // (由于我们只使用 Transform feedback 进行并行运算,因此实际上不生成窗口也可以)
  32.     GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
  33.     if (window == NULL)
  34.     {
  35.         // 检查是否成功生成窗口,如果没有成功打印出错信息并且退出
  36.         std::cout << "Failed to create GLFW window" << std::endl;
  37.         glfwTerminate();
  38.         return -1;
  39.     }
  40.     // 设置窗口window的上下文
  41.     glfwMakeContextCurrent(window);
  42.     // 配置window变化时的回调函数
  43.     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
  44.     // 使用 glad 加载 OpenGL 中的各种函数
  45.     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
  46.     {
  47.         std::cout << "Failed to initialize GLAD" << std::endl;
  48.         return -1;
  49.     }
  50.     /************************************/
  51.     /****** 2.构建,编译 vertex shader ******/
  52.     // vertex shader
  53.     unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
  54.     glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
  55.     glCompileShader(vertexShader);
  56.     // 检查是否成功编译 vertex shader
  57.     int success;
  58.     char infoLog[512];
  59.     glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
  60.     if (!success)
  61.     {
  62.         glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
  63.         std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
  64.     }
  65.     // 生成 shader 程序
  66.     unsigned int shaderProgram = glCreateProgram();
  67.     glAttachShader(shaderProgram, vertexShader);
  68.     /****** 3. 指定 Transform feedback object 的接收变量  ******/
  69.     // 该步骤必须在 glLinkProgram() 之前进行
  70.     // 因为使用 Transform feedback 会改变 shader 程序的渲染流程,因此需要先指定 Transform feedback 的接收变量,告诉
  71.     // OpenGL 应该如何链接、编译 shader 程序,再进行 glLinkProgram()
  72.     const char *feedbackVaryings[] = {"outputValue"}; // 我们希望接收 vertex shader 的输出变量 out float outputValue
  73.     // glTransformFeedbackVaryings() 函数的参数解释:
  74.     // (1). 目标 shader program
  75.     // (2). 目标接收变量的个数,即 feedbackVaryings 数组中 字符串的个数
  76.     // (3). 目标接收的变量名字符串数组
  77.     // (4). 接收变量的存储目标. GL_INTERLEAVED_ATTRIBS, 表示所有输出变量都输出到 一个 buffer 中,
  78.     // GL_SEPARATE_ATTRIBS 表示 输出变量输出到不同的 buffer 中. 此处我们选择将输出存储到 一个 buffer 中
  79.     glTransformFeedbackVaryings(shaderProgram, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
  80.     /************************************/
  81.     /****** 4. 链接 shader program  ******/
  82.     glLinkProgram(shaderProgram); // 链接 shader program
  83.     // 检查是否成功链接 vertex shader 和 fragment shader
  84.     glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
  85.     if (!success)
  86.     {
  87.         glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
  88.         std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
  89.     }
  90.     glDeleteShader(vertexShader);
  91.     /************************************/
  92.     /****** 5.准备输入数据, VAO 和 VBO *******/
  93.     float inputData[] = {0.0f, 0.5f, 1.0f};
  94.     unsigned int VBO, VAO;
  95.     glGenVertexArrays(1, &VAO); // 生成一个VAO对象
  96.     glGenBuffers(1, &VBO);      // 生成一个VBO对象
  97.     glBindVertexArray(VAO); // 绑定VAO
  98.     glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBO
  99.     glBufferData(GL_ARRAY_BUFFER, sizeof(inputData), inputData,
  100.                  GL_STATIC_DRAW); // 将vertices中的数据复制到刚刚绑定的VBO buffer中去,VBO buffer是GPU内存上的一块区域
  101.     // glVertexAttribPointer() 用于指定顶点属性如何从绑定的 VBO 中读取数据,
  102.     // 此时绑定的 buffer 是 VBO,因此location=0 处的顶点属性就从VBO中读数据 glVertexAttribPointer()
  103.     // 需要6个参数,每个参数的含义如下:
  104.     // (1). 指定要配置的顶点属性, 即 shader 中指定 数据 location 值,对于position部分,此处填入 0
  105.     // (2). 指定顶点属性的大小,shader中我们将VBO中的数据传给了一个 float 类型的变量,
  106.     // 每个顶点数据只有一个 float, 因此此处填 1
  107.     // (3). 指定数据的类型,用于使用的是浮点型,因此此处填入 GL_FLOAT
  108.     // (4). 指定是否自动对数据进行归一化,我们不需要自动诡异化,因此此处填入 GL_FALSE
  109.     // (5). 指定VBO中顶点数据组之间的间隔,可以手动设置间隔,例如 sizeof(float),
  110.     // 也可以填 0 让 Opengl 自动设置
  111.     // (6). 指定读取VBO数据起始位置偏移,单位为字节,这里填入 (void *)(0*sizeof(float)) 即可
  112.     glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0,
  113.                           (void *)(0 * sizeof(float))); // 设置如何读取VBO中数据
  114.     glEnableVertexAttribArray(0);                       // 启用 location = 0 处的顶点属性
  115.     glBindBuffer(GL_ARRAY_BUFFER, 0);                   // 解绑VBO
  116.     /************************************/
  117.     /****** 6. 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据  ******/
  118.     // 新建一个 buffer 用于作为 Transform feedback object 的 buffer,用来接收 计算后的输出结果
  119.     GLuint tbo;
  120.     glGenBuffers(1, &tbo);
  121.     glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo);
  122.     glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(inputData), nullptr, GL_STATIC_READ);
  123.     // 将 tbo 绑定到 TFO 的 0号卡槽上, 此处不对卡槽机制进行过多的讲解
  124.     glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);
  125.     /************************************/
  126.     /****** 7. 运行 shader 程序  ******/
  127.         // 7.1 use shader program
  128.     glUseProgram(shaderProgram);
  129.     // 关闭 光栅化阶段, 只使用 vertex shader 和 geometry shader (如果有的话) 阶段
  130.     // 7.2 disable rasterizer
  131.     glEnable(GL_RASTERIZER_DISCARD);
  132.     // 开始绘制 Transform feedback
  133.     // 此处只有 vertex shader,因此 glBeginTransformFeedback() 中指定的 图元 需要跟 glDrawArray() 中的相同
  134.         // 7.3 begin transform feedback
  135.     glBeginTransformFeedback(GL_POINTS);
  136.     // 7.4 bind VAO
  137.     glBindVertexArray(VAO); // 绑定VAO,指定当前使用的VAO对象
  138.         // 7.5 draw
  139.     glDrawArrays(GL_POINTS, 0,
  140.                  3); // 输入数据数据只有三个 float,因此可以将每个数据分配给一个 point ,在 vertex shader 中进行计算处理
  141.         // 7.6 end transform feedback
  142.     glEndTransformFeedback(); // 结束 Transform feedback
  143.     // 开启 光栅化阶段
  144.     glDisable(GL_RASTERIZER_DISCARD);
  145.     glBindVertexArray(0); // 解绑 VAO
  146.     glFlush(); // 刷新 gl 命令 buffer 保证前面的命令都运行完成
  147.     /************************************/
  148.    
  149.     /****** 8. 将 Transform feedback buffer 中的数据传输到 CPU   ******/
  150.     // 接收 Transform feedback 对应的 buffer (tbo) 中的数据
  151.     GLfloat feedback[3];
  152.     glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
  153.     for (int i = 0; i < 3; i++)
  154.     {
  155.         printf("%f\n", feedback[i]);
  156.     }
  157.     /************************************/
  158.     /****** 9.释放资源 ******/
  159.     // 释放之前申请的 VBO, VAO 资源和 shader 程序
  160.     glDeleteVertexArrays(1, &VAO);
  161.     glDeleteBuffers(1, &VBO);
  162.     glDeleteProgram(shaderProgram);
  163.     // glfw 释放 glfw使用的所有资源
  164.     glfwTerminate();
  165.     /************************************/
  166.     return 0;
  167. }
  168. // 用于处理用户输入的函数
  169. void processInput(GLFWwindow *window)
  170. {
  171.     // 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口
  172.     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
  173.         glfwSetWindowShouldClose(window, true);
  174. }
  175. // 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
  176. // 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
  177. void framebuffer_size_callback(GLFWwindow *window, int width, int height)
  178. {
  179.     glViewport(0, 0, width, height);
  180. }
复制代码
1.3. 运行结果

  1. 0.000000
  2. 0.707107
  3. 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 代码

  1. #include <glad/glad.h>
  2. #include <GLFW/glfw3.h>
  3. #include <iostream>
  4. // 用于处理窗口大小改变的回调函数
  5. void framebuffer_size_callback(GLFWwindow *window, int width, int height);
  6. // 用于处理用户输入的函数
  7. void processInput(GLFWwindow *window);
  8. // 指定窗口默认width和height像素大小
  9. const unsigned int SCR_WIDTH = 800;
  10. const unsigned int SCR_HEIGHT = 600;
  11. /****** vertex shader 代码 ******/
  12. const char *vertexShaderSource = R"(
  13. #version 330 core
  14. layout (location = 0) in float inputValue;
  15. out float geoValue;
  16. void main()
  17. {
  18.     geoValue =  sqrt(inputValue);
  19. }
  20. )";
  21. /****** geometry shader 代码 ******/
  22. const char *geometryShaderSource = R"(
  23. #version 330 core
  24. layout (points) in;
  25. layout (triangle_strip, max_vertices=3) out;
  26. in float[] geoValue;
  27. out float outputValue;
  28. void main()
  29. {
  30.     for(int i=0; i<3; i++){
  31.         outputValue = geoValue[0] + i;
  32.         EmitVertex();
  33.     }
  34.     EndPrimitive();
  35. }
  36. )";
  37. /************************************/
  38. int main()
  39. {
  40.     /****** 1.初始化glfw, glad, 窗口 *******/
  41.     // glfw 初始化 + 配置 glfw 参数
  42.     glfwInit();
  43.     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  44.     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  45.     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  46.     // glfw 生成窗口
  47.     // (由于我们只使用 Transform feedback 进行并行运算,因此实际上不生成窗口也可以)
  48.     GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
  49.     if (window == NULL)
  50.     {
  51.         // 检查是否成功生成窗口,如果没有成功打印出错信息并且退出
  52.         std::cout << "Failed to create GLFW window" << std::endl;
  53.         glfwTerminate();
  54.         return -1;
  55.     }
  56.     // 设置窗口window的上下文
  57.     glfwMakeContextCurrent(window);
  58.     // 配置window变化时的回调函数
  59.     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
  60.     // 使用 glad 加载 OpenGL 中的各种函数
  61.     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
  62.     {
  63.         std::cout << "Failed to initialize GLAD" << std::endl;
  64.         return -1;
  65.     }
  66.     /************************************/
  67.     /****** 2.构建,编译 vertex shader, geometry shader ******/
  68.     // vertex shader
  69.     unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
  70.     glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
  71.     glCompileShader(vertexShader);
  72.     // 检查是否成功编译 vertex shader
  73.     int success;
  74.     char infoLog[512];
  75.     glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
  76.     if (!success)
  77.     {
  78.         glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
  79.         std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
  80.     }
  81.     // geometry shader
  82.     unsigned int geometryShader = glCreateShader(GL_GEOMETRY_SHADER);
  83.     glShaderSource(geometryShader, 1, &geometryShaderSource, NULL);
  84.     glCompileShader(geometryShader);
  85.     // 检查是否成功编译 geometry shader
  86.     glGetShaderiv(geometryShader, GL_COMPILE_STATUS, &success);
  87.     if (!success)
  88.     {
  89.         glGetShaderInfoLog(geometryShader, 512, NULL, infoLog);
  90.         std::cout << "ERROR::SHADER::GEOMETRY::COMPILATION_FAILED\n" << infoLog << std::endl;
  91.     }
  92.     // 生成 shader 程序
  93.     unsigned int shaderProgram = glCreateProgram();
  94.     glAttachShader(shaderProgram, vertexShader);
  95.     glAttachShader(shaderProgram, geometryShader);
  96.     /****** 3. 指定 Transform feedback object 的接收变量  ******/
  97.     // 该步骤必须在 glLinkProgram() 之前进行
  98.     // 因为使用 Transform feedback 会改变 shader 程序的渲染流程,因此需要先指定 Transform feedback 的接收变量,告诉
  99.     // OpenGL 应该如何链接、编译 shader 程序,再进行 glLinkProgram()
  100.     const char *feedbackVaryings[] = {"outputValue"}; // 我们希望接收 vertex shader 的输出变量 out float outputValue
  101.     // glTransformFeedbackVaryings() 函数的参数解释:
  102.     // (1). 目标 shader program
  103.     // (2). 目标接收变量的个数,即 feedbackVaryings 数组中 字符串的个数
  104.     // (3). 目标接收的变量名字符串数组
  105.     // (4). 接收变量的存储目标. GL_INTERLEAVED_ATTRIBS, 表示所有输出变量都输出到 一个 buffer 中,
  106.     // GL_SEPARATE_ATTRIBS 表示 输出变量输出到不同的 buffer 中. 此处我们选择将输出存储到 一个 buffer 中
  107.     glTransformFeedbackVaryings(shaderProgram, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
  108.     /************************************/
  109.     /****** 4. 链接 shader program  ******/
  110.     glLinkProgram(shaderProgram); // 链接 shader program
  111.     // 检查是否成功链接 vertex shader 和 fragment shader
  112.     glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
  113.     if (!success)
  114.     {
  115.         glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
  116.         std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
  117.     }
  118.     glDeleteShader(vertexShader);
  119.     glDeleteShader(geometryShader);
  120.     /************************************/
  121.     /****** 5.准备输入数据, VAO 和 VBO *******/
  122.     float inputData[] = {0.0f, 0.5f, 1.0f};
  123.     unsigned int VBO, VAO;
  124.     glGenVertexArrays(1, &VAO); // 生成一个VAO对象
  125.     glGenBuffers(1, &VBO);      // 生成一个VBO对象
  126.     glBindVertexArray(VAO); // 绑定VAO
  127.     glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBO
  128.     glBufferData(GL_ARRAY_BUFFER, sizeof(inputData), inputData,
  129.                  GL_STATIC_DRAW); // 将vertices中的数据复制到刚刚绑定的VBO buffer中去,VBO buffer是GPU内存上的一块区域
  130.     // glVertexAttribPointer() 用于指定顶点属性如何从绑定的 VBO 中读取数据,
  131.     // 此时绑定的 buffer 是 VBO,因此location=0 处的顶点属性就从VBO中读数据 glVertexAttribPointer()
  132.     // 需要6个参数,每个参数的含义如下:
  133.     // (1). 指定要配置的顶点属性, 即 shader 中指定 数据 location 值,对于position部分,此处填入 0
  134.     // (2). 指定顶点属性的大小,shader中我们将VBO中的数据传给了一个 float 类型的变量,
  135.     // 每个顶点数据只有一个 float, 因此此处填 1
  136.     // (3). 指定数据的类型,用于使用的是浮点型,因此此处填入 GL_FLOAT
  137.     // (4). 指定是否自动对数据进行归一化,我们不需要自动诡异化,因此此处填入 GL_FALSE
  138.     // (5). 指定VBO中顶点数据组之间的间隔,可以手动设置间隔,例如 sizeof(float),
  139.     // 也可以填 0 让 Opengl 自动设置
  140.     // (6). 指定读取VBO数据起始位置偏移,单位为字节,这里填入 (void *)(0*sizeof(float)) 即可
  141.     glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0,
  142.                           (void *)(0 * sizeof(float))); // 设置如何读取VBO中数据
  143.     glEnableVertexAttribArray(0);                       // 启用 location = 0 处的顶点属性
  144.     glBindBuffer(GL_ARRAY_BUFFER, 0);                   // 解绑VBO
  145.     /************************************/
  146.     /****** 6. 设置绑定到 Transform feedback object 上的 buffer,用于接收输出数据  ******/
  147.     // 新建一个 buffer 用于作为 Transform feedback object 的 buffer,用来接收 计算后的输出结果
  148.     GLuint tbo;
  149.     glGenBuffers(1, &tbo);
  150.     glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo);
  151.     // 注意!!! 由于我们使用 geometry shader 将一个输入变量(geoValue)变为三个输出(outputValue),
  152.     // 因此 Transform feedback object 中用于接收数据的 buffer 大小应该为输入数据的 3 倍大
  153.     glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(inputData) * 3, nullptr, GL_STATIC_READ);
  154.     // 将 tbo 绑定到 TFO 的 0号卡槽上, 此处不对卡槽机制进行过多的讲解
  155.     glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);
  156.     /************************************/
  157.     /****** 7. 运行 shader 程序  ******/
  158.     // 7.1 use shader program
  159.     glUseProgram(shaderProgram);
  160.     // 7.2 disable rasterizer
  161.     // 关闭 光栅化阶段, 只使用 vertex shader 和 geometry shader (如果有的话) 阶段
  162.     glEnable(GL_RASTERIZER_DISCARD);
  163.     // 7.3 begin transform feedback
  164.     // 开始绘制 Transform feedback
  165.     // 注意!!! 由于此时使用了 geometry shader, glBeginTransformFeedback() 中的图元应该与 geometry shader 的输出一致,
  166.     // 尽管 geometry shader 中的 out 图元为 triangle_strip,
  167.     // 但是 transform feedback 依旧独立地存储每个 triangle 的顶点数据,
  168.     // 而不是使用 strip 对数据进行压缩
  169.     glBeginTransformFeedback(GL_TRIANGLES);
  170.     // 7.4 bind VAO
  171.     glBindVertexArray(VAO); // 绑定VAO,指定当前使用的VAO对象
  172.     // 7.5 draw
  173.     glDrawArrays(GL_POINTS, 0,
  174.                  3); // 输入数据数据只有三个 float,因此可以将每个数据分配给一个 point ,在 vertex shader 中进行计算处理
  175.     // 7.6 end transform feedback
  176.     glEndTransformFeedback(); // 结束 Transform feedback
  177.     // 开启 光栅化阶段
  178.     glDisable(GL_RASTERIZER_DISCARD);
  179.     glBindVertexArray(0); // 解绑 VAO
  180.     glFlush();            // 刷新 gl 命令 buffer 保证前面的命令都运行完成
  181.     /************************************/
  182.     /****** 8. 将 Transform feedback buffer 中的数据传输到 CPU   ******/
  183.     // 接收 Transform feedback 对应的 buffer (tbo) 中的数据
  184.     GLfloat feedback[9];
  185.     glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
  186.     for (int i = 0; i < 9; i++)
  187.     {
  188.         printf("%f\n", feedback[i]);
  189.     }
  190.     /************************************/
  191.     /****** 9.释放资源 ******/
  192.     // 释放之前申请的 VBO, VAO 资源和 shader 程序
  193.     glDeleteVertexArrays(1, &VAO);
  194.     glDeleteBuffers(1, &VBO);
  195.     glDeleteProgram(shaderProgram);
  196.     // glfw 释放 glfw使用的所有资源
  197.     glfwTerminate();
  198.     /************************************/
  199.     return 0;
  200. }
  201. // 用于处理用户输入的函数
  202. void processInput(GLFWwindow *window)
  203. {
  204.     // 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口
  205.     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
  206.         glfwSetWindowShouldClose(window, true);
  207. }
  208. // 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
  209. // 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
  210. void framebuffer_size_callback(GLFWwindow *window, int width, int height)
  211. {
  212.     glViewport(0, 0, width, height);
  213. }
复制代码
2.3 结果

  1. 0.000000
  2. 1.000000
  3. 2.000000
  4. 0.707107
  5. 1.707107
  6. 2.707107
  7. 1.000000
  8. 2.000000
  9. 3.000000
复制代码
四、参考

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

瑞星

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表