如果您需要更多信息,请询问我
我想通过一次请求绘制着色器在屏幕上显示的所有对象。
现在我必须在对象之间切换着色器,而且速度非常慢,通过此优化我可以赢得 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 使用什么格式的浮点值 - 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;
}
}
"#;