Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How can I pull back data generated in a shader to the CPU while using SFML?  (Read 1683 times)

0 Members and 1 Guest are viewing this topic.

jokoon

  • Newbie
  • *
  • Posts: 35
    • View Profile
I wrote a shader that generates noise, it uses gl_TexCoord[0].xy (a thing generated by SFML) as input and outputs a float for every pixel.

I want the data on the CPU, as an array of 32bits float.

Here is what I'm doing:

    //using opengl 3.0 (I also tried 3.2)
    ContextSettings settings(0, 0, 0, 3, 0);
    sf::RenderWindow window(sf::VideoMode(windowsize.x, windowsize.y),
        "yada", 7, settings
    );


Here I overwrite the texture after the texture is created:

 
#ifndef GL_R32F
#define GL_R32F 0x822E // found this in glad.h for opengl 3.0, easy guess is that this value doesn't change across opengl versions
#endif
auto handle = sprite_perlin.getTexture()->getNativeHandle();
glBindTexture(GL_TEXTURE_2D, handle);
auto size = sprite_perlin.getTexture()->getSize();
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, size.x, size.y, 0, GL_RED, GL_FLOAT, NULL);
        }

Here I read the texture data into values:

       
float values[4096];
// whole_size is the size of the texture, here it is 60
if(whole_size*whole_size < 4096)
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, values);

Image brought_back;
brought_back.create(whole_size, whole_size, Color::Red);

float mi = 1e20, ma = -1e20;

for (int i = 0; i < whole_size; ++i) {
    for (int j = 0; j < whole_size; ++j) {
        brought_back.setPixel(i, j, { 0,Uint8(255.f*values[whole_size*i + j]),0 });
        ma = max(values[whole_size*i + j], ma);
        mi = min(values[whole_size*i + j], mi);
    }
}


 

mi and ma are both to 0.0f;

Here is the noise generated when showed on a sprite with the shader (with some color function that takes a float) The shader works as intended, I would just like to retrieve the data as float on the CPU.



Here is a simplified version of the shader:
uniform int texture_size;

float get_value_at(ivec2 pixel_coord) {
    //dummy value
    return 0.34f;
}

void main(){
    ivec2 pix_pos = ivec2(gl_TexCoord[0].xy*texture_size);

    float val = get_value_at(pix_pos);

    gl_FragColor = vec4(val,0,0,1);
}


Here is an updated code, which is using rendertexture to test things out (sorry it's 417 lines, but it should work as is):

//#include "stdafx.h"
#include <random>
#include <algorithm>

#include <iostream>
#include <string>
#include <vector>
#include <map>

#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <SFML/OpenGL.hpp>
//#include <Box2D\Box2D.h>
using namespace std;
using namespace sf;

typedef Vector2f Vec2;
typedef Vector2<double> Vec2d;
typedef Vector2i Vec2i;

float interp(float start, float end, float coef) { return (end - start)*coef + start; }
Vec2 interp(Vec2 start, Vec2 end, float coef) { return coef * (end - start) + start; }

vector<float> seeded_rand_float(unsigned int seed, int many) {
    vector<float> ret;
    std::mt19937 rr;
    std::uniform_real_distribution<float> dist(0, 1.0);

    rr.seed(seed);

    for (int j = 0; j < many; ++j)
        ret.push_back(dist(rr));
    return ret;
}
vector<Vec2> seeded_rand_vec2(unsigned int seed, int many) {
    auto coeffs1 = seeded_rand_float(seed, many * 2);
    vector<Vec2> pushere;
    for (int i = 0; i < many; ++i)
        pushere.push_back(Vec2(coeffs1[2 * i], coeffs1[2 * i + 1]));
    return pushere;
}
#define msg(s) cout<< #s << " " << s << endl;

RenderWindow * window;
RenderTarget * target;
namespace labground {
    Vec2 winsize, wincenter;
    Vec2i windowsize;
    Vec2 mpos_abs, mpos;


    //logfile lf;
    //configfile cfg;
    Transform transf_glob;

    Vec2 tr(Vec2 p) {
        return transf_glob.transformPoint(p);
    };
    Vec2 untr(Vec2 p) {
        return transf_glob.getInverse().transformPoint(p);
    };

    View view, ui_view;
   
    // zooming
    float zoom_mult = 1.5f;
    float zoom_div = 1.0f / zoom_mult;
    float zoomlevel = 1;

    void zoom (Event&e) {
        using namespace labground;
        mpos_abs = window->mapPixelToCoords(Vec2i(mpos), view);

        // msg(view.getSize());
        // msg(view.getCenter());

        //view = window->getView();
        if (e.mouseWheel.delta < 0) {
            zoomlevel *= zoom_mult;
            view.setSize(view.getSize()*zoom_mult);
            auto center = interp(mpos_abs, view.getCenter(), zoom_mult);
            center = Vec2(Vec2i(center));
            view.setCenter(center);
        }
        if (e.mouseWheel.delta > 0) {
            zoomlevel *= zoom_div;
            view.setSize(view.getSize()*zoom_div);
            auto center = interp(mpos_abs, view.getCenter(), zoom_div);
            view.setCenter(center);
        }
        window->setView(view);
    };
}

namespace perlin_shading {
    Shader perlin_shader;

    sf::Texture texture_shader, texture_broughtback;
    sf::Sprite sprite_perlin, sprite_broughtback, sprite_rtext;
    Image img_back;
    typedef Vec2i ivec2;
    typedef Vec2 vec2;

    float sprite_scale;

    int grad_size, grad_quant, whole_size;
    Vec2 gradients[16];
    Vec2 gradients_ctype[4096];
    RenderTexture rtext;

    void init_perlin() {
        perlin_shader.loadFromFile("shaders/perlin-better.frag", sf::Shader::Fragment);
        //perlin_shader.loadFromFile("shaders/" + cfg.getstr("shader_file"), sf::Shader::Fragment);
        //perlin_shader.loadFromFile("shaders/noisebench.vert", "shaders/" + cfg.getstr("shader_file"));
        perlin_shader.setUniform("grad_size", grad_size);
        perlin_shader.setUniform("grad_quant", grad_quant);

        //perlin_shader.setUniformArray("gradients", gradients, grad_quant*grad_quant);
        perlin_shader.setUniformArray("gradients", gradients_ctype, grad_quant*grad_quant);

        //texture.loadFromFile("pics/orxqx6B.png");

        //sprite_perlin.move(wincenter);

        Image img;
        //img.create(400, 400, Color::Black);
        img.create(whole_size, whole_size, Color::Red);
        for (int i = 0; i < 10; ++i) {
            img.setPixel(i * 2, i, Color::Green);
            img.setPixel(i, i * 2, Color::Red);
            img.setPixel(i, i, Color::Yellow);
            img.setPixel(i / 2, i, Color::Cyan);
            img.setPixel(i, i / 2, Color::Magenta);
        }
        texture_shader.loadFromImage(img);
        //shader.setUniform("texture", sf::Shader::CurrentTexture);
        //perlin_shader.setUniform("v_texCoord4D", Glsl::Vec4(1,1,1,1));

        sprite_perlin.setTexture(texture_shader);
        {
#ifndef GL_R32F
#define GL_R32F 0x822E
#endif
            auto handle = sprite_perlin.getTexture()->getNativeHandle();
            glBindTexture(GL_TEXTURE_2D, handle);
            auto size = sprite_perlin.getTexture()->getSize();
            glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, size.x, size.y, 0, GL_RED, GL_FLOAT, NULL);
        }
        sprite_perlin.move(labground::wincenter);
        sprite_perlin.setScale(sprite_scale, sprite_scale);


        //sprite_perlin.setScale(sprite_scale, sprite_scale);
    }

    void pull_data() {
        using namespace perlin_shading;

        float values[4096];
        /*
        void glGetTexImage(
            GLenum target,
            GLint level,
            GLenum format,
            GLenum type,
            GLvoid * pixels);

        void glTexImage2D(      GLenum target,
            GLint level,
            GLint internalformat,
            GLsizei width,
            GLsizei height,
            GLint border,
            GLenum format,
            GLenum type,
            const GLvoid * data);

        void glReadPixels(      GLint x,
            GLint y,
            GLsizei width,
            GLsizei height,
            GLenum format,
            GLenum type,
            GLvoid * data);


            */

        float mi = 1e20, ma = -1e20;

        // format = GL_RED, type = GL_FLOAT
        //window->clear();
        //window->draw(sprite_broughtback);
        if (false) {
            if (whole_size*whole_size < 4096)
            {
                //glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, values);
                //glReadPixels(0, 0, whole_size, whole_size, GL_RED, GL_FLOAT, values);
            }
            Image brought_back;
            brought_back.create(whole_size, whole_size, Color::Red);


            for (int i = 0; i < whole_size; ++i) {
                for (int j = 0; j < whole_size; ++j) {
                    ma = max(values[whole_size*i + j], ma);
                    mi = min(values[whole_size*i + j], mi);
                }
            }
            msg(ma);
            msg(mi);
       
        }

        //texture_broughtback.loadFromImage(brought_back);
        rtext.create(whole_size, whole_size);
       
        {
            rtext.clear();
            rtext.display();
            rtext.draw(sprite_perlin, &perlin_shader);

            sprite_rtext.setTexture(rtext.getTexture());

            glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, values);
        }

        for (int i = 0; i < whole_size; ++i) {
            for (int j = 0; j < whole_size; ++j) {
                ma = max(values[whole_size*i + j], ma);
                mi = min(values[whole_size*i + j], mi);
            }
        }
        msg(ma);
        msg(mi);

        //img_back = rtext.getTexture().copyToImage();
        //texture_broughtback.loadFromImage(img_back);

        //sprite_broughtback.setTexture(texture_broughtback);
       
        // scales and positions
        sprite_broughtback.setScale(sprite_scale, sprite_scale);
        sprite_rtext.setScale(sprite_scale, sprite_scale);
       
        sprite_broughtback.move({ 0,labground::wincenter.y });
        sprite_rtext.move({ labground::wincenter.x,0 });
       
        auto error = glGetError();
        msg(error);
    }

    void init() {
        grad_size = 20;
        grad_quant = 4;
        whole_size = grad_size * (grad_quant - 1);

        if (grad_quant*grad_quant > 4096) { msg("can't send gradients, please allocate a large C-type array in the code below!"); return; }
        {
            int seed = 43123;
            auto rand_v = seeded_rand_vec2(seed, grad_quant*grad_quant);
            for (int i = 0; i < grad_quant*grad_quant; ++i) {
                gradients_ctype[i] = 2.0f*rand_v[i] - Vec2{ 1.f, 1.0f };
            }

        }

        msg(grad_size);
        msg(grad_quant);
        msg(whole_size);

        auto smallest_size = min(labground:: windowsize.x, labground::windowsize.y);
        sprite_scale = 0.5f* smallest_size / whole_size;
        init_perlin();
        pull_data();
    }

    void draw(RenderTarget * target) {
        //target->draw(sprite_perlin, &perlin_shader);
        target->draw(sprite_perlin, &perlin_shader);
        target->draw(sprite_broughtback);
        target->draw(sprite_rtext);
    }
    void mouse_moved(Vec2 mpos_abs) {
        auto pixel = ivec2(mpos_abs / sprite_scale);
        pixel.x = max(0, min(whole_size - 1, pixel.x));
        pixel.y = max(0, min(whole_size - 1, pixel.y));
    }
}

