zoukankan      html  css  js  c++  java
  • OpenGL-Transform Feedback

     

    Transform Feedback主要作用是获取顶点着色器或者几何着色器输出的数据,并将结果存储到一个或多个的缓冲对象里,主要用于粒子系统中。

    以光栅化为界限划分frond end和back end两个阶段,该过程位于front end的最后一个阶段。

    粒子系统:

    粒子系统是为了模仿一些自然现象烟、灰尘、烟火、雨等所使用的技术,这些自然现象均由大量的小粒子组成,并以某种方式在一起移动。

    为了模仿一个由粒子组成的自然现象,通常需要维护一个粒子的位置信息和一些其他的属性速率、颜色等等,并且在每一帧中执行下面的步骤:

    (1) 更新每个粒子的属性,根据模拟现象的复杂度,计算可能简单也可能困难;

    (2) 渲染粒子;

    第一步更新粒子属性时发生在 CPU,应用程序访问顶点缓冲区就可以获得或者更新每个粒子的属性。第二步渲染发生在 GPU。

    这样就会存在两个问题:

    (1) 在CPU中更新粒子属性,要求GL驱动程序从GPU显存中拷贝顶点缓冲区的数据到CPU内存。如果粒子数量巨大,且帧率要求较高,会对程序的性能产生很大的影响;

    (2) 更新粒子属性意味着需要在不同的数据上进行相同的数学计算,如果将这个过程在GPU上进行,会更好的发挥GPU的优势;

    Transform Feedback:

    OpenGL在3.0版本之后加入了新特性即Transform Feedback,主要的实现思路是在VS/GS处理之后,捕获即将装配为图元(点,线段,三角形)的顶点,将部分或者全部属性存放到一个特殊的缓存Transform Feedback Buffer中。

    根据这个特性,它和顶点着色器可以组合成一个渲染管线,是否进行后续的光栅化处理由我们决定,这样一些通用的计算可以利用GPU强大的计算能力去做。同时,在下一帧渲染的时候,上一帧中输出的顶点信息可以在这一帧中作为顶点缓存使用,在这样的一个循环过程中,我们可以不借助于应用程序来实现对粒子信息的更新。

    下面这幅图片介绍了Transform Feedback Buffer在管线中所处的位置:

     

    如果没有使用GS,在调用绘制函数时传入的顶点数量的参数就是Transform Feedback缓存中图元的个数。

    如果GS存在,因为GS能够创建和删除图元,那么图元的数量是未知的,所以Transform Feedback缓存中的图元数也就不确定。

    不知道缓存里包含多少顶点,使用缓存中的数据进行绘图就会不明确,为了克服这个困难,Transform Feedback提供了一个新的绘图函数,这个函数不需要使用顶点的个数作为参数。系统会自动的为每一个缓存计算顶点数。当缓存被用来作为输入时,系统会自动使用之前计算出来的顶点数。如果多次将数据输入到Transform Feedback缓存中,那么顶点数也会相应的增加。

    需要注意的是,在同一个绘制过程中,相同的缓存不能同时作为输入和输出。如果想要在一个顶点缓冲区中更新粒子,需要两个buffer 并进行切换。在第1帧的时候在buffer A中更新粒子并从buffer B中渲染粒子,在第2帧的时候在buffer B中更新粒子并从buffer A中渲染粒子。

    示例:

    相关API:

    //创建Transform Feedback对象
    
    GLuint glGenTransformFeedbacks(GLenum target,  GLuint *id)
    
    //绑定
    
    GLuint glBindTransformFeedback(GLenum target,  GLuint id)
    
    //删除对象
    
    GLuint glDeleteTransformFeedbacks(GLsizei n, const GLuint* ids)
    
    //缓存的绑定
    
    void glBindBufferBase(GLenum target, GLint index, GLuint buffer)
    
    //部分缓存的绑定
    
    void glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)
    
    //设置缓存记录的变量
    
    void glTransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar **varyings, GLenum bufferMode)
    
    //启动
    
    void glBeginTransformFeedback(GLenum primitiveMode)
    
    //暂停
    
    void glPauseTransformFeedback(void)
    
    //重新启动
    
    void glResumeTransformFeedback(void)
    
    //停止
    
    void glEndTransformFeedback(void)
    View Code

    示例代码:

    // Link statically with GLEW
    #define GLEW_STATIC
    
    #include <GL/glew.h>
    #include <SFML/Window.hpp>
    
    // Vertex shader
    const GLchar* vertexShaderSrc = R"glsl(
        #version 150 core
        in float inValue;
        out float geoValue;
        void main()
        {
            geoValue = sqrt(inValue);
        }
    )glsl";
    
    // Geometry shader
    const GLchar* geoShaderSrc = R"glsl(
        #version 150 core
        layout(points) in;
        layout(triangle_strip, max_vertices = 3) out;
        in float[] geoValue;
        out float outValue;
        void main()
        {
            for (int i = 0; i < 3; i++) {
                outValue = geoValue[0] + i;
                EmitVertex();
            }
            EndPrimitive();
        }
    )glsl";
    
    int main()
    {
        // SFML window configuration
        sf::ContextSettings settings;
        settings.depthBits = 24;
        settings.stencilBits = 8;
        sf::Window window(sf::VideoMode(800, 600, 32), "Transform Feedback", sf::Style::Titlebar | sf::Style::Close, settings);
    
        // Initialize GLEW
        glewExperimental = GL_TRUE;
        glewInit();
    
        // Compile shaders
        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSrc, nullptr);
        glCompileShader(vertexShader);
    
        GLuint geoShader = glCreateShader(GL_GEOMETRY_SHADER);
        glShaderSource(geoShader, 1, &geoShaderSrc, nullptr);
        glCompileShader(geoShader);
    
        // Create program and specify transform feedback variables
        GLuint program = glCreateProgram();
        glAttachShader(program, vertexShader);
        glAttachShader(program, geoShader);
    
        // GL_INTERLEAVED_ATTRIBS: write all attributes to a buffer object
        // GL_SEPARATE_ATTRIBS: write attributes to multiple buffer objects
        const GLchar* feedbackVaryings[] = { "outValue" };
        glTransformFeedbackVaryings(program, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
    
        glLinkProgram(program);
        glUseProgram(program);
    
        // Create VAO
        GLuint vao;
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
    
        // Create input VBO and vertex format
        GLfloat data[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
        GLuint vbo;
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
    
        GLint inputAttrib = glGetAttribLocation(program, "inValue");
        glEnableVertexAttribArray(inputAttrib);
        glVertexAttribPointer(inputAttrib, 1, GL_FLOAT, GL_FALSE, 0, 0);
    
        // Create transform feedback buffer
        GLuint tbo;
        glGenBuffers(1, &tbo);
        glBindBuffer(GL_ARRAY_BUFFER, tbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(data) * 3, nullptr, GL_STATIC_READ);
    
        // Create query object to collect info
        GLuint query;
        glGenQueries(1, &query);
    
        // Perform feedback transform
        glEnable(GL_RASTERIZER_DISCARD);
    
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);
        glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query);
        glBeginTransformFeedback(GL_TRIANGLES);
        glDrawArrays(GL_POINTS, 0, 5);
        glEndTransformFeedback();
        glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
    
        glDisable(GL_RASTERIZER_DISCARD);
    
        glFlush();
    
        // Fetch and print results
        GLuint primitives;
        glGetQueryObjectuiv(query, GL_QUERY_RESULT, &primitives);
    
        GLfloat feedback[15];
        glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
    
        // It returns the number of primitives, not the number of vertices. 
        printf("%u primitives written!
    
    ", primitives);
    
        for (int i = 0; i < 15; i++) {
            printf("%f
    ", feedback[i]);
        }
    
        glDeleteQueries(1, &query);
    
        glDeleteProgram(program);
        glDeleteShader(geoShader);
        glDeleteShader(vertexShader);
    
        glDeleteBuffers(1, &tbo);
        glDeleteBuffers(1, &vbo);
    
        glDeleteVertexArrays(1, &vao);
    
        window.close();
    
        return 0;
    }
    View Code
    关山难越,谁悲失路之人; 萍水相逢,尽是他乡之客。
  • 相关阅读:
    java 自定义线程池
    java 锁
    volatile相关内容
    消息驱动式微服务:Spring Cloud Stream & RabbitMQ
    JVM中的本机内存跟踪
    性能监控: SPF4J介绍
    Spring Batch 入门级示例教程
    使用 Spring Boot Actuator 构建 RESTful Web 应用
    回调“地狱”和反应模式
    Java动态规划
  • 原文地址:https://www.cnblogs.com/xue0708/p/15185215.html
Copyright © 2011-2022 走看看