I did a lot of work and here is the result.
In order to correctly transfer the positions, you need to convert them to byte representation.
In order to extract them, you need to know what format of float values Rust uses - IEEE 754.
Next, on the shader side, you need to restore the float value from the bytes, it is also restored with a small error - so you need to round the bytes up using round.
I hope this will make someone's life easier, because I have passed 75% of hell, but my task is not completely finished.
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;
}
}
"#;