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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM