How am I going wrong in glMultiDrawArraysIndirect function? - python

I'm programming using Python and Modern OpenGL, and I tried to implement the glMultiDrawArraysIndirect function in my code to draw a simple shape, I want to apply it later on to a more complex thing, but this is just a simple test that I don't know exactly where the error is.
import glfw, time, ctypes, math, pyrr
import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import *
glfw.init()
glfw.window_hint(glfw.SAMPLES, 4)
w = glfw.create_window(640, 480, "Galeria das Sombras", None, None)
glfw.make_context_current(w)
v = """
#version 430
in layout(location=0) vec3 posicao;
in layout(location=1) vec2 textura;
uniform mat4 view;
uniform vec3 def;
uniform vec3 pos;
uniform vec3 scale;
uniform float giro;
uniform float giro2;
out vec2 texcords;
void main(){
texcords = textura;
vec3 p = vec3(posicao.x*scale.x,posicao.y*scale.y,posicao.z*scale.z);
p = p+def;
p = vec3(-sin(giro)*p.z+cos(giro)*p.x,p.y,sin(giro)*p.x+cos(giro)*p.z);
p = vec3(p.x,-sin(giro2)*p.z+cos(giro2)*p.y,sin(giro2)*p.y+cos(giro2)*p.z);
p = p+pos+vec3(gl_InstanceID,0,0);
gl_Position = view*vec4(p,1);
}
"""
f = """
#version 430
in vec2 texcords;
uniform vec3 cor;
uniform sampler2D texinfo;
void main(){
gl_FragColor = vec4(cor,1)*texture(texinfo,texcords);
}
"""
shader = compileProgram(compileShader(v,GL_VERTEX_SHADER),compileShader(f,GL_FRAGMENT_SHADER))
tudo = [-1,-1,0,0,1,
1,-1,0,1,1,
1,1,0,1,0,
-1,1,0,0,0]
tudo = np.array(tudo, np.float32)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, len(tudo)*4, tudo, GL_STATIC_DRAW)
'''
tudo = [[1,1,0,3],[2,2,1,3]]
tudo = np.array(tudo, np.uint8)
VBI = glGenBuffers(1)
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, VBI)
glBufferData(GL_DRAW_INDIRECT_BUFFER, len(tudo)*4, tudo, GL_STATIC_DRAW)
'''
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glUseProgram(shader)
view = pyrr.matrix44.create_perspective_projection_matrix(60, 640/480, .1, 1000)
p = glGetUniformLocation(shader, "view")
glUniformMatrix4fv(p, 1, GL_FALSE, view)
glEnable(GL_DEPTH_TEST)
glEnable(GL_MULTISAMPLE)
glEnable(GL_TEXTURE_2D)
from PIL import Image
t = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, t)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
buffer = Image.open("p.jpg")
data = buffer.tobytes()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, buffer.size[0], buffer.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, data)
girar = 0
tempo = glfw.get_time()
tfps = glfw.get_time()
fps = 0
action = 0
while not glfw.window_should_close(w):
glfw.swap_buffers(w)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
view = pyrr.matrix44.create_perspective_projection_matrix(60, glfw.get_window_size(w)[0]/glfw.get_window_size(w)[1], .1, 1000)
p = glGetUniformLocation(shader, "view")
glUniformMatrix4fv(p, 1, GL_FALSE, view)
glViewport(0,0,glfw.get_window_size(w)[0],glfw.get_window_size(w)[1])
p = glGetUniformLocation(shader, "cor")
glUniform3f(p, 1, 0, 0)
p = glGetUniformLocation(shader, "def")
glUniform3f(p, 0, 0, 0)
p = glGetUniformLocation(shader, "scale")
glUniform3f(p, 1, 1, 1)
p = glGetUniformLocation(shader, "giro")
glUniform1f(p, girar*math.pi/180)
if glfw.get_time() - tempo > 1/60:
girar+=1
tempo = glfw.get_time()
if action > 0:
action-=.05
if action < 0:
action = 0
p = glGetUniformLocation(shader, "giro2")
if glfw.get_key(w, glfw.KEY_W) and action == 0:
action = 2
if action > 1:
glUniform1f(p, (1-(action-1))*-90*(math.pi/180))
else:
glUniform1f(p, action*-90*(math.pi/180))
p = glGetUniformLocation(shader, "pos")
glUniform3f(p, 0, 0, -10)
glMultiDrawArraysIndirect(GL_TRIANGLES, np.array([[0,3,1,0],[1,3,1,1]]), 2, 1)
fps+=1
if glfw.get_time() - tfps > 1:
print("FPS:",fps)
fps = 0
tfps = glfw.get_time()
glfw.poll_events()
if fps > 400:
time.sleep(.01)
glfw.destroy_window(w)
glfw.terminate()
In the VBO there is a square, but I was going to draw only 2 triangles using the first 3 points and then the last 3 next to each other, I didn't find many examples of this type of code on the internet, only glMultiDrawArraysIndirect documentation but I couldn't do it run in my code, at least not without giving a good lock, when I change the drawcount for 1 wheel but nothing appears on the screen.
There is and the drawcount I took from the site: http://docs.gl/gl4/glMultiDrawArraysIndirect
I tried to change the indirect value for different types of numpy arrays, with different uint dtypes but most of them either error or run without anything appearing on the screen does anyone know what's wrong?

