Reputation: 8295
I want to express the below struct into as numpy array.
struct UniformBufferObject {
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
};
Thereafter, the numpy array is meant to be read in as a GLSL layout:
layout(binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
In the above statements, mat4
denotes a 4x4 matrix.
Below, I created a python list which I then converts to a numpy array (w/o copying) with a 4x4 shape where each element is a dtype=float32
.
Question: What should I do next to represent the UniformBufferObject
struct as a numpy array?
>>> model = [0.1, 0.1, 0.1, 1, 0.1, 0.1, 0.1, 1, 0.1, 0.1, 0.1, 1, 0.1, 0.1, 0.1, 0,]
>>> view = [0.2, 0.2, 0.2, 1, 0.2, 0.2, 0.2, 1, 0.2, 0.2, 0.2, 1, 0.2, 0.2, 0.2, 0,]
>>> proj = [0.3, 0.3, 0.3, 1, 0.3, 0.3, 0.3, 1, 0.3, 0.3, 0.3, 1, 0.3, 0.3, 0.3, 0,]
>>> modeln = np.asarray(model, dtype= 'f4').reshape(4,4)
>>> viewn = np.asarray(view, dtype= 'f4').reshape(4,4)
>>> projn = np.asarray(proj, dtype= 'f4').reshape(4,4)
>>> modeln
array([[0.1, 0.1, 0.1, 1. ],
[0.1, 0.1, 0.1, 1. ],
[0.1, 0.1, 0.1, 1. ],
[0.1, 0.1, 0.1, 0. ]], dtype=float32)
>>> viewn
array([[0.2, 0.2, 0.2, 1. ],
[0.2, 0.2, 0.2, 1. ],
[0.2, 0.2, 0.2, 1. ],
[0.2, 0.2, 0.2, 0. ]], dtype=float32)
>>> projn
array([[0.3, 0.3, 0.3, 1. ],
[0.3, 0.3, 0.3, 1. ],
[0.3, 0.3, 0.3, 1. ],
[0.3, 0.3, 0.3, 0. ]], dtype=float32)
>>>
Upvotes: 1
Views: 1355
Reputation: 211135
If in GLSL a Uniform block is specified like this
layout(binding = 0) uniform UniformBufferObject { mat4 model; mat4 view; mat4 proj; } ubo;
then OpenGL does not guarantee, that the members in th block are stored in consecutive order. OpenGL gives no guarantees about the memory layout, alignment or order of the elements.
To achieve a well defined order and memory layout the memory layout qualifier std140
ahs to be used. The std140
layout is provided since OpenGL 3.1 respectively OpenGL ES 3.0 or the OpenGL extension ARB_uniform_buffer_object:
layout(std140, binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
A detailed specification about the layout can be found at:
In case of the above uniform block with std140
layout the 3 matrices are tightly packed. The memory layout are 48 (3*4*4) float
s in a row and has a size of 192 bytes
mat4 model byte offset 0: size in bytes 64 = 4*4*sizeof(float)
mat4 view byte offset 64: size in bytes 64 = 4*4*sizeof(float)
mat4 proj byte offset 128: size in bytes 64 = 4*4*sizeof(float)
--------------------------------------------------------------------
size in bytes 192 = 3*4*4*sizeof(float)
To create a buffer which corresponds to this memory layout, a flat numpy.array
can be used:
e.g.
model = [0.1, 0.1, 0.1, 1, 0.1, 0.1, 0.1, 1, 0.1, 0.1, 0.1, 1, 0.1, 0.1, 0.1, 0,] view = [0.2, 0.2, 0.2, 1, 0.2, 0.2, 0.2, 1, 0.2, 0.2, 0.2, 1, 0.2, 0.2, 0.2, 0,] proj = [0.3, 0.3, 0.3, 1, 0.3, 0.3, 0.3, 1, 0.3, 0.3, 0.3, 1, 0.3, 0.3, 0.3, 0,]
import numpy as np
ubo_data = np.array([model, view, proj], dtype='f4')
It is not necessary to reshap()
the array. But of course you can do so, if you want to access the elements of the model matrix (ubo_data[0]
), the view matrix (ubo_data[1]
) and the projection matrix (ubo_data[2]
) directly:
ubo_data = np.array([model, view, proj], dtype='f4').reshape(3, 4, 4)
Note, it is even not necessary to use NumPy. A buffer with the same memory layout can be generated by a ctypes array:
import ctypes
ubo_data = (ctypes.c_float * 48)(*model, *view, *proj)
Using PyOpenGL, each of the above data arrays can be used to crate and initialize the buffer store of a uniform block buffer by glBufferData
ubo = glGenBuffers( 1 )
glBindBuffer(GL_UNIFORM_BUFFER, ubo)
glBufferData(GL_UNIFORM_BUFFER, ubo_data, GL_STATIC_DRAW)
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo)
glBindBuffer(GL_UNIFORM_BUFFER, 0)
or update the data store of an existing buffer by glBufferSubData
:
glBindBuffer(GL_UNIFORM_BUFFER, ubo)
glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo_data)
Note, when using PyGLM, then the data of a uniform buffer like this can be updated directly:
e.g.:
import glm
model = glm.mat4(1)
view = glm.lookAt(glm.vec3(0,-3,0), glm.vec3(0,0,0), glm.vec3(0,0,1))
proj = glm.perspective(glm.radians(90), 800.0/600.0, 0.1, 100)
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, glm.sizeof(glm.mat4), glm.value_ptr(model))
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 1*glm.sizeof(glm.mat4), glm.sizeof(glm.mat4), glm.value_ptr(view))
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 2*glm.sizeof(glm.mat4), glm.sizeof(glm.mat4), glm.value_ptr(proj))
Upvotes: 1