AlgebraMaster

Modern C++ 创造非凡 . 改变世界

导航

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\n", 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\n", 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

posted on 2020-05-30 16:34  gearslogy  阅读(956)  评论(0编辑  收藏  举报