Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pack(4bytes) and unpack(vec4) between c++ and GLSL

Tags:

c++

opengl

glsl

I would like to save memory and since input data are in range [0,..,255] i don't need 4xFloat but 4xByte is going to be enough. GLSL and gpu don't like bytes so pack and unpack is required. Also no precision loss should occur.

C++ and packing code:

The fastest and simplest solution should be using uint for transfer. GLM has already vector prepared so putting glm::u8vec4(1, 1, 1, 1) into vertex array should be all right. I checked vertex arrays with gDEBugger so i am sure they contains right data. VAP looks like this:

glVertexAttribPointer(
    0,
    1, // one element
    GL_UNSIGNED_INT, //transfer data type
    GL_FALSE, //dont normalize
    1 * sizeof(GLuint), // size of one element
    (void*)0 // offset
    );

GLSL and unpacking:

layout(location = 0) in uint data;
vec4 unpack;
unpack.x = float((data & uint(0xff000000)) >> 24);
unpack.y = float((data & uint(0x00ff0000)) >> 16);
unpack.z = float((data & uint(0x0000ff00)) >> 8);
unpack.w = float((data & uint(0x000000ff)) >> 0);

Result should provide hint but it's too much mess. result

Edit:

Even tho my solution is pointless, there are two bugs. First as mentioned below i have to use glVertexAttribIPointer. Second in my case shifts were mirrored(X should be shifted by 0, Y by 8,..).

Proper solution looks like this:

glVertexAttribPointer(
    0,
    4,
    GL_UNSIGNED_BYTE,
    GL_FALSE,
    4 * sizeof(GLubyte),
    (void*)0
);
shader:
layout(location = 0) in vec4 data;
like image 748
Keo Avatar asked Sep 16 '25 16:09

Keo


2 Answers

GLSL and gpu don't like bytes so pack and unpack is required. Also no precision loss should occur.

Why do you say that? Using GL_UNSIGNED_BYTE attribute data is well-supported and very common (e.g. for colors). There is no performance issue with that (as long as one adheres to basic alignment rules).

The GL will automatically convert integer attribute data to float. The normalized parameter defines if the data shall be normalized to the [0,1] (or [-1,1] for signed int types) range, or if it shall be converted directly to float, so that (GLubyte)255 will appear as 255.0f in the shader.

Your code fails because you need to use glVertexAttribIPointer() (note the I there) to set up integer attributes. But in your case, it really is a pointless exersice.

like image 127
derhass Avatar answered Sep 18 '25 05:09

derhass


Your C++ code is correct, but you can also use the glVertexAttribIPointer (I stands for Integer?). The last two values can be zero/nullptr, as there is only one value in the buffer. All parameters are meant for the currently bound buffer, except for the first one which applies to the currently bound VAO.

glVertexAttribIPointer(0, 1, GL_UNSIGNED_INT, 0, nullptr);

The OpenGL shader code should look like:

layout(location = 0) in uint data;
out vec4 color                     // I assume you want this to go 'out'?
void main()
{
    color = vec4(
        data & uint(0xFF000000),
        data & uint(0x00FF0000),
        data & uint(0x0000FF00),
        data & uint(0x000000FF));
}

Now you can store rgba values in the uint. Such as: 0xRRGGBBAA, supplying you for 256 values for every part.

like image 29
Yeti Avatar answered Sep 18 '25 06:09

Yeti