如何将大量对象(10000个或更多)传递给着色器?

问题描述 投票:0回答:1

如果您需要更多信息,请询问我

我想通过一次请求绘制着色器在屏幕上显示的所有对象。

现在我必须在对象之间切换着色器,而且速度非常慢,通过此优化我可以赢得 100+ 帧。

我读到了有关 FBO 的内容,但不知怎的,我需要在用 FBO 编写的纹理上找到这些对象,所以我开始寻找如何将一堆位置和其他信息传递给着色器。

我发现它可以通过纹理传递,但是这里也有一些困难。

据我了解,我需要向纹理写入一个字节数组,因为我的坐标是f32。

我能写出来,但我根本不明白如何提取它。 网上关于glsl的资料很少,不知道为什么。

use macroquad::prelude::*;

#[macroquad::main("Texture")]
async fn main() {
    let bytes: Vec<u8> = vec![
        0, 255, 0, 255,
        255, 255, 0, 255,
        255, 0, 0, 255,
        255, 0, 255, 255
    ];
    let texture = Texture2D::from_rgba8(2, 2, &bytes); // here i write rgba array, but need write bytes of f32 array like [6.20, 50.31231, 44.1213, 994.44]

    let lens_material = load_material(
        ShaderSource::Glsl {
            vertex: VERTEX_SHADER,
            fragment: FRAGMENT_SHADER,
        },
        MaterialParams {
            uniforms: vec![
                UniformDesc::new("Size", UniformType::Float2),
                UniformDesc::new("Position", UniformType::Float2)
            ],
            textures: vec!["MyTexture".to_string()],
            ..Default::default()
        },
    )
        .unwrap();

    let size = vec2(300., 300.);
    let pos1 = vec2(screen_width()/2. - size.x / 8., screen_height()/2. - size.y / 8.);
    let mut pos2 = vec2(screen_width()/2. - size.x / 2., screen_height()/2. - size.y / 2.);


    let positions = vec![pos1, pos2];
    let bounds = vec2(14000., 14000.); // Position bounds
    let normalized_positions: Vec<Vec2> = positions.iter().map(|&p| p / bounds * 255.).collect();




    dbg!(normalized_positions);

    dbg!(screen_width(), screen_height());

    loop {
        clear_background(WHITE);


        lens_material.set_texture("MyTexture", texture.clone());
        lens_material.set_uniform("Size", size);
        lens_material.set_uniform("Position", pos1 + size / 2.);

        // Rect
        gl_use_material(&lens_material); {
            draw_rectangle(pos1.x, pos1.y, size.x, size.y, WHITE);
        } gl_use_default_material();

        draw_circle(screen_width() / 2., screen_height()/2., 60., PURPLE);
        lens_material.set_texture("MyTexture", texture.clone());
        lens_material.set_uniform("Size", size / 2.);
        lens_material.set_uniform("Position", pos2);

        // Circle
        gl_use_material(&lens_material); {
            draw_circle(pos2.x, pos2.y, size.x / 2., WHITE);
        } gl_use_default_material();


        next_frame().await;
    }
}

const FRAGMENT_SHADER: &'static str = r#"
    #version 300 es

    #ifdef GL_ES
        precision highp float;
    #endif

    uniform sampler2D MyTexture;

    in vec2 uv;
    out vec4 FragColor;

    void main() {
        float dist = length(uv - vec2(0.5, 0.5));

        // if (dist < 0.5) {
            vec4 texColor = texture(MyTexture, uv);
            FragColor = texColor;
        // }
    }
"#;

const VERTEX_SHADER: &'static str = r#"
    #version 300 es

    #ifdef GL_ES
        precision highp float;
    #endif

    uniform vec2 Size;
    uniform vec2 Position;

    in vec2 position;
    uniform mat4 Projection;
    uniform mat4 Model;

    out vec2 uv;

    void main() {
        gl_Position = Projection * Model * vec4(position, 0.0, 1.);

        vec2 normalizedPosition = (position - Position) / Size * 0.5 + 0.5; // [0; 1]
        uv = normalizedPosition;
    }
"#;

Aleksey 评论更新:

这些物体都是相同的形状(圆形),我想了解传输的基本原理,然后如果需要的话我可能可以实现它。

我已经通过制服传输了许多其他数据,但 4096 的限制对于当前任务来说太小了。

您附加的示例似乎适用于制服,但我还看到了一个带有实例和纹理的示例。

但是,我不明白opengl如何理解它前面有一个对象,如果实际上它是传输屏幕的碎片?

假设整个屏幕是白色的,上面有3个红色圆圈,gl_InstanceID会遍历它们吗?

我尝试这种方式,但因为我不明白它是如何工作的。 又看了一遍,现在我觉得InstanceID就是当前数据包的编号。

let size = vec2(300., 300);
let pos1 = vec2(screen_width()/2. - size.x / 8., screen_height()/2. - size.y / 8.);
 let mut pos2 = vec2(screen_width()/2. - size.x / 2., screen_height()/2. - size.y / 2.);

 let positions: Vec<f32> = vec![ 
pos1.x, // Pos 1 
pos1.y, // Pos 1 
0.0, // Pos 1 
0.0, // Pos 1 
pos2.x, // Pos 2 
pos2.y, // Pos 2 
0.0, // Pos 2 
0.0 // Pos 2 
];

 let mut bytes: [u8; 64] = [0u8; 64];
 let pos_bytes: &[u8] = bytemuck::cast_slice(&positions);

 for (i, &byte) in pos_bytes.iter().enumerate() { bytes[i] = byte;
 } let positions_texture = Texture2D::from_rgba8(4, 4, &bytes);

lens_material.set_texture("PositionsTexture", positions_texture.clone());

顶点:


 void main() { gl_Position = Projection * Model * vec4(position, 0.0, 1.);

 vec4 Position = texture(PositionsTexture, vec2(gl_InstanceID, gl_InstanceID));

 vec2 normalizedPosition = (position - Position.xy) / Size * 0.5 + 0.5; // [0; 1] uv = normalizedPosition;
 } 
rust glsl
1个回答
0
投票

我做了很多工作,这就是结果。

为了正确传输位置,您需要将它们转换为字节表示。

为了提取它们,您需要知道 Rust 使用什么格式的浮点值 - IEEE 754。

接下来,在着色器方面,您需要从字节恢复浮点值,恢复时也会出现一个小错误 - 因此您需要使用

round
将字节向上舍入。

我希望这能让别人的生活变得更轻松,因为我已经度过了75%的地狱,但我的任务还没有完全完成。

use macroquad::prelude::*;
use macroquad::prelude::FilterMode::Nearest;

#[macroquad::main("Texture")]
async fn main() {

    let bytes8: Vec<u8> = vec![
        0, 255, 0, 255, // Green
        255, 255, 0, 255, // Yellow
        255, 0, 0, 255, // Red
        255, 0, 255, 255 // Purple +-
    ];

    let texture = Texture2D::from_rgba8(2, 2, &bytemuck::cast_slice(&bytes8));
    texture.set_filter(Nearest);

    let material = load_material(
        ShaderSource::Glsl {
            vertex: VERTEX_SHADER,
            fragment: FRAGMENT_SHADER,
        },
        MaterialParams {
            uniforms: vec![
                UniformDesc::new("Size", UniformType::Float2),
                UniformDesc::new("Position", UniformType::Float2)
            ],
            textures: vec![
                "MyTexture".to_string(),
                "MyTexture2".to_string(),
            ],
            ..Default::default()
        },
    )
        .unwrap();

    let size = vec2(300., 300.);
    let pos1 = vec2(200., 222.);
    let pos2 = vec2(600., 300.);

    let positions_buffer: Vec<u8> = vec![
        vec2(pos1.x, pos1.y),
        vec2(pos2.x, pos2.y)
    ]
        .iter()
        .flat_map(|p| [
            p.x.to_le_bytes(),
            p.y.to_le_bytes(),
        ])
        .flatten()
        .collect();

    let width = 4;
    let height = 1;

    let texture2 = Texture2D::from_rgba8(width as u16, height as u16, &positions_buffer);
    texture2.set_filter(Nearest);

    loop {
        clear_background(WHITE);

        material.set_texture("MyTexture", texture.clone());
        material.set_texture("MyTexture2", texture2.clone());
        material.set_uniform("Size", size);
        material.set_uniform("Position", pos1);

        gl_use_material(&material); {
            draw_circle(pos1.x, pos1.y, size.x / 2., WHITE);
        } gl_use_default_material();

        gl_use_material(&material); {
            draw_circle(pos2.x, pos2.y, size.x / 2., WHITE);
        } gl_use_default_material();

        next_frame().await;
    }
}

const FRAGMENT_SHADER: &'static str = r#"
    #version 300 es

    #ifdef GL_ES
        precision highp float;
    #endif

    uniform sampler2D MyTexture;

    in vec2 uv;
    out vec4 FragColor;

    void main() {
        vec4 texColor = texture(MyTexture, uv);
        FragColor = texColor;
    }
"#;

const VERTEX_SHADER: &'static str = r#"
    #version 300 es

    #ifdef GL_ES
        precision highp float;
    #endif

    uniform vec2 Size;
    uniform vec2 Position;

    in vec2 position;
    uniform mat4 Projection;
    uniform mat4 Model;

    out vec2 uv;

    uniform sampler2D MyTexture2;

    // Float from bytes in IEEE 754 format
    float bytesToFloat32(int b0, int b1, int b2, int b3) {
        int intBits = (b0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
        float sign = ((intBits >> 31) == 0) ? 1.0 : -1.0;
        int exponent = ((intBits >> 23) & 0xFF) - 127;
        int mantissaBits = intBits & 0x7FFFFF;

        float mantissa = 1.0;
        float power = 0.5;

        for (int i = 22; i >= 0; i--) {
            if ((mantissaBits & (1 << i)) != 0) {
                mantissa += power;
            }
            power *= 0.5;
        }

        return sign * mantissa * pow(2.0, float(exponent));
    }

    vec4 getBytesFromTexture(float index) {
        vec2 uv = vec2(index, 1.);
        vec4 Bytes = texture(MyTexture2, uv);

        return Bytes * 255.;
    }

    in vec2 aOffset;

    void main() {

        gl_Position = Projection * Model * vec4(position, 0.0, 1.);

        int width = 4;
        float step = 1. / float(width);

        for (int i = 0; i < width / 2; i+=2) {
            vec4 X = getBytesFromTexture(step * float(i));
            vec4 Y = getBytesFromTexture(step * float(i+1));

            float x = bytesToFloat32(int(round(X.x)), int(round(X.y)), int(round(X.z)), int(round(X.w)));
            float y = bytesToFloat32(int(round(Y.x)), int(round(Y.y)), int(round(Y.z)), int(round(Y.w)));

            vec2 Position = vec2(x, y);

            vec2 normalizedPosition = (position - Position) / Size * 0.5 + 0.5; // [0; 1]
            uv = normalizedPosition;
        }

    }
"#;
© www.soinside.com 2019 - 2024. All rights reserved.