// treating SFML events
void mouse_moved(Vec2 pos) {
};
void mouse_click (sf::Mouse::Button button) {
};
void mouse_release (sf::Mouse::Button button) {
    if (button == Mouse::Button::Left) {
    }
};
void treat_other_event (Event&e) {
    if (e.type == Event::MouseWheelMoved && e.mouseWheel.delta){
        labground::zoom(e);
    }
};
void treat_key_event(Keyboard::Key k) {
    switch (k)
    {
    case Keyboard::E:
        break;
    case Keyboard::I:

        break;
    case Keyboard::Q:
        break;
    case Keyboard::BackSpace:
        break;

    case Keyboard::Space:
        break;

    case Keyboard::S:
        break;
    case Keyboard::Num1:
        break;
    case Keyboard::Num2:
    case Keyboard::Num3:
    case Keyboard::Num4:
    case Keyboard::Num5:
        break;
    }
};
#include <SFML\Window\ContextSettings.hpp>
// initializing the window
void init_window() {
    using namespace labground;
    Vec2i screen_resolution = { int(VideoMode::getDesktopMode().width), int(VideoMode::getDesktopMode().height) };

    windowsize = Vec2i(0.5f*screen_resolution.x, 0.9f*screen_resolution.y);
    //winsize = Vec2(windowsize);
    wincenter = 0.5f*Vec2(windowsize);
   
    // placing the window on the desktop
    Vec2i windowpos;
    VideoMode::getDesktopMode().height;

    windowpos = Vec2i(
        screen_resolution.x - windowsize.x - 10,
        screen_resolution.y - windowsize.y - 40
    );
    //auto opengl = cfg.getvar<Vec2i>("opengl");
    // ContextSettings::Core
    //ContextSettings settings(0, 0, 0, 4, 5);
    ContextSettings settings(0, 0, 0, 3, 0);

    //sf::RenderWindow window(sf::VideoMode(windowsize.x, windowsize.y), "perlin shader");
    window = new sf::RenderWindow(sf::VideoMode(windowsize.x, windowsize.y), "perlin shader", 7, settings);
    window->setFramerateLimit(60);
    //frame_duration = 1.0f / 60;

    window->setPosition(windowpos);

    ui_view = view = window->getDefaultView();

}

void draw() {
    using namespace labground;
    window->setView(view);
    //////////////// OBJECTS THAT CAN BE ZOOMED ////////////////

    perlin_shading::draw(window);

    //////////////// OBJECTS THAT CANNOT BE ZOOMED ////////////////
   
    window->setView(ui_view);
}
void loop() {
    using namespace labground;
    while (window->isOpen())
    {
        sf::Event event;
        while (window->pollEvent(event))
        {
            switch (event.type)
            {
            case sf::Event::KeyPressed:
                if (event.key.code == sf::Keyboard::Escape)
                    window->close();
                treat_key_event(event.key.code);
                break;
            case sf::Event::Closed:
                window->close();
                break;
            case sf::Event::MouseButtonPressed:
                mouse_click(event.mouseButton.button);
                break;
            case sf::Event::MouseButtonReleased:
                mouse_release(event.mouseButton.button);
                break;
            case sf::Event::MouseMoved:
                mpos = Vec2(event.mouseMove.x, event.mouseMove.y);
                mpos_abs = window->mapPixelToCoords(Vec2i(mpos), view);
                mouse_moved(mpos);
                break;
            default:
                treat_other_event(event);
                break;
            }
        }

        window->clear(Color(16,16,16));
        //update();
        draw();
        window->display();
    }

}
int main(int argc, char*argv[]) {
    init_window();
    perlin_shading::init();
    loop();
}
« Last Edit: September 20, 2019, 08:28:51 pm by jokoon »