When you specify the NumPy array you need to specify the type uint32. The last argument (stride) is specifies the distance in basic machine units between elements of the draw parameter array (16 bytes):
(see glMultiDrawArraysIndirect)
indirect = np.array([[3, 10, 0, 0], [3, 5, 1, 0]], dtype=np.uint32)
glMultiDrawArraysIndirect(GL_TRIANGLES, indirect, 2, 16)
or
glMultiDrawArraysIndirect(GL_TRIANGLES, indirect,
indirect.shape[0], indirect.dtype.itemsize * indirect.shape[1])
The above code does the same as:
(see glDrawArraysIndirect)
indirect1 = np.array([3, 10, 0, 0], dtype=np.uint32)
glDrawArraysIndirect(GL_TRIANGLES, indirect1)
indirect2 = np.array([3, 5, 1, 0], dtype=np.uint32)
glDrawArraysIndirect(GL_TRIANGLES, indirect2)
Respectively the same as:
(see glDrawArraysInstancedBaseInstance)
glDrawArraysInstancedBaseInstance(GL_TRIANGLES, 0, 3, 10, 0)
glDrawArraysInstancedBaseInstance(GL_TRIANGLES, 1, 3, 5, 0)
When you use glDrawArraysIndirect or glMultiDrawArraysIndirect, you need to create the following data structure:
(see GLAPI/glMultiDrawArraysIndirect)
typedef struct {
uint count;
uint instanceCount;
uint first;
uint baseInstance;
} DrawArraysIndirectCommand;
This can be achieved using a NumPy array with the data type uint32:
np.array([count, instanceCount, first, baseInstance], dtype=np.uint32)

Related

pyopengl - texture is not rendering as its should

this is original 2d 256x256 image i am trying to texture on screen. but for some reason it does not rendering without glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) line ever. And when its rendering since pic is 2d i send attrib pointer uv is size of 2 with glVertexAttribPointer(attribute_texture, 2, GL_FLOAT, GL_FALSE, 0, None)
but its turns out this.
when i turn it to 3 just for trying sake its like this
what eer i try it did not change any better
what am i doing wrong here? i realy could use another pair of eyes. thank you.
And this is my code:
import glfw
from OpenGL.GL import *
# from OpenGL.GL.shaders import compileShader, compileProgram
import numpy as np
from math import radians
from pyrr import matrix44, Vector3
from PIL import Image
# ----------------------------------------------------------------------
if not glfw.init():
raise Exception("GLFW not initialized")
window = glfw.create_window(800, 600, "personal", None, None)
if not window:
glfw.terminate()
raise Exception("window did not created")
glfw.set_window_pos(window, xpos=200, ypos=50)
glfw.make_context_current(window)
# ----------------------------------------------------------------------
vertices = np.array([-0.7, 0.7, 0.0,
-0.7, -0.7, 0.0,
0.7, -0.7, 0.0,
0.7, 0.7, 0.0], dtype=np.float32)
indices = np.array([0, 1, 3,
3, 1, 2], dtype=np.uint32)
# color = np.array([0.0, 0.0, 0.0,
# 0.0, 0.0, 0.0,
# 0.0, 0.0, 0.0,
# 0.0, 0.0, 0.0], dtype=np.float32)
texture_coord = np.array([0, 0,
0, 1,
1, 1,
1, 0], dtype=np.uint32)
# --------------------------------------------------------------------- TRANSFORMATION CALCULATION
matrix = matrix44.create_identity(dtype=np.float32)
matrix = np.dot(matrix44.create_from_translation(Vector3([0.0, 0.0, 0.0])), matrix)
matrix = np.dot(matrix44.create_from_x_rotation(radians(0)), matrix)
matrix = np.dot(matrix44.create_from_y_rotation(radians(0)), matrix)
matrix = np.dot(matrix44.create_from_z_rotation(radians(0)), matrix)
matrix = np.dot(matrix44.create_from_scale(Vector3([1, 1, 1])), matrix)
# ---------------------------------------------------------------------
vertex_shader_src = """
#version 330 core
layout(location = 0)in vec3 position;
layout(location = 1)in vec2 texture;
//in vec3 color;//for using color equal it to toFColor
uniform mat4 trans;
out vec3 toFColor;
out vec2 passTexCoord;
void main(){
gl_Position = trans * vec4(position.x,position.y,position.z,1.0f);
//toFColor=color;
passTexCoord=texture;
}
"""
# ---------------------------------------------------------------------
fragment_shader_src = """
#version 330 core
//in vec3 toFColor;
in vec2 passTexCoord;
//uniform vec3 triColor;
uniform sampler2D texture_sampler;
out vec4 outColor;
void main(){
//outColor = vec4(toFColor,1.0);
outColor = texture(texture_sampler, passTexCoord);
}
"""
# ---------------------------------------------------------------------PyOpenGL working shader program
# shader_program = compileProgram(compileShader(vertex_shader_src, GL_VERTEX_SHADER),
# compileShader(fragment_shader_src, GL_FRAGMENT_SHADER))
# ---------------------------------------------------------------------tutorial way shader program
vertex_shader_id = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex_shader_id, vertex_shader_src)
glCompileShader(vertex_shader_id)
if glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS) == GL_FALSE:
print(glGetShaderInfoLog(vertex_shader_id))
print("cant compile shader")
fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment_shader_id, fragment_shader_src)
glCompileShader(fragment_shader_id)
if glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS) == GL_FALSE:
print(glGetShaderInfoLog(fragment_shader_id))
print("cant compile shader")
shader_program = glCreateProgram()
glAttachShader(shader_program, vertex_shader_id)
glAttachShader(shader_program, fragment_shader_id)
glBindAttribLocation(shader_program, 0, "position")
glBindAttribLocation(shader_program, 1, "texture")
glLinkProgram(shader_program)
glValidateProgram(shader_program)
glUseProgram(shader_program)
# --------------------------------------------------------------------- my trying for VAO vertex positions
VAO = glGenVertexArrays(1) # generate vao
glBindVertexArray(VAO) # ready VAO to use
VBO1 = glGenBuffers(1) # generate vbo
glBindBuffer(GL_ARRAY_BUFFER, VBO1) # binding vbo for use
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW) # setting VBO what to carry
attribute_position = glGetAttribLocation(shader_program, "position") # taking attribute name position from shader
glEnableVertexAttribArray(attribute_position) # ready to use attribute
glVertexAttribPointer(attribute_position, 3, GL_FLOAT, GL_FALSE, 0,
None) # telling OpenGL how to read data on given uniform
# ---------------------------------------------------------------------- indexing positions
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW)
# ---------------------------------------------------------------------- coloring instead of texture
# i am closing this because i will use texture
# VBO2 = glGenBuffers(1)
# glBindBuffer(GL_ARRAY_BUFFER, VBO2)
# glBufferData(GL_ARRAY_BUFFER, color.nbytes, color, GL_STATIC_DRAW)
# attribute_color = glGetAttribLocation(shader_program, "color") # taking uniform name color from shader
# glEnableVertexAttribArray(attribute_color) # ready to use uniform
# glVertexAttribPointer(attribute_color, 3, GL_FLOAT, GL_FALSE, 0,
# None) # telling OpenGL how to read data on given attribute
# ---------------------------------------------------------------------- Texture pressing
TEX_BO = glGenTextures(1)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, TEX_BO)
attribute_texture = glGetAttribLocation(shader_program, "texture")
glEnableVertexAttribArray(attribute_texture)
glVertexAttribPointer(attribute_texture, 3, GL_FLOAT, GL_FALSE, 0, None)
texture = Image.open("../res/pic1.png", "r")
texture = texture.transpose(Image.FLIP_TOP_BOTTOM)
image_data = texture.convert("RGBA").tobytes()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# Set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
# ---------------------------------------------------------------------- TRANSFORMATION MATRIX -------------------------
loc_uniform_transformation = glGetUniformLocation(shader_program, "trans")
# ----------------------------------------------------------------------
while not glfw.window_should_close(window):
glfw.poll_events()
glClearColor(0.15, 0.15, 0.15, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUniformMatrix4fv(loc_uniform_transformation, 1, GL_FALSE, matrix44.create_identity())
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glfw.swap_buffers(window)
# ------------------------------------------------------------------------------ Cleaning
glDeleteTextures(1, int(TEX_BO))
glDetachShader(shader_program, vertex_shader_id)
glDetachShader(shader_program, fragment_shader_id)
glDeleteShader(vertex_shader_id)
glDeleteShader(fragment_shader_id)
glDeleteProgram(shader_program)
glDeleteBuffers(1, int(VBO1))
# glDeleteBuffers(1, int(VBO2)) # if use colors
glDeleteBuffers(1, int(EBO))
glDeleteVertexArrays(1, int(VAO))
# ------------------------------------------------------------------------------
glfw.destroy_window(window)
glfw.terminate()
You have to set glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) because the default minifying function is GL_NEAREST_MIPMAP_LINEAR. Since you do not generate mipmaps the texture would be "Mipmap Incomplete" if you do not change the minimize function to GL_NEAREST or GL_LINEAR.
The type of the array of texture coordinates must be np.float32 rather than np.uint32:
texture_coord = np.array([0, 0,
0, 1,
1, 1,
1, 0], dtype=np.float32)
You missed to create the buffer object for the texture coordinates:
VBO2 = glGenBuffers(1) # generate vbo
glBindBuffer(GL_ARRAY_BUFFER, VBO2) # binding vbo for use
glBufferData(GL_ARRAY_BUFFER, texture_coord.nbytes, texture_coord, GL_STATIC_DRAW)
attribute_texture = glGetAttribLocation(shader_program, "texture")
glEnableVertexAttribArray(attribute_texture)
glVertexAttribPointer(attribute_texture, 2, GL_FLOAT, GL_FALSE, 0, None)
When glVertexAttribPointer is called, the buffer object currently bound to the target GL_ARRAY_BUFFER is associated to the specified vertex attribute.

How to render text with PyOpenGL?

I'm learning modern openGL, and at this moment I'm facing trouble with rendering text. I'm following this tutorial which is in C++, but I'm trying to implement in python.
Here is my code:
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
import glfw
import freetype
import glm
import numpy as np
from PIL import Image
import math
import time
class CharacterSlot:
def __init__(self, texture, glyph):
self.texture = texture
self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)
if isinstance(glyph, freetype.GlyphSlot):
self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
self.advance = glyph.advance.x
elif isinstance(glyph, freetype.BitmapGlyph):
self.bearing = (glyph.left, glyph.top)
self.advance = None
else:
raise RuntimeError('unknown glyph type')
def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
return np.asarray([
xpos, ypos - h, zfix, 0.0, 1.0,
xpos, ypos, zfix, 0.0, 0.0,
xpos + w, ypos, zfix, 1.0, 0.0,
xpos, ypos - h, zfix, 0.0, 1.0,
xpos + w, ypos, zfix, 1.0, 0.0,
xpos + w, ypos - h, zfix, 1.0, 1.0
], np.float32)
VERTEX_SHADER = """
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
"""
FRAGMENT_SHADER = """
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}
"""
shaderProgram = None
Characters = dict()
VBO = None
VAO = None
def initliaze():
global VERTEXT_SHADER
global FRAGMENT_SHADER
global shaderProgram
global Characters
global VBO
global VAO
#compiling shaders
vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
#creating shaderProgram
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
#get projection
#problem
shader_projection = glGetUniformLocation(shaderProgram, "projection")
projection = glm.ortho(0.0,640,0.0,640)
glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection));
#disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
face = freetype.Face("Vera.ttf")
face.set_char_size( 48*64 )
#load first 128 characters of ASCII set
for i in range(0,128):
face.load_char(chr(i))
glyph = face.glyph
#generate texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
glyph.bitmap.width, glyph.bitmap.rows,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
glyph.bitmap.buffer)
#texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#now store character for later use
Characters[chr(i)] = CharacterSlot(texture,glyph)
glBindTexture(GL_TEXTURE_2D, 0);
#configure VAO/VBO for texture quads
VAO = glGenVertexArrays(1)
glBindVertexArray(VAO)
VBO = glGenBuffers(1);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, 6 * 4, None, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
def render_text(window,text,x,y,scale,color):
global shaderProgram
global Characters
global VBO
global VAO
face = freetype.Face("Vera.ttf")
face.set_char_size(48*64)
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
color[0]/255,color[1]/255,color[2]/255)
glActiveTexture(GL_TEXTURE0);
for c in text:
ch = Characters[c]
w,h = ch.textureSize
w = w*scale
h = w*scale
vertices = _get_rendering_buffer(x,y,w,h)
#render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.texture);
#update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, len(vertices), vertices)
glBindBuffer(GL_ARRAY_BUFFER, 0);
#render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
#now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance+6)*scale;
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glfwSwapBuffers(window);
glfwPollEvents();
def main():
glfw.init()
window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
glfw.make_context_current(window)
initliaze()
while not glfw.window_should_close(window):
glfw.poll_events()
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
render_text(window,'hello',1,1,1,(100,100,100))
glfw.terminate()
if __name__ == '__main__':
main()
I'm facing trouble in two portion so far I can understand. The first problem in initliaze(), error raised for the following portion.
shader_projection = glGetUniformLocation(shaderProgram, "projection")
projection = glm.ortho(0.0,640,0.0,640)
glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection));
I've commented out the above portion to ignore. The second problem is in render_text() function, error raised for the following portion.
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
color[0]/255,color[1]/255,color[2]/255)
There could be problems in many more places. I don't understand that why text rendering will be so difficult. What am I missing here?
You missed to install the shader program by glUseProgram:
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
glUseProgram(shaderProgram) # <---
The 2nd argument to glBufferData and the 3rd argument of glBufferSubData is the size in bytes:
glBufferData(GL_ARRAY_BUFFER, 6 * 4, None, GL_DYNAMIC_DRAW)
glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_DYNAMIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, 0, len(vertices), vertices)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)
The vertex attribute consist of a 2 dimension vertex coordinate (x, y) and a 2 dimensional texture coordinate. Remove the wird zfix from the array of vertex attribute data. Furthermore you have to flip the 2nd component of the texture coordinates (otherwise the text is upside down)
def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
return np.asarray([
xpos, ypos - h, 0, 0,
xpos, ypos, 0, 1,
xpos + w, ypos, 1, 1,
xpos, ypos - h, 0, 0,
xpos + w, ypos, 1, 1,
xpos + w, ypos - h, 1, 0
], np.float32)
The stride argument of glVertexAttribIPointer has to be specified in bytes. If stride is 0, the generic vertex attributes are understood to be tightly packed in the array. Hence in your case stride has to be 16 or 0:
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4, 0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
face.load_char(chr(i)) generates a image with on color channel (1 byte per pixel). Use the internal format and format GL_RED rather than GL_RGB for generating the 2 dimensional texture image:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer)
You have to bind the vertex array, before drawing the text:
glBindVertexArray(VAO)
for c in text:
# [...]
glDrawArrays(GL_TRIANGLES, 0, 6)
There is typo when you increment x, you have to use the >>-operator rather than the +-operator:
x += (ch.advance+6)*scale
x += (ch.advance>>6)*scale
and another typo when you compute h:
h = w*scale
h = h*scale
You have to enable alpha blending:
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
In NDC (normalized device coordinates) the left bottom is (-1, -1) and the right top is (1, 1). Set the orthographic projection in that way, that the top left of the window is at (0, 0):
projection = glm.ortho(0.0,640,0.0,640)
projection = glm.ortho(0, 640, 640, 0)
The reference point of the text is at the bottom. Hence you have to set a x coordinate greater than the text height:
render_text(window,'hello',1,1,1,(100,100,100))
render_text(window,'hello', 20, 50, 1, (255, 100, 100))
See the complete example (I've used a different font):
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
import glfw
import freetype
import glm
import numpy as np
from PIL import Image
import math
import time
fontfile = "Vera.ttf"
#fontfile = r'C:\source\resource\fonts\gnu-freefont_freesans\freesans.ttf'
class CharacterSlot:
def __init__(self, texture, glyph):
self.texture = texture
self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)
if isinstance(glyph, freetype.GlyphSlot):
self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
self.advance = glyph.advance.x
elif isinstance(glyph, freetype.BitmapGlyph):
self.bearing = (glyph.left, glyph.top)
self.advance = None
else:
raise RuntimeError('unknown glyph type')
def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
return np.asarray([
xpos, ypos - h, 0, 0,
xpos, ypos, 0, 1,
xpos + w, ypos, 1, 1,
xpos, ypos - h, 0, 0,
xpos + w, ypos, 1, 1,
xpos + w, ypos - h, 1, 0
], np.float32)
VERTEX_SHADER = """
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
"""
FRAGMENT_SHADER = """
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}
"""
shaderProgram = None
Characters = dict()
VBO = None
VAO = None
def initliaze():
global VERTEXT_SHADER
global FRAGMENT_SHADER
global shaderProgram
global Characters
global VBO
global VAO
#compiling shaders
vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
#creating shaderProgram
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
glUseProgram(shaderProgram)
#get projection
#problem
shader_projection = glGetUniformLocation(shaderProgram, "projection")
projection = glm.ortho(0, 640, 640, 0)
glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection))
#disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
face = freetype.Face(fontfile)
face.set_char_size( 48*64 )
#load first 128 characters of ASCII set
for i in range(0,128):
face.load_char(chr(i))
glyph = face.glyph
#generate texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer)
#texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
#now store character for later use
Characters[chr(i)] = CharacterSlot(texture,glyph)
glBindTexture(GL_TEXTURE_2D, 0)
#configure VAO/VBO for texture quads
VAO = glGenVertexArrays(1)
glBindVertexArray(VAO)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_DYNAMIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
def render_text(window,text,x,y,scale,color):
global shaderProgram
global Characters
global VBO
global VAO
face = freetype.Face(fontfile)
face.set_char_size(48*64)
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
color[0]/255,color[1]/255,color[2]/255)
glActiveTexture(GL_TEXTURE0)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glBindVertexArray(VAO)
for c in text:
ch = Characters[c]
w, h = ch.textureSize
w = w*scale
h = h*scale
vertices = _get_rendering_buffer(x,y,w,h)
#render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.texture)
#update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)
glBindBuffer(GL_ARRAY_BUFFER, 0)
#render quad
glDrawArrays(GL_TRIANGLES, 0, 6)
#now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance>>6)*scale
glBindVertexArray(0)
glBindTexture(GL_TEXTURE_2D, 0)
glfw.swap_buffers(window)
glfw.poll_events()
def main():
glfw.init()
window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
glfw.make_context_current(window)
initliaze()
while not glfw.window_should_close(window):
glfw.poll_events()
glClearColor(0,0,0,1)
glClear(GL_COLOR_BUFFER_BIT)
render_text(window,'hello', 20, 50, 1, (255, 100, 100))
glfw.terminate()
if __name__ == '__main__':
main()
See also FreeType / OpenGL text rendering

How to add texture to a triangle?

I'm trying to add woody texture to the triangle. Code works fine for only triangle. But raises error while I try to add texture. I think the problem is in the GLSL or in creating EBO/VBO (not sure). The whole screen remains black.
Here is the whole code. What am I doing wrong here?
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
import glfw
import numpy as np
from PIL import Image
VERTEX_SHADER = """
#version 330
layout (location = 0) in vec4 position;
in vec2 InTexCoords;
out vec2 OutTexCoords;
void main(){
gl_Position = position;
OutTexCoords = InTexCoords;
}
"""
FRAGMENT_SHADER = """
#version 330
out vec4 FragColor;
uniform vec4 triangleColor;
in vec2 OutTexCoords;
uniform sampler2D sampleTex;
void main() {
FragColor = texture(sampleTex,OutTexCoords);
}
"""
shaderProgram = None
def initialize():
global VERTEXT_SHADER
global FRAGMENT_SHADER
global shaderProgram
#compiling shaders
vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
#creating shaderProgram
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
#vertex and indices data
#triangle #texture
vertices = [-0.5, -0.5, 0.0, 0.0,0.0,
0.5, -0.5, 0.0, 1.0,0.0,
0.0, 0.5, 0.0, 0.5,1.0]
indices = [0,1,2]
vertices = np.array(vertices, dtype=np.float32)
indices = np.array(vertices, dtype=np.float32)
#add vertices to buffer
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
#add indices to buffer
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
position = 0
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(position)
texCoords = 1
glBindAttribLocation( shaderProgram, texCoords, 'InTexCoords')
glVertexAttribPointer(texCoords,2, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(texCoords)
#creating texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
image = Image.open("wood.jpg")
img_data = np.array(list(image.getdata()), np.uint8)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
def render(window):
global shaderProgram
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(shaderProgram)
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, None)
glUseProgram(0)
glfw.swap_buffers(window)
def main():
glfw.init()
window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
glfw.make_context_current(window)
initialize()
while not glfw.window_should_close(window):
glfw.poll_events()
render(window)
glfw.terminate()
if __name__ == '__main__':
main()
I was trying to follow this tutorial learnopengl. But the tutorial is in C++. Besides, I had slightly different approach. I'm not adding color codes in vertices. But I don't think it is the problem in the way of adding the texture.
The stride argument of glVertexAttribPointer specifies the byte offset between consecutive generic vertex attributes. Your attributes consist of vertex coordinates with 3 components and texture coordinates with 2 components. Hence your stride argument has to 20 (5 * 4 bytes) rather than 24:
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(0))
glVertexAttribPointer(texCoords,2, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glVertexAttribPointer(texCoords,2, GL_FLOAT, GL_FALSE, 20, ctypes.c_void_p(12))
The data type of the indices has to be integral. The type in the draw call (glDrawElements(..., ..., GL_UNSIGNED_INT, ...)) has to match this type. Use uint32 rather than float (and vertices -> indices):
indices = np.array(vertices, dtype=np.float32)
indices = np.array(indices, dtype=np.uint32)
Associating a generic vertex attribute index with a named attribute variable (glBindAttribLocation) has to be done, before the program is linked (before glLinkProgram).
I recommend to set the attribute index by a Layout Qualifier:
layout (location = 0) in vec4 position;
layout (location = 1) in vec2 InTexCoords;

Rendered model disappears at some asymptote

I've been trying to render a simple teapot with PyOpenGL, but have been running into strange issues. I can't seem to figure out exactly where the error originates from, despite the simplicity of the code.
Main.py
import pygame
from pygame.locals import *
from MV import *
import ctypes
from OpenGL.GL import *
from OpenGL.GL import shaders
from OpenGL.GLU import *
import teapot as tp
vertex_shader = '''
#version 420
in vec3 vpos_modelspace;
in vec3 vnorm_modelspace;
uniform mat4 mvp;
out vec4 vertcolor;
void main(){
vertcolor = vec4(vnorm_modelspace, 1.0);
gl_Position = mvp * vec4(vpos_modelspace, 1.0);
}
'''
fragment_shader = '''
#version 420
in vec4 vertcolor;
out vec4 fragcolor;
void main(){
fragcolor = vertcolor;
}
'''
model = tp.teapot
pygame.init()
canvas = pygame.display.set_mode((800, 600), DOUBLEBUF|OPENGL)
pygame.display.set_caption('Test')
glClearColor(.5, .5, .5, 1)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LESS)
glDisable(GL_CULL_FACE)
VERTEXSHADER = shaders.compileShader(vertex_shader, GL_VERTEX_SHADER)
FRAGMENTSHADER = shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER)
program = shaders.compileProgram(VERTEXSHADER, FRAGMENTSHADER)
glUseProgram(program)
vpos_loc = glGetAttribLocation(program, 'vpos_modelspace')
vnorm_loc = glGetAttribLocation(program, 'vnorm_modelspace')
mvp_loc = glGetUniformLocation(program, 'mvp')
eye = numpy.array([0, 0, 1], dtype=numpy.float32)
at = numpy.array([0, 0, 0], dtype=numpy.float32)
up = numpy.array([0, 1, 0], dtype=numpy.float32)
mvp = frustum(-1, 1, 1, -1, .1, 1000)#lookAt(eye, at, up)
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
vbo_pos = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo_pos)
vbo_norm = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo_norm)
verts = []
normals = []
for i in range(0, len(model.faces), 3):
index = model.faces[i:i+3]
verts.extend(model.vertices[3*index[0]:3*index[0]+3])
verts.extend(model.vertices[3*index[1]:3*index[1]+3])
verts.extend(model.vertices[3*index[2]:3*index[2]+3])
normals.extend(model.normals[3*index[0]:3*index[0]+3])
normals.extend(model.normals[3*index[1]:3*index[1]+3])
normals.extend(model.normals[3*index[2]:3*index[2]+3])
verts = numpy.array(verts, dtype=numpy.float32)
normals = numpy.array(normals, dtype=numpy.float32)
glBindBuffer(GL_ARRAY_BUFFER, vbo_pos)
glBufferData(GL_ARRAY_BUFFER, verts.size * verts.itemsize, verts, GL_STATIC_DRAW)
glVertexAttribPointer(vpos_loc, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glEnableVertexAttribArray(vpos_loc)
glBindBuffer(GL_ARRAY_BUFFER, vbo_norm)
glBufferData(GL_ARRAY_BUFFER, normals.size * normals.itemsize, normals, GL_STATIC_DRAW)
glVertexAttribPointer(vnorm_loc, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glEnableVertexAttribArray(vnorm_loc)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
while(True):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glUseProgram(program)
rotation_matrix = rotate(.01, [0, 1, 0])
mvp = mvp # rotation_matrix
glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, mvp.flatten())
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, int(verts.size/3))
glBindVertexArray(0)
glUseProgram(0)
pygame.display.flip()
main()
MV.py
import numpy
def normalize(vector):
return vector/numpy.linalg.norm(vector)
def translate(pos):
return numpy.array([[1, 0, 0, pos[0]],
[0, 1, 0, pos[1]],
[0, 0, 1, pos[2]],
[0, 0, 0, 1]], dtype=numpy.float32)
def rotate(angle, axis):
rads = angle * numpy.pi/180
v = normalize(axis)
c = numpy.cos(rads)
omc = 1-c
s = numpy.sin(rads)
return numpy.array([[v[0]*v[0]*omc + c, v[0]*v[1]*omc - v[2]*s, v[0]*v[2]*omc + v[1]*s, 0],
[v[0]*v[1]*omc + v[2]*s, v[1]*v[1]*omc + c, v[1]*v[2]*omc - v[0]*s, 0],
[v[0]*v[2]*omc - v[1]*s, v[1]*v[2]*omc + v[0]*s, v[2]*v[2]*omc + c, 0],
[0, 0, 0, 1]], dtype=numpy.float32)
def lookAt(eye, at, up):
n = normalize(at-eye)
u = normalize(numpy.cross(n, up))
v = normalize(numpy.cross(u, n))
rotate = numpy.array([[u[0], v[0], -n[0], 0],
[u[1], v[1], -n[1], 0],
[u[2], v[2], -n[2], 0],
[0, 0, 0, 1]], dtype=numpy.float32).transpose()
return rotate#translate(-eye)
def frustum(left, right, top, bottom, near, far):
rl = right-left
tb = top-bottom
fn = far-near
return numpy.array([[2*near/rl, 0, (right+left)/rl, 0],
[0, 2*near/tb, (top+bottom)/tb, 0],
[0, 0, -(far+near)/fn, -(2*far*near)/fn],
[0, 0, -1, 0]], dtype=numpy.float32)
The output shows the teapot being rotated (though not about the axis that I expected) and sort of shrinking and disappearing at rotations of 0, pi, 2pi, etc. I believe the teapot vertices are being processed correctly, as it does show up when rotated and is correctly shaded with normal values.
Output at 5 degrees - Model is 'growing'
Output at 30 degrees - Strange culling?
Output at 60 degrees - Relatively normal
Output at 170 degrees - Model is 'shrinking'
Output at 190 degrees - Model is 'growing' on the other side of the plane
At rotations 0, pi, 2pi, etc the model is completely invisible/too small to see.

Visualizing a 3D NumPy array with PyOpenGL

I want to create a PyOpenGL/QtOpenGL widget that will allow me to visualize an arbitrary NumPy 3D matrix, not unlike the following Hinton diagram envisioned as a "cube of cubes" instead of a "square of squares":
I'm having a bit of a rough time with OpenGL though. Here is my code thus far:
from OpenGL.GL import *
from OpenGL.GLUT import *
from PyQt4 import QtGui, QtOpenGL
import numpy as np
action_keymap = {
# 'a': lambda: glTranslate(-1, 0, 0),
# 'd': lambda: glTranslate( 1, 0, 0),
# 'w': lambda: glTranslate( 0, 1, 0),
# 's': lambda: glTranslate( 0,-1, 0),
'a': lambda: glRotate(-5, 0, 1, 0),
'd': lambda: glRotate( 5, 0, 1, 0),
# 'W': lambda: glRotate(-5, 1, 0, 0),
# 'S': lambda: glRotate( 5, 1, 0, 0),
}
ARRAY = np.ones([3,3,3])
class GLWidget(QtOpenGL.QGLWidget):
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
for idx, value in np.ndenumerate(ARRAY):
rel_pos = np.array(idx)/np.max(ARRAY.shape)
glTranslate(* rel_pos)
glutSolidCube(0.9/np.max(ARRAY.shape))
glTranslate(*-rel_pos)
def resizeGL(self, w, h):
glLoadIdentity()
glRotate(35,1,0,0)
glRotate(45,0,1,0)
def initializeGL(self):
glClearColor(0.1, 0.1, 0.3, 1.0)
def keyPressEvent(self, event):
action = action_keymap.get(str(event.text()))
if action:
action()
self.updateGL()
def mousePressEvent(self, event):
super().mousePressEvent(event)
self.press_point = event.pos()
def mouseMoveEvent(self, event):
super().mouseMoveEvent(event)
motion = event.pos()-self.press_point
self.press_point = event.pos()
glRotate(motion.x(),0,1,0)
glRotate(motion.y(),1,0,0)
self.updateGL()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = GLWidget()
w.show()
sys.exit(app.exec_())
My problems are as follows:
1) Lighting. I've been reading up on lighting and materials, but I cannot seem to get a simple light somewhere giving the shape some clarity. I'd like the simplest, most basic possible light to be able to distinguish the squares instead of them being all rendered as pure white on all sides. I know how to change the color, but it doesn't alleviate the problem. What is the simplest light I can shine on this lattice to get some clarity on the subcomponents?
2) It is slow. I'll work out the math to achieve proper positioning and resizing of squares down the line, but I was wondering if there was a way to vectorize the process (after all, it's only turning the index into a translation and the value into a cube size for every element in the array). Should I write an extension in cpp, wrap my code with ctypes, or is there a way to outsource the work to OpenGL explicitly? What is the standard way to send a repetitive task to OpenGL from Python?
This task is perfectly suited for Instancing. With instancing an object can be rendered multiple times.
In this case instancing is used to render a cube for ach element of a 3d NumPy array.
Lets assume we've the following 3D array (array3d) of random values in the range [0, 1]:
shape = [5, 4, 6]
number_of = shape[0] * shape[1] * shape[2]
array3d = np.array(np.random.rand(number_of), dtype=np.float32).reshape(shape)
For each element of the array an instance of a mesh (cube) has to be rendered:
e.g.
number_of = array3d.shape[0] * array3d.shape[1] * array3d.shape[2]
glDrawElementsInstanced(GL_TRIANGLES, self.__no_indices, GL_UNSIGNED_INT, None, number_of)
The array can be loaded to a 3D texture (glTexImage3D):
glActiveTexture(GL_TEXTURE1)
tex3DObj = glGenTextures(1)
glBindTexture(GL_TEXTURE_3D, tex3DObj)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0)
glTexImage3D(GL_TEXTURE_3D, 0, GL_R16F, *array3d.shape, 0, GL_RED, GL_FLOAT, array3d)
In the vertex shader for a single cube, a instance transformation matrix can be computes by the dimension of the 3D texture (which is equal the shape of the 3D array) and the gl_InstanceID of the element cube.
The element cube is further scaled by the value of the element in the 3D texture.
Assuming a vertex shader with a §D texture sampler uniform u_array3D and a vertex coordinate attribute a_pos:
in vec3 a_pos;
uniform sampler3D u_array3D;
The dimension of the texture can be get by textureSize:
ivec3 dim = textureSize(u_array3D, 0);
With the dimension and the gl_InstanceID, the index of the element can be computed:
ivec3 inx = ivec3(0);
inx.z = gl_InstanceID / (dim.x * dim.y);
inx.y = (gl_InstanceID - inx.z * dim.x * dim.y) / dim.x;
inx.x = gl_InstanceID - inx.z * dim.x * dim.y - inx.y * dim.x;
and the value of the element can be fetched (texelFetch):
float value = texelFetch(u_array3D, inx, 0).x;
Finally a instance transformation matrix dependent on the element index and element value can be calculated:
vec3 scale = 1.0 / vec3(dim);
scale = vec3(min(scale.x, min(scale.y, scale.z)));
vec3 trans = 2 * scale * (vec3(inx) - vec3(dim-1) / 2.0);
mat4 instanceMat = mat4(
vec4(scale.x * cube_scale, 0.0, 0.0, 0.0),
vec4(0.0, scale.y * cube_scale, 0.0, 0.0),
vec4(0.0, 0.0, scale.z * cube_scale, 0.0),
vec4(trans, 1.0)
);
vec4 instance_pos = instanceMat * vec4(a_pos, 1.0);
The value can be additionally visualized by the color of the cube. For this the floating point value in the range [0.0, 1.0] is transformed to a RGB color in the HSV color range:
vec3 HUEtoRGB(in float H)
{
float R = abs(H * 6.0 - 3.0) - 1.0;
float G = 2.0 - abs(H * 6.0 - 2.0);
float B = 2.0 - abs(H * 6.0 - 4.0);
return clamp( vec3(R,G,B), 0.0, 1.0 );
}
vec3 color = HUEtoRGB(0.66 * (1-0 - value));
See also OpenGL - Python examples
Pure NumPy / PyOpenGL example program. The values of the array are changed randomly:
import numpy as np
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GL.shaders import *
class MyWindow:
__glsl_vert = """
#version 450 core
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec3 a_nv;
layout (location = 2) in vec4 a_col;
out vec3 v_pos;
out vec3 v_nv;
out vec4 v_color;
layout (binding = 1) uniform sampler3D u_array3D;
uniform mat4 u_proj;
uniform mat4 u_view;
uniform mat4 u_model;
vec3 HUEtoRGB(in float H)
{
float R = abs(H * 6.0 - 3.0) - 1.0;
float G = 2.0 - abs(H * 6.0 - 2.0);
float B = 2.0 - abs(H * 6.0 - 4.0);
return clamp( vec3(R,G,B), 0.0, 1.0 );
}
void main()
{
ivec3 dim = textureSize(u_array3D, 0);
vec3 scale = 1.0 / vec3(dim);
scale = vec3(min(scale.x, min(scale.y, scale.z)));
ivec3 inx = ivec3(0);
inx.z = gl_InstanceID / (dim.x * dim.y);
inx.y = (gl_InstanceID - inx.z * dim.x * dim.y) / dim.x;
inx.x = gl_InstanceID - inx.z * dim.x * dim.y - inx.y * dim.x;
float value = texelFetch(u_array3D, inx, 0).x;
vec3 trans = 2 * scale * (vec3(inx) - vec3(dim-1) / 2.0);
mat4 instanceMat = mat4(
vec4(scale.x * value, 0.0, 0.0, 0.0),
vec4(0.0, scale.y * value, 0.0, 0.0),
vec4(0.0, 0.0, scale.z * value, 0.0),
vec4(trans, 1.0)
);
mat4 model_view = u_view * u_model * instanceMat;
mat3 normal = transpose(inverse(mat3(model_view)));
vec4 view_pos = model_view * vec4(a_pos.xyz, 1.0);
v_pos = view_pos.xyz;
v_nv = normal * a_nv;
v_color = vec4(HUEtoRGB(0.66 * (1-0 - value)), 1.0);
gl_Position = u_proj * view_pos;
}
"""
__glsl_frag = """
#version 450 core
out vec4 frag_color;
in vec3 v_pos;
in vec3 v_nv;
in vec4 v_color;
void main()
{
vec3 N = normalize(v_nv);
vec3 V = -normalize(v_pos);
float ka = 0.1;
float kd = max(0.0, dot(N, V)) * 0.9;
frag_color = vec4(v_color.rgb * (ka + kd), v_color.a);
}
"""
def __init__(self, w, h):
self.__caption = 'OpenGL Window'
self.__vp_valid = False
self.__vp_size = [w, h]
glutInit()
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(self.__vp_size[0], self.__vp_size[1])
self.__glut_wnd = glutCreateWindow(self.__caption)
self.__program = compileProgram(
compileShader( self.__glsl_vert, GL_VERTEX_SHADER ),
compileShader( self.__glsl_frag, GL_FRAGMENT_SHADER ),
)
self.___attrib = { a : glGetAttribLocation (self.__program, a) for a in ['a_pos', 'a_nv', 'a_col'] }
print(self.___attrib)
self.___uniform = { u : glGetUniformLocation (self.__program, u) for u in ['u_model', 'u_view', 'u_proj'] }
print(self.___uniform)
v = [[-1,-1,1], [1,-1,1], [1,1,1], [-1,1,1], [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1]]
c = [[1.0, 0.0, 0.0], [1.0, 0.5, 0.0], [1.0, 0.0, 1.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
n = [[0,0,1], [1,0,0], [0,0,-1], [-1,0,0], [0,1,0], [0,-1,0]]
e = [[0,1,2,3], [1,5,6,2], [5,4,7,6], [4,0,3,7], [3,2,6,7], [1,0,4,5]]
index_array = [si*4+[0, 1, 2, 0, 2, 3][vi] for si in range(6) for vi in range(6)]
attr_array = []
for si in range(len(e)):
for vi in e[si]:
attr_array += [*v[vi], *n[si], *c[si], 1]
self.__no_vert = len(attr_array) // 10
self.__no_indices = len(index_array)
vertex_attributes = np.array(attr_array, dtype=np.float32)
indices = np.array(index_array, dtype=np.uint32)
self.__vao = glGenVertexArrays(1)
self.__vbo, self.__ibo = glGenBuffers(2)
glBindVertexArray(self.__vao)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.__ibo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, self.__vbo)
glBufferData(GL_ARRAY_BUFFER, vertex_attributes, GL_STATIC_DRAW)
float_size = vertex_attributes.itemsize
glVertexAttribPointer(0, 3, GL_FLOAT, False, 10*float_size, None)
glVertexAttribPointer(1, 3, GL_FLOAT, False, 10*float_size, c_void_p(3*float_size))
glVertexAttribPointer(2, 4, GL_FLOAT, False, 10*float_size, c_void_p(6*float_size))
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
glEnable(GL_DEPTH_TEST)
glUseProgram(self.__program)
shape = [5, 4, 6]
number_of = shape[0] * shape[1] * shape[2]
self.array3d = np.array(np.random.rand(number_of), dtype=np.float32).reshape(shape)
glActiveTexture(GL_TEXTURE1)
self.tex3DObj = glGenTextures(1)
glBindTexture(GL_TEXTURE_3D, self.tex3DObj)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0)
glTexImage3D(GL_TEXTURE_3D, 0, GL_R16F, *self.array3d.shape, 0, GL_RED, GL_FLOAT, self.array3d)
glutReshapeFunc(self.__reshape)
glutDisplayFunc(self.__mainloop)
def run(self):
self.__starttime = 0
self.__starttime = self.elapsed_ms()
glutMainLoop()
def elapsed_ms(self):
return glutGet(GLUT_ELAPSED_TIME) - self.__starttime
def __reshape(self, w, h):
self.__vp_valid = False
def __mainloop(self):
number_of = self.array3d.shape[0] * self.array3d.shape[1] * self.array3d.shape[2]
rand = (np.random.rand(number_of) - 0.5) * 0.05
self.array3d = np.clip(np.add(self.array3d, rand.reshape(self.array3d.shape)), 0, 1)
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, *self.array3d.shape, GL_RED, GL_FLOAT, self.array3d)
if not self.__vp_valid:
self.__vp_size = [glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)]
self.__vp_valid = True
glViewport(0, 0, self.__vp_size[0], self.__vp_size[1])
aspect, ta, near, far = self.__vp_size[0]/self.__vp_size[1], np.tan(np.radians(90.0) / 2), 0.1, 10
proj = np.array(((1/ta/aspect, 0, 0, 0), (0, 1/ta, 0, 0), (0, 0, -(far+near)/(far-near), -1), (0, 0, -2*far*near/(far-near), 0)), np.float32)
view = np.array(((1, 0, 0, 0), (0, 0, -1, 0), (0, 1, 0, 0), (0, 0, -2, 1)), np.float32)
c, s = (f(np.radians(30.0)) for f in [np.cos, np.sin])
viewRotX = np.array(((1, 0, 0, 0), (0, c, s, 0), (0, -s, c, 0), (0, 0, 0, 1)), np.float32)
view = np.matmul(viewRotX, view)
c1, s1, c2, s2, c3, s3 = (f(self.elapsed_ms() * np.pi * 2 / tf) for tf in [5000.0, 7333.0, 10000.0] for f in [np.cos, np.sin])
rotMatZ = np.array(((c3, s3, 0, 0), (-s3, c3, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)), np.float32)
model = rotMatZ
glUniformMatrix4fv(self.___uniform['u_proj'], 1, GL_FALSE, proj )
glUniformMatrix4fv(self.___uniform['u_view'], 1, GL_FALSE, view )
glUniformMatrix4fv(self.___uniform['u_model'], 1, GL_FALSE, model )
glClearColor(0.2, 0.3, 0.3, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawElementsInstanced(GL_TRIANGLES, self.__no_indices, GL_UNSIGNED_INT, None, number_of)
glutSwapBuffers()
glutPostRedisplay()
window = MyWindow(800, 600)
window.run()
This won't directly create the sort of visualization you're looking for, but I would highly recommend taking a look at the glumpy package by Nicholas Rougier : https://code.google.com/p/glumpy/. OpenGL can be a pain to use, especially for someone who is not a graphics expert, and glumpy abstracts away most of the pain to let you just display numpy arrays on the screen.

Categories