首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Glium中使用UniformBuffer将任意大小的对象传递给片段着色器

在Glium中使用UniformBuffer将任意大小的对象传递给片段着色器
EN

Stack Overflow用户
提问于 2015-10-04 21:36:16
回答 1查看 807关注 0票数 5

我的问题是在试验各种不同的技术时提出的,我对这些技术都没有太多的经验。可悲的是,我甚至不知道我是否犯了一个愚蠢的逻辑错误,我是否使用了glium机箱错误,我是否在GLSL上搞砸了等等。无论如何,我从零开始了一个新的锈蚀项目,我的工作是以一个最小的例子来展示我的问题,这个问题至少会在我的电脑上重现。

但是,这个最小的例子最终很难解释,所以我首先做了一个更小的例子,它做我想要做的事情,尽管通过黑客攻击位,并且被限制在128个元素(在GLSL uvec4中被限制为4乘以32位)。从这一点出发,我的问题出现的版本是相当简单的。

一个工作版本,具有简单的uniform和位移位。

该程序在屏幕上创建一个矩形,纹理坐标从0.0水平到128.0。该程序包含一个矩形顶点着色器和一个片段着色器,它使用纹理坐标在矩形上绘制垂直条纹:如果纹理坐标(夹紧到uint)是奇数,它画一种颜色,当纹理坐标是偶数时,它画另一种颜色。

代码语言:javascript
复制
// GLIUM, the crate I'll use to do "everything OpenGL"
#[macro_use]
extern crate glium;

// A simple struct to hold the vertices with their texture-coordinates.
// Nothing deviating much from the tutorials/crate-documentation.
#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
    tex_coords: [f32; 2],
}

implement_vertex!(Vertex, position, tex_coords);


// The vertex shader's source. Does nothing special, except passing the
// texture coordinates along to the fragment shader.
const VERTEX_SHADER_SOURCE: &'static str = r#"
    #version 140

    in vec2 position;
    in vec2 tex_coords;
    out vec2 preserved_tex_coords;

    void main() {
        preserved_tex_coords = tex_coords;
        gl_Position = vec4(position, 0.0, 1.0);
    }
"#;

// The fragment shader. uses the texture coordinates to figure out which color to draw.
const FRAGMENT_SHADER_SOURCE: &'static str =  r#"
    #version 140

    in vec2 preserved_tex_coords;
    // FIXME: Hard-coded max number of elements. Replace by uniform buffer object
    uniform uvec4 uniform_data;
    out vec4 color;

    void main() {
        uint tex_x = uint(preserved_tex_coords.x);
        uint offset_in_vec = tex_x / 32u;
        uint uint_to_sample_from = uniform_data[offset_in_vec];
        bool the_bit = bool((uint_to_sample_from >> tex_x) & 1u);
        color = vec4(the_bit ? 1.0 : 0.5, 0.0, 0.0, 1.0);
    }
"#;

// Logic deciding whether a certain index corresponds with a 'set' bit on an 'unset' one.
// In this case, for the alternating stripes, a trivial odd/even test.
fn bit_should_be_set_at(idx: usize) -> bool {
    idx % 2 == 0
}

fn main() {
    use glium::DisplayBuild;
    let display = glium::glutin::WindowBuilder::new().build_glium().unwrap();

    // Sets up the vertices for a rectangle from -0.9 till 0.9 in both dimensions.
    // Texture coordinates go from 0.0 till 128.0 horizontally, and from 0.0 till
    // 1.0 vertically.
    let vertices_buffer = glium::VertexBuffer::new(
        &display,
        &vec![Vertex { position: [ 0.9, -0.9], tex_coords: [  0.0, 0.0] },
              Vertex { position: [ 0.9,  0.9], tex_coords: [  0.0, 1.0] },
              Vertex { position: [-0.9, -0.9], tex_coords: [128.0, 0.0] },
              Vertex { position: [-0.9,  0.9], tex_coords: [128.0, 1.0] }]).unwrap();
    // The rectangle will be drawn as a simple triangle strip using the vertices above.
    let indices_buffer = glium::IndexBuffer::new(&display,
                                                 glium::index::PrimitiveType::TriangleStrip,
                                                 &vec![0u8, 1u8, 2u8, 3u8]).unwrap();
    // Compiling the shaders defined statically above.
    let shader_program = glium::Program::from_source(&display,
                                                     VERTEX_SHADER_SOURCE,
                                                     FRAGMENT_SHADER_SOURCE,
                                                     None).unwrap();

    // Some hackyy bit-shifting to get the 128 alternating bits set up, in four u32's,
    // which glium manages to send across as an uvec4.
    let mut uniform_data = [0u32; 4];
    for idx in 0..128 {
        let single_u32 = &mut uniform_data[idx / 32];
        *single_u32 = *single_u32 >> 1;
        if bit_should_be_set_at(idx) {
            *single_u32 = *single_u32 | (1 << 31);
        }
    }

    // Trivial main loop repeatedly clearing, drawing rectangle, listening for close event.
    loop {
        use glium::Surface;
        let mut frame = display.draw();
        frame.clear_color(0.0, 0.0, 0.0, 1.0);
        frame.draw(&vertices_buffer, &indices_buffer, &shader_program,
                   &uniform! { uniform_data: uniform_data },
                   &Default::default()).unwrap();
        frame.finish().unwrap();

        for e in display.poll_events() { if let glium::glutin::Event::Closed = e { return; } }
    }
}

