Рендеринг вывода вычислительного шейдера на экран - PullRequest
2 голосов
/ 21 сентября 2019

Я пытаюсь настроить конвейер для рендеринга лучей.

Сначала я установил вершинный и геометрический шейдеры, чтобы взять только 1 произвольное число с плавающей запятой, и сделал квад, чтобы я могиспользуйте только фрагментный шейдер и вводите данные, пропуская их через все шейдеры или униформы.

Но потом я наткнулся на вычислительный шейдер и нашел несколько учебных пособий, но все они делали почти одно и то же, делая квад и рендерингвывод на него шейдеров вычислений, я думаю, что это довольно глупо, если у вас есть возможность рендерить, как вы хотите, с помощью шейдера вычислений и все еще делать трюки, чтобы получить свой результат.

После некоторых дальнейших исследований я нашел функцию'glFramebufferImage2D' и, насколько я понял, он присоединяет ImageTexture (тот, который я написал с моим вычислительным шейдером в моем случае) к Framebuffer (буфер, который отображается, насколько я понял).Так что мне не нужно делать трюк, генерирующий квад.Но код, который я написал, просто показывает черный экран.Я что-то не так понял?или я что-то пропустил в своем коде?

Это мой код: (Я пока не беспокоюсь о предупреждениях и отключении шейдерных программ. Я просто хотел проверить концепцию сейчас.)

main.rs

extern crate sdl2;
extern crate gl;

use sdl2::pixels::Color;
use std::ffi::{CStr, CString};


fn main() {

    let width = 512;
    let height = 512;

    let sdl = sdl2::init().unwrap();
    let video_sys = sdl.video().unwrap();

    let gl_attr = video_sys.gl_attr();
    gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
    gl_attr.set_context_version(4, 1);

    let window = video_sys.window("test", width, height).opengl().build().unwrap();
    let gl_ctx = window.gl_create_context().unwrap();

    let gl = gl::load_with(|s| video_sys.gl_get_proc_address(s) as *const std::os::raw::c_void);


    unsafe {
        gl::Viewport(0, 0, width as gl::types::GLsizei, height as gl::types::GLsizei);
        gl::ClearColor(0.0, 0.0, 0.0, 1.0);
    }

    let shader = unsafe { gl::CreateShader(gl::COMPUTE_SHADER) };
    unsafe {
        gl::ShaderSource(shader, 1, &CString::new(include_str!("screen.comp")).unwrap().as_ptr(), std::ptr::null());
        gl::CompileShader(shader);
    }

    let program = unsafe { gl::CreateProgram() };
    unsafe {
        gl::AttachShader(program, shader);
        gl::LinkProgram(program);
        gl::UseProgram(program);
    }

    let mut tex_out : gl::types::GLuint = 0;
    unsafe {
        gl::GenTextures(1, &mut tex_out);
        gl::ActiveTexture(gl::TEXTURE0);
        gl::BindTexture(gl::TEXTURE_2D, tex_out);
        gl::TexImage2D(
            gl::TEXTURE_2D,
            0,
            gl::RGBA32F as gl::types::GLint,
            width as gl::types::GLsizei,
            height as gl::types::GLsizei,
            0,
            gl::RGBA,
            gl::FLOAT,
            std::ptr::null()
        );
        gl::BindImageTexture(0, tex_out, 0, gl::FALSE, 0, gl::WRITE_ONLY, gl::RGBA32F);
    }


    let mut event_pump = sdl.event_pump().unwrap();

    'main: loop {

        for event in event_pump.poll_iter() {
            match event {
                sdl2::event::Event::Quit {..} => break 'main,
                _ => {},
            }
        }

        unsafe {
            gl::DispatchCompute(width as gl::types::GLuint, height as gl::types::GLuint, 1);
            gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
            gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, tex_out, 0);
        }

        window.gl_swap_window();

    }

}

screen.comp

#version 430
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform image2D img_output;

void main() {

    vec4 pixel = vec4(1.0, 1.0, 1.0, 1.0);

    ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);

    imageStore(img_output, pixel_coords, pixel);

}

EDIT: рабочий код: (с некоторым цветовым тестом)

main.rs:

extern crate sdl2;
extern crate gl;

use std::ffi::{CStr, CString};


struct Renderer {
    width : u32 ,
    height : u32 ,
    shader : gl::types::GLuint ,
    program : gl::types::GLuint ,
}

impl Renderer {

    fn new(width : u32, height : u32, shader_name : &CStr) -> Self {

        unsafe {
            gl::Viewport(0, 0, width as gl::types::GLsizei, height as gl::types::GLsizei);
            gl::ClearColor(0.0, 0.0, 0.0, 1.0);
        }

        let shader = unsafe { gl::CreateShader(gl::COMPUTE_SHADER) };
        unsafe {
            gl::ShaderSource(shader, 1, &shader_name.as_ptr(), std::ptr::null());
            gl::CompileShader(shader);
        }

        let program = unsafe { gl::CreateProgram() };
        unsafe {
            gl::AttachShader(program, shader);
            gl::LinkProgram(program);
            gl::UseProgram(program);
        }

        let mut tex_out : gl::types::GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut tex_out);
            gl::ActiveTexture(gl::TEXTURE0);
            gl::BindTexture(gl::TEXTURE_2D, tex_out);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as gl::types::GLint);
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA32F as gl::types::GLint,
                width as gl::types::GLsizei,
                height as gl::types::GLsizei,
                0,
                gl::RGBA,
                gl::FLOAT,
                std::ptr::null()
            );
            gl::BindImageTexture(0, tex_out, 0, gl::FALSE, 0, gl::WRITE_ONLY, gl::RGBA32F);
        }

        let mut fbo : gl::types::GLuint = 0;
        unsafe {
            gl::GenFramebuffers(1, &mut fbo );
            gl::BindFramebuffer(gl::FRAMEBUFFER, fbo );
            gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, tex_out, 0);
            gl::BindFramebuffer(gl::READ_FRAMEBUFFER, fbo);
            gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
        }

        let resolution = unsafe { gl::GetUniformLocation(program, CString::new("iResolution").unwrap().as_ptr()) };
        unsafe { gl::Uniform2i(resolution, width as gl::types::GLint, height as gl::types::GLint); }

        Self {
            width : width ,
            height : height ,
            shader : shader ,
            program : program ,
        }

    }

    fn get_input(&self, name : &str) -> gl::types::GLint {
        unsafe { gl::GetUniformLocation(self.program, CString::new(name).unwrap().as_ptr()) }
    }

    fn input1f(&self, ptr : gl::types::GLint, f : gl::types::GLfloat) {
        unsafe { gl::Uniform1f(ptr, f) };
    }

    fn draw(&self) {

        unsafe {
            gl::DispatchCompute(self.width as gl::types::GLuint, self.height as gl::types::GLuint, 1);
            gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
            gl::BlitFramebuffer(0, 0, self.width as gl::types::GLint, self.height as gl::types::GLint, 0, 0, self.width as gl::types::GLint, self.height as gl::types::GLint, gl::COLOR_BUFFER_BIT, gl::LINEAR);
        }

    }

}

impl Drop for Renderer {

    fn drop(&mut self) {
        unsafe {
            gl::DeleteShader(self.shader);
            gl::DeleteProgram(self.program);
        }
    }

}


fn main() {

    let width = 512;
    let height = 512;

    let sdl = sdl2::init().unwrap();
    let video_sys = sdl.video().unwrap();

    let gl_attr = video_sys.gl_attr();
    gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
    gl_attr.set_context_version(4, 1);

    let window = video_sys.window("test", width, height).opengl().build().unwrap();
    let _gl_ctx = window.gl_create_context().unwrap();

    let _gl = gl::load_with(|s| video_sys.gl_get_proc_address(s) as *const std::os::raw::c_void);

    let render = Renderer::new(width, height, &CString::new(include_str!("screen.comp")).unwrap());
    let time_attr = render.get_input("time");

    let mut event_pump = sdl.event_pump().unwrap();

    let mut time = 0.0;

    'main: loop {

        for event in event_pump.poll_iter() {
            match event {
                sdl2::event::Event::Quit {..} => break 'main,
                _ => {},
            }
        }

        time += 0.01;
        if time > 1.0 {
            time = 0.0;
        }

        render.input1f(time_attr, time);
        render.draw();
        window.gl_swap_window();

    }

}

screen.comp:

#version 430
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform image2D img;
uniform ivec2 iResolution;

uniform float time;

void main() {

    ivec2 iCoords = ivec2(gl_GlobalInvocationID.xy);

    vec2 uv = vec2(iCoords) / vec2(iResolution);

    vec4 pixel = vec4(uv, time, 1.0);

    imageStore(img, iCoords, pixel);

}

Ответы [ 2 ]

1 голос
/ 21 сентября 2019

Вот ревизия с предлагаемыми исправлениями и несколькими полировками:

extern crate sdl2;
extern crate gl;

//use sdl2::pixels::Color;
//use std::ffi::{CStr, CString};
use std::ffi::{CString};

fn main() {

let width = 512;
let height = 512;

let sdl = sdl2::init().unwrap();
let video_sys = sdl.video().unwrap();

let gl_attr = video_sys.gl_attr();
gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
gl_attr.set_context_version(4, 1);

let window = video_sys.window("test", width, height).opengl().build().unwrap();
let _gl_ctx = window.gl_create_context().unwrap();

let _gl = gl::load_with(|s| video_sys.gl_get_proc_address(s) as *const std::os::raw::c_void);


unsafe {
    gl::Viewport(0, 0, width as gl::types::GLsizei, height as gl::types::GLsizei);
    gl::ClearColor(0.1, 0.30, 0.50, 1.0);

    let shader = gl::CreateShader(gl::COMPUTE_SHADER);
    gl::ShaderSource(shader, 1, &CString::new(include_str!("screen.comp")).unwrap().as_ptr(), std::ptr::null());
    gl::CompileShader(shader);

    let program = gl::CreateProgram();
    gl::AttachShader(program, shader);
    gl::LinkProgram(program);
    gl::UseProgram(program);

    let mut tex_out : gl::types::GLuint = 0;

    gl::GenTextures(1, &mut tex_out);
    gl::ActiveTexture(gl::TEXTURE0);
    gl::BindTexture(gl::TEXTURE_2D, tex_out);
    gl::TexImage2D( gl::TEXTURE_2D, 0, gl::RGBA32F as gl::types::GLint, width as gl::types::GLsizei, height as gl::types::GLsizei, 0, gl::RGBA, gl::FLOAT, std::ptr::null() );
    let mut event_pump = sdl.event_pump().unwrap();
    let mut fbo: gl::types::GLuint = 0;

    gl::BindImageTexture(0, tex_out, 0, gl::FALSE, 0, gl::WRITE_ONLY, gl::RGBA32F);
    gl::GenFramebuffers(1, &mut fbo);
    gl::BindFramebuffer(gl::FRAMEBUFFER, fbo);
    gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, tex_out, 0);
    gl::BindFramebuffer(gl::READ_FRAMEBUFFER, fbo);
    gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);



    'main: loop {

        for event in event_pump.poll_iter() {
            match event {
                sdl2::event::Event::Quit {..} => break 'main,
                _ => {},
            }
        }

        gl::Clear(gl::COLOR_BUFFER_BIT);
        gl::DispatchCompute(width as gl::types::GLuint, height as gl::types::GLuint, 1);
        gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
        gl::BlitFramebuffer(
            0, 0, width as gl::types::GLsizei, height as gl::types::GLsizei,
            0, 0, width as gl::types::GLsizei, height as gl::types::GLsizei,
            gl::COLOR_BUFFER_BIT, gl::LINEAR);

        window.gl_swap_window();
    }


  }

}
1 голос
/ 21 сентября 2019

Существует проблема при создании текстуры.Начальное значение TEXTURE_MIN_FILTER равно NEAREST_MIPMAP_LINEAR.Если вы не измените его и не создадите мип-карты, тогда текстура не будет «полной».
Установите TEXTURE_MIN_FILTER либо NEAREST, либо LINEAR, чтобы решить проблему:

gl::BindTexture(gl::TEXTURE_2D, tex_out);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR)

Вы преобразовали изображение из вычислительного шейдера в объект текстуры, но для того, чтобы переместить эту текстуру на экран, вы должны скопировать это изображение текстуры в цветовую плоскость кадрового буфера по умолчанию.Невозможно изменить цветовую плоскость кадрового буфера по умолчанию, просто добавив текстуру.

Вместо этого вам необходимо создать именованный Объект кадрового буфера (glGenFramebuffers,glBindFramebuffer) и прикрепите объект текстуры к цветовой плоскости буфера кадров (glFramebufferTexture2D).

let mut fbo : gl::types::GLuint = 0;
gl::GenFramebuffers(1, &mut fbo);
gl::BindFramebuffer(gl::FRAMEBUFFER, fbo);
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, tex_out, 0)

Привязка этого буфера кадров к цели GL_READ_FRAMEBUFFER (glBindFramebuffer) и привязать буфер кадра по умолчанию (ноль) к цели GL_DRAW_FRAMEBUFFER.

gl::BindFramebuffer(gl::READ_FRAMEBUFFER, fbo);
gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);

Наконец, в цикле рендеринга используйте glBlitFramebuffer для копирования содержимого именованного объекта фрамбуфера (текстуры) в цветовую плоскость кадрового буфера по умолчанию.

gl::DispatchCompute(width as gl::types::GLuint, height as gl::types::GLuint, 1);
gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT);
gl::BlitFramebuffer(
    0, 0, width as gl::types::GLsizei, height as gl::types::GLsizei,
    0, 0, width as gl::types::GLsizei, height as gl::types::GLsizei,
    gl::COLOR_BUFFER_BIT, gl::LINEAR);

Обратите внимание, что при использовании glBlitFramebuffer размер области просмотра может отличаться от размера изображения текстуры.
Этот подход не будет работать, если формат текстурыбыло бы цельным.Это работает, потому что формат изображения текстуры является плавающей точкой, а формат кадрового буфера по умолчанию - с фиксированной запятой.

...