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

    1,实际上这个原理类似opencl,将数据通过draw api做运算,一般通过的绘制方法:

    glBeginTransformFeedback(GL_POINTS);
    glDrawArrays(GL_POINTS, 0, 5);
    glEndTransformFeedback();

    如上绘制5个点,每个点携带一个 float数值,然后再vert shading里面做运算。

    下面一个案例将准备好的数值传入gpu,然后做sqrt运算:

    #define GLEW_STATIC
    // GLEW
    #include <GL/glew.h>
    #include <cstdlib>
    #undef GLFW_DLL
    // GLFW
    #include <GLFW/glfw3.h>
    #include <iostream>
    #include <sstream>
    #include <fstream>
    #include <string>
    using namespace std;
    
    
    string readFile(const char *path){
        ifstream stream;
        stringstream ss;
        stream.exceptions(ifstream::badbit);
        try
        {
            stream.open(path);    // open file
            ss << stream.rdbuf(); // get strings from file
        } catch (ifstream::failure e)
        {
            cout << "ERROR::OPEN FILE:" << path << endl;
        }
        // close file handle
        stream.close();
    
        // get str() from stringstream
        string shaderCode = ss.str();
        return shaderCode;
    }
    
    
    
    void init(){
    
    
        string code =  readFile("shaders/feedback/CP_01.vert");
        const char * vertexShaderSrc = code.c_str();
    
        // Create shader and compile
        GLuint shader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(shader, 1, &vertexShaderSrc, nullptr);
        glCompileShader(shader);
    
        // Create program and specify transform feedback variables
        GLuint program = glCreateProgram();
        glAttachShader(program, shader);
    
        // binding the out value
        const GLchar* feedbackVaryings[] = { "outValue" };
        glTransformFeedbackVaryings(program, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
    
        // finally link program and use
        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;
        glCreateBuffers(1, &tbo);
        glBindBuffer(GL_ARRAY_BUFFER, tbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(data), nullptr, GL_STATIC_READ);
    
        // Perform feedback transform
        glEnable(GL_RASTERIZER_DISCARD);
    
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tbo);  // deafault
    
    
        glBeginTransformFeedback(GL_POINTS);
        glDrawArrays(GL_POINTS, 0, 5);
        glEndTransformFeedback();
    
        glDisable(GL_RASTERIZER_DISCARD);
    
        glFlush();
    
        // Fetch and print results
        GLfloat feedback[5];
        // parm1: target
        // parm2: offset
        // parm3: size
        // parm4: void *data
        // glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
    
    
        //OGL 4.5 use-> glGetNamedBufferSubData()
        glGetNamedBufferSubData(tbo,0,sizeof(feedback), feedback);
    
    
        printf("%f %f %f %f %f
    ", feedback[0], feedback[1], feedback[2], feedback[3], feedback[4]);
    
        glDeleteProgram(program);
        glDeleteShader(shader);
    
        glDeleteBuffers(1, &tbo);
        glDeleteBuffers(1, &vbo);
    
        glDeleteVertexArrays(1, &vao);
    
    
    }
    
    
    
    
    
    int main(){
    
    
    
        glfwInit();
    
    
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_VISIBLE,GL_FALSE);
        GLFWwindow * window = glfwCreateWindow(800,600,"Hello",NULL,NULL);
        glfwMakeContextCurrent(window);
    
    
        // glew init
        glewInit();
    
    
    
    /*
        while( !glfwWindowShouldClose( window ) ) {
    
            glfwPollEvents();
            glfwSwapBuffers(window);
        }
    */
    
    
    
    
        init();
    
    
    
    
    
    
    
    
    
    
    
        glfwTerminate();
        glfwDestroyWindow(window);
    
        return 0;
    }
    View Code

    2,有趣的是切换双重缓冲的buffer方法

    这个来自红皮书的:https://github.com/openglredbook/examples/blob/master/src/03-xfb/03-xfb.cpp

     if ((frame_count & 1) != 0)
     {
        glBindVertexArray(vao[1]);
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo[0]);
     }
     else
     {
        glBindVertexArray(vao[0]);
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo[1]);
     }

     

     双缓冲更新 如图用的上述方法。帧0 用的buffer2, 帧1 buffer1, 来回切换,传递到glsl中。

     在https://open.gl/feedback结尾有个小练习:https://open.gl/content/code/c8_exercise_1.txt,使用的是初始数据->OPENGL->Feedback to client( getSubData() ) -> OPENGL update粒子。

    3,在https://open.gl/feedback最后一个,输入5个float数值,然后再输入到geometry shader:

    // Vertex shader
    const GLchar* vertexShaderSrc = R"glsl(
        in float inValue;
        out float geoValue;
    
        void main()
        {
            geoValue = sqrt(inValue);
        }
    )glsl";
    
    // Geometry shader
    const GLchar* geoShaderSrc = R"glsl(
        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";

    从 vertex shader里输出的geoValue传递到Geometry shader,geoValue.然后每个点生成3个顶点(geometry shader里是输出三角形),也就是15个数据。

    注意geoValue传入的获取方法,我们传入的是点,所以index 只有[0].

    如果输入的是三角形则:

    geoValue[0] 代表三角形第一个顶点的数据

    geoValue[1] 代表三角形第二个顶点的数据

    geoValue[2] 代表三角形第三个顶点的数据

    最后feedback开始接受数据的时候比如是三角形接入:

    glBeginTransformFeedback(GL_TRIANGLES);

    最后拿数据也是15个:

    // Fetch and print results
    GLfloat feedback[15];
    glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
    
    for (int i = 0; i < 15; i++) {
        printf("%f
    ", feedback[i]);
    }

    4,有个很重要的问题,如果你在vertex shading里面的:

    #version 450 core
    layout (location = 0) in vec2 P;
    layout (location = 1) in vec2 V;
    layout (location = 2) in vec2 OrigP;
    
    out vec2 outP;
    out vec2 outV;
    
    
    uniform vec2 mousePos;
    uniform float dt = 0.01;
    
    void main() {
    
        gl_Position = vec4(P,0, 1.0);
        outP = vec2(0,0);
        outV = vec2(0,0);
    }

    C++里这么feedback,必须要把outP,outV处的变量写上一个值。不填写的话,fragmentshader就会错误的显示黑色。

    const GLchar* feedbackVaryings[] = { "outP", "outV" };
    glTransformFeedbackVaryings(program, 2, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);

     鼠标与粒子交互:

    #version 450 core
    layout (location = 0) in vec2 P;  // prev P
    layout (location = 1) in vec2 V;  // prev V
    layout (location = 2) in vec2 OrigP; // original P
    
    out vec2 outP; // next frame P
    out vec2 outV; // next Frame V
    
    
    uniform vec2 mousePos;
    uniform float dt = 0.01;
    
    void main() {
        vec2 next_v = OrigP - P;
        if(length(mousePos - OrigP) < 0.75f){
            vec2 a = 1.5f * normalize(mousePos - P);
            next_v = V + a * dt;
        }
        if(length(next_v) > 1.0f){
            next_v *= 0.25;
        }
        vec2 next_p = P + next_v * dt;
    
        outP = next_p;
        outV = next_v;
        gl_Position = vec4(next_p, 0.0, 1.0);
    
    
    }
    GLSL Vert
    #define GLEW_STATIC
    // GLEW
    #include <GL/glew.h>
    #include <chrono>
    #include <cstdlib>
    #undef GLFW_DLL
    // GLFW
    #include <GLFW/glfw3.h>
    #include "utils.h"
    
    using namespace std;
    using namespace AlgebraMaster;
    
    const int width = 800;
    const int height = 800;
    
    
    
    static GLuint shaderProgram;
    static GLuint VAO,VBO;
    static GLuint TFBO; // transform feedback object
    
    
    // Fragment shader
    const GLchar* fragmentShaderSrc = R"glsl(
        #version 450 core
    
        out vec4 outColor;
    
        void main()
        {
            outColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    )glsl";
    
    
    void init_particles_data(GLfloat *data, int numdata){
    
        glCreateVertexArrays(1, &VAO);
        glBindVertexArray(VAO);
    
    
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * numdata, data, GL_STREAM_DRAW);
    
        //glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STREAM_DRAW);
        //glNamedBufferData(VBO,sizeof(data),data,GL_STREAM_DRAW);
        //glNamedBufferStorage(VBO,sizeof(data),data, 0);
    
    
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), 0);
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
        glEnableVertexAttribArray(2);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(4 * sizeof(GLfloat)));
    
    
    
        glGenBuffers(1, &TFBO);
        glBindBuffer(GL_ARRAY_BUFFER, TFBO);
        glBufferData(GL_ARRAY_BUFFER, 400 * sizeof(GLfloat), nullptr, GL_STATIC_READ);
        //glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, TFBO);
        glPointSize(10.0f);
    
    }
    
    void initShader(){
        string vert_code =  readFile("shaders/feedback/mouse_track_particles.vert");
        string frag_code =  readFile("shaders/feedback/mouse_track_particles.frag");
    
        const char * vertexShaderSrc = vert_code.c_str();
        const char * fragShaderSrc = frag_code.c_str();
    
    
        // Compile shaders
        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSrc, nullptr);
        glCompileShader(vertexShader);
    
        GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSrc, nullptr);
        glCompileShader(fragmentShader);
    
        // Create shaderProgram and specify transform feedback variables
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
    
        const GLchar* feedbackVaryings[] = { "outP", "outV" };
        glTransformFeedbackVaryings(shaderProgram, 2, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
        glLinkProgram(shaderProgram);
        glUseProgram(shaderProgram);
    
    
    }
    
    void display(){
        //glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, TFBO);
        // Perform feedback transform and draw vertices
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, TFBO);
        glBeginTransformFeedback(GL_POINTS);
        glDrawArrays(GL_POINTS, 0, 100);
        glEndTransformFeedback();
    }
    
    void cursor_pos_callback(GLFWwindow *w, double x, double y);
    void framebuffer_size_callback(GLFWwindow* window, int width, int height);
    
    
    int main(){
    
    
    
        glfwInit();
    
    
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        //glfwWindowHint(GLFW_VISIBLE,GL_FALSE);
    
    
        GLFWwindow * window = glfwCreateWindow(width,height,"Hello",NULL,NULL);
        glfwSetCursorPosCallback(window, cursor_pos_callback);
        glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
        glfwMakeContextCurrent(window);
    
    
        // glew init
        glewInit();
        // vbo and initialize data
        // Create input VBO and vertex format
        GLfloat data[600] = {};
        // Vertex format: 6 floats per vertex:
        // pos.x  pox.y  vel.x  vel.y  origPos.x  origPos.y
        // Set original and initial positions
        for (int y = 0; y < 10; y++) {
            for (int x = 0; x < 10; x++) {
                data[60 * y + 6 * x] = 0.2f * x - 0.9f;
                data[60 * y + 6 * x + 1] = 0.2f * y - 0.9f;
                data[60 * y + 6 * x + 4] = 0.2f * x - 0.9f;
                data[60 * y + 6 * x + 5] = 0.2f * y - 0.9f;
            }
        }
        initShader();
        init_particles_data(data, 100 * 6);
    
    
        GLfloat feedback[400];
    
    
        auto t_prev = std::chrono::high_resolution_clock::now();
        while( !glfwWindowShouldClose( window ) ) {
            // Clear the screen to black
            glClearColor(0.2f, 0.2f, 0.2f, 0.2f);
            glClear(GL_COLOR_BUFFER_BIT);
    
    
    
            // Calculate delta time
            auto t_now = std::chrono::high_resolution_clock::now();
            float time = std::chrono::duration_cast<std::chrono::duration<float>>(t_now - t_prev).count();
            t_prev = t_now;
    
    
            //cout << newx <<" " <<newy << endl;
            GLint uniMousePos = glGetUniformLocation(shaderProgram, "mousePos");
            cout << uniMousePos << endl;
            double x;
            double y;
            glfwGetCursorPos(window,&x,&y);
            auto newx = (x / 400.0) - 1.0;
            auto newy = (-y / 400.0 ) + 1.0;
            glUniform2f(uniMousePos, newx, newy);
    
            // Render content
            display();
    
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, TFBO);
            glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback), feedback);
            //cout << feedback[0] << " " << feedback[1] <<endl;
            for (int i = 0; i < 100; i++) {
                data[6 * i] = feedback[4 * i];
                data[6 * i + 1] = feedback[4 * i + 1];
                data[6 * i + 2] = feedback[4 * i + 2];
                data[6 * i + 3] = feedback[4 * i + 3];
            }
    
            // glBufferData() would reallocate the whole vertex data buffer, which is unnecessary here.
            // glBufferSubData() is used instead - it updates an existing buffer.
            glBindBuffer(GL_ARRAY_BUFFER,VBO);
            glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data);
    
    
    
            glfwSwapBuffers(window);
            glfwPollEvents();
    
        }
    
        glfwTerminate();
        glfwDestroyWindow(window);
    
        return 0;
    }
    
    void cursor_pos_callback(GLFWwindow *w, double x, double y){
        // NDC SPACE
        double newx = x / (double(width)/2.0) -1;
        double newy = -y / (double(height)/2.0) +1;
        //cout << newx <<" " <<newy << endl;
        GLint uniMousePos = glGetUniformLocation(shaderProgram, "mousePos");
        glUniform2f(uniMousePos, newx, newy);
    
    }
    void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    {
        // make sure the viewport matches the new window dimensions; note that width and
        // height will be significantly larger than specified on retina displays.
        glViewport(0, 0, width, height);
    
    }
    main.cpp

     源码中从GPU内从copy下来,只copy到feedback一部分,只拷贝vec2 P ,vec2 V 共100个点,所以有400个float数据。

    最后填充到VBO中,关键的地方来了:防止内存重新创建。

    // glBufferData() would reallocate the whole vertex data buffer, which is unnecessary here.
    // glBufferSubData() is used instead - it updates an existing buffer.
    glBindBuffer(GL_ARRAY_BUFFER,VBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data);

    REF:

    https://open.gl/feedback

    OpenGL Programming Guide Ninth Edition https://github.com/openglredbook/examples/blob/master/src/03-xfb/03-xfb.cpp

  • 相关阅读:
    Shell学习笔记 ——第一天
    Myclipse 安装 Maven遇见的N个异常
    Myeclipse 创建 Web Maven项目
    Guava API
    String 转Map(基于Guava类库)
    Mybatis——helloWorld级程序
    redis
    listener、context、filter、servlet及其加载顺序
    junit 单元测试
    hibernate —— 树状存储
  • 原文地址:https://www.cnblogs.com/gearslogy/p/12993443.html
Copyright © 2011-2022 走看看