但这还不够..。

这个程序工作,并显示矩形与交替条纹,但有明确的限制是限制128条(或64条,我猜。其他64是“矩形的背景”)。为了允许任意多条条纹(或者一般情况下,将任意多的数据传递给片段着色器),可以使用均匀缓冲对象胶质暴露。遗憾的是,胶质回购中最相关的例子无法在我的机器上编译:GLSL版本不受支持,buffer关键字是支持的版本中的语法错误,通常不支持计算着色器(在我的机器上使用glium ),也不支持无头渲染上下文。

一个不太实用的版本,带有缓冲区uniform

因此,由于无法从该示例开始,我不得不使用文档从头开始。对于上面的示例,我提出了以下内容:

代码语言:javascript
复制
// Nothing changed here...
#[macro_use]
extern crate glium;

#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
    tex_coords: [f32; 2],
}

implement_vertex!(Vertex, position, tex_coords);


const VERTEX_SHADER_SOURCE: &'static str = r#"
    #version 140

    in vec2 position;
    in vec2 tex_coords;
    out vec2 preserved_tex_coords;

    void main() {
        preserved_tex_coords = tex_coords;
        gl_Position = vec4(position, 0.0, 1.0);
    }
"#;
// ... up to here.

// The updated fragment shader. This one uses an entire uint per stripe, even though only one
// boolean value is stored in each.
const FRAGMENT_SHADER_SOURCE: &'static str =  r#"
    #version 140
    // examples/gpgpu.rs uses
    //     #version 430
    //     buffer layout(std140);
    // but that shader version is not supported by my machine, and the second line is
    // a syntax error in `#version 140`

    in vec2 preserved_tex_coords;

    // Judging from the GLSL standard, this is what I have to write:
    layout(std140) uniform;
    uniform uniform_data {
        // TODO: Still hard-coded max number of elements, but now arbitrary at compile-time.
        uint values[128];
    };
    out vec4 color;

    // This one now becomes much simpler: get the coordinate, clamp to uint, index into
    // uniform using tex_x, cast to bool, choose color.
    void main() {
        uint tex_x = uint(preserved_tex_coords.x);
        bool the_bit = bool(values[tex_x]);
        color = vec4(the_bit ? 1.0 : 0.5, 0.0, 0.0, 1.0);
    }
"#;


// Mostly copy-paste from glium documentation: define a Data type, which stores u32s,
// make it implement the right traits
struct Data {
    values: [u32],
}

implement_buffer_content!(Data);
implement_uniform_block!(Data, values);


// Same as before
fn bit_should_be_set_at(idx: usize) -> bool {
    idx % 2 == 0
}

// Mostly the same as before
fn main() {
    use glium::DisplayBuild;
    let display = glium::glutin::WindowBuilder::new().build_glium().unwrap();

    let vertices_buffer = glium::VertexBuffer::new(
        &display,
        &vec![Vertex { position: [ 0.9, -0.9], tex_coords: [  0.0, 0.0] },
              Vertex { position: [ 0.9,  0.9], tex_coords: [  0.0, 1.0] },
              Vertex { position: [-0.9, -0.9], tex_coords: [128.0, 0.0] },
              Vertex { position: [-0.9,  0.9], tex_coords: [128.0, 1.0] }]).unwrap();
    let indices_buffer = glium::IndexBuffer::new(&display,
                                                 glium::index::PrimitiveType::TriangleStrip,
                                                 &vec![0u8, 1u8, 2u8, 3u8]).unwrap();
    let shader_program = glium::Program::from_source(&display,
                                                     VERTEX_SHADER_SOURCE,
                                                     FRAGMENT_SHADER_SOURCE,
                                                     None).unwrap();


    // Making the UniformBuffer, with room for 128 4-byte objects (which u32s are).
    let mut buffer: glium::uniforms::UniformBuffer<Data> =
              glium::uniforms::UniformBuffer::empty_unsized(&display, 4 * 128).unwrap();
    {
        // Loop over all elements in the buffer, setting the 'bit'
        let mut mapping = buffer.map();
        for (idx, val) in mapping.values.iter_mut().enumerate() {
            *val = bit_should_be_set_at(idx) as u32;
            // This _is_ actually executed 128 times, as expected.
        }
    }

    // Iterating again, reading the buffer, reveals the alternating 'bits' are really
    // written to the buffer.

    // This loop is similar to the original one, except that it passes the buffer
    // instead of a [u32; 4].
    loop {
        use glium::Surface;
        let mut frame = display.draw();
        frame.clear_color(0.0, 0.0, 0.0, 1.0);
        frame.draw(&vertices_buffer, &indices_buffer, &shader_program,
                   &uniform! { uniform_data: &buffer },
                   &Default::default()).unwrap();
        frame.finish().unwrap();

        for e in display.poll_events() { if let glium::glutin::Event::Closed = e { return; } }
    }
}

我希望这会产生相同的条形矩形(或者给出一些错误,或者如果我做错了什么事情就会崩溃)。相反,它显示矩形,其中最右边的四分之一为实心鲜红色(即“片段着色器读取它时该位似乎已被设置”),其余四分之三则为深红(即“当片段着色器读取它时该位未被设置”)。

自原始投寄以来的更新

我真的在黑暗中刺痛,所以我想这可能是一个低级的错误,包括内存排序、endianness、缓冲区溢出/不足等等。我尝试了各种方法,用容易识别的位模式填充“相邻”内存位置(例如,每三组中有一位,每四组中有一位,两组中有一位,接着有两位未设置等等)。这并没有改变输出。

让内存“接近”uint values[128]的一个显而易见的方法是将其放入Data结构中,就在values前面(不允许在values后面,因为Datavalues: [u32]是动态大小的)。如上所述,这不会改变输出。然而,在uniform_data缓冲区中放置一个适当填充的uvec4,并使用类似于第一个示例的E 125main函数,可以产生最初的结果。这表明glium::uniforms::UniformBuffer<Data>在se E 128中执行E 229工作。

因此,我更新了标题,以反映出问题似乎存在于其他地方。

在伊莱回答之后

伊莱·弗里德曼的回答帮助我找到了解决方案,但我还没有完全做到这一点。

分配和填充一个缓冲区的4倍大确实改变了输出,从一个四分之一填充矩形变成一个完全填充矩形。噢,这不是我想要的。不过,我的着色器现在正在从正确的记忆单词中读取信息。所有这些词都应该用正确的位模式填充。然而,矩形的任何部分都没有被条纹化。由于bit_should_be_set_at应该每隔一步设置一次,所以我提出了这样一个假设,即所发生的事情如下:

代码语言:javascript
复制
Bits: 1010101010101010101010101010101010101
Seen: ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   
What it looks like: all bits set

为了检验这一假设,我将bit_should_be_set_at修改为在3、4、5、6、7和8的倍数上返回true

代码语言:javascript
复制
Bits: 1001001001001001001001001001001001001
Seen: ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   
What it looks like: first bit set, then repeating two unset, one set.

Bits: 1000100010001000100010001000100010001
Seen: ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   
What it looks like: all bits set

Bits: 1000010000100001000010000100001000010
Seen: ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   
What it looks like: first bit set, then repeating four unset, one set.

Bits: 1000001000001000001000001000001000001
Seen: ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   
What it looks like: first bit set, then repeating two unset, one set.

Bits: 1000000100000010000001000000100000010
Seen: ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   
What it looks like: first bit set, then repeating six unset, one set.

Bits: 1000000010000000100000001000000010000
Seen: ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   
What it looks like: first bit set, then every other bit set.

这个假设有意义吗?不管怎么说:问题是设置数据(在锈蚀边),还是将其读回(在GLSL一侧)?

EN

回答 1

Stack Overflow用户

发布于 2015-10-07 22:02:56

你遇到的问题是如何分配制服。uint values[128];没有您认为的内存布局;它实际上具有与uint4 values[128]相同的内存布局。见object.txt分节2.15.3.1.2.

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32938673

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档