pyopengl blackscreen not drawing - python

cant seem to draw a triangle
import pygame
from pygame.locals import *
import numpy as np
from OpenGL.GL import *
pygame.init()
display = (600,600)
clock = pygame.time.Clock()
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
positions = [
-0.5, -0.5,
0, 0.5,
0.5, -0.5
]
positions = np.array(positions, dtype=np.float32)
buffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, buffer)
glBufferData(GL_ARRAY_BUFFER, positions.size, positions, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, 0)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
if not run:
break
glDrawArrays(GL_TRIANGLES, 0, 3)
pygame.display.flip()
is this a python problem or am i writing it wrong ?

The 2nd argument of glBufferData is the buffer size in bytes:
glBufferData(GL_ARRAY_BUFFER, positions.size, positions, GL_STATIC_DRAW)
glBufferData(GL_ARRAY_BUFFER, positions.size*4, positions, GL_STATIC_DRAW)
The parameter size can be omitted:
glBufferData(GL_ARRAY_BUFFER, positions, GL_STATIC_DRAW)
If a named buffer object is bound, then the 6th parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store. But the type of the parameter is a pointer anyway (c_void_p).
So if the offset is 0, then the 6th parameter can either be None or c_void_p(0) else the offset has to be caste to c_void_p(0):
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, 0)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, None)
Complete example:
import pygame
from pygame.locals import *
import numpy as np
from OpenGL.GL import *
pygame.init()
display = (600,600)
clock = pygame.time.Clock()
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
positions = [
-0.5, -0.5,
0, 0.5,
0.5, -0.5
]
positions = np.array(positions, dtype=np.float32)
buffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, buffer)
glBufferData(GL_ARRAY_BUFFER, positions, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, None)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
glClear(GL_COLOR_BUFFER_BIT)
glDrawArrays(GL_TRIANGLES, 0, 3)
pygame.display.flip()
pygame.quit()

It is hard to know what example will work without knowing what GL version your hardware supports (print (glGetString(GL_VERSION))), but here is a compatibility demo that should work, but you should consider modifying it to at least OpenGL3.3/GLSL 330:
import pygame
from pygame.locals import *
import numpy as np
from OpenGL.GL import *
pygame.init()
display = (600,600)
clock = pygame.time.Clock()
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
positions = [
-0.5, -0.5,
0, 0.5,
0.5, -0.5
]
vertex_shader_source = """#version 120
attribute vec2 aPos;
varying vec2 pos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, 0, 1);
pos = aPos;
}"""
fragment_shader_source = """#version 120
varying vec2 pos;
void main()
{
gl_FragColor = vec4(pos.x, pos.y,0,1);
}"""
vertex_shader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex_shader, vertex_shader_source)
glCompileShader(vertex_shader)
if glGetShaderiv(vertex_shader, GL_COMPILE_STATUS) != GL_TRUE:
raise RuntimeError(glGetShaderInfoLog(vertex_shader))
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment_shader, fragment_shader_source)
glCompileShader(fragment_shader)
if glGetShaderiv(fragment_shader, GL_COMPILE_STATUS) != GL_TRUE:
raise RuntimeError(glGetShaderInfoLog(fragment_shader))
shader_program = glCreateProgram()
glAttachShader(shader_program, vertex_shader)
glAttachShader(shader_program, fragment_shader)
glLinkProgram(shader_program)
positions = np.array(positions, dtype=np.float32)
VAO = glGenVertexArrays(1)
VBO = glGenBuffers(1)
glBindVertexArray(VAO)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
# using positions.size * np.dtype(np.float32).itemsize
# to get the size (in bytes) of the vertex data
glBufferData(GL_ARRAY_BUFFER, positions.size * np.dtype(np.float32).itemsize, \
positions, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
aPos = glGetAttribLocation(shader_program, 'aPos')
glEnableVertexAttribArray(aPos)
# ctypes.c_void_p(0) to specify the offset (0 wont work)
glVertexAttribPointer(aPos, 2, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
if glGetProgramiv(shader_program, GL_LINK_STATUS) != GL_TRUE:
raise RuntimeError(glGetProgramInfoLog(shader_program))
glClearColor(0,0,1,1);
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
if not run:
break
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(shader_program)
glBindVertexArray(VAO)
glDrawArrays(GL_TRIANGLES, 0, 3)
pygame.display.flip()
Result:

Related

Rendering a 2D quad from a VBO with PyOpenGL?

How Can You Render A Quad From A VBO using PyOpenGL and Pygame?
I am trying to get an image to render on my screen using PyOoenGL and pygame, while not using any VBO's I managed to get it to display using this draw function:
def draw(texture: Texture):
texture.Bind()
glBegin(GL_QUADS)
glTexCoord2f(0, 0)
glVertex2f(-1, -1)
glTexCoord2f(1, 0)
glVertex2f(1, -1)
glTexCoord2f(1, 1)
glVertex2f(1, 1)
glTexCoord2f(0, 1)
glVertex2f(-1, 1)
glEnd()
However, when I tried to change it out to render using a VBO I could not see well... anything:
def GenVBO():
# gen VBO for a quad # glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 64, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, 0, 32, (GLfloat * 12)(-1, -1, 0, 0, 0, 0, 1, -1, 0, 1, 0, 0))
glBindBuffer(GL_ARRAY_BUFFER, 0)
return VBO
def draw(texture: Texture, VBO):
texture.Bind()
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glVertexPointer(2, GL_FLOAT, 32, None)
glTexCoordPointer(2, GL_FLOAT, 32, ctypes.c_void_p(8))
glDrawArrays(GL_QUADS, 0, 4)
I do not get any errors, just a blank screen, I have also tried to move around by changing my perspective with a movement script, but still no output.
And just in case it's relevant, here is the main script:
def main():
pygame.init()
display = (800, 600)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
glTranslatef(0.0, 0.0, -5)
texture = LoadImage("test.png")
VBO = GenVBO()
while True:
down = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
# (the movement script is here)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
draw(texture, VBO)
pygame.display.flip()
pygame.time.wait(10)
You have 2 possibiliets:
(x0, y0, u0, v0, x1, y1, u1, v1, ...)
In this case stride is 16 bytes, because each attribute tuple has 4*4 bytes (x, y, u, v). The offset of the vertices is 0 and the offset of the texture coordinates is 8 bytes:
bufferData = (GLfloat * 16)(-1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1, -1, 1, 0, 1)
glBufferData(GL_ARRAY_BUFFER, bufferData, GL_STATIC_DRAW)
glVertexPointer(2, GL_FLOAT, 16, None)
glTexCoordPointer(2, GL_FLOAT, 16, ctypes.c_void_p(8))
(x0, y0, x1, y1, ..., u0, v0, u1, v1, ...)
In this case stride vor the vertices is 8 bytes and the stride for the texture coordinates is 8 bytes. The offset of the vertices is 0 and the offset of the texture coordinates is 32 bytes (8*4):
vertexData = (GLfloat * 8)(-1, -1, 1, -1, 1, 1, -1, 1)
textureData = (GLfloat * 8)( 0, 0, 1, 0, 1, 1, 0, 1)
glBufferData(GL_ARRAY_BUFFER, 64, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, 0, 32, vertexData)
glBufferSubData(GL_ARRAY_BUFFER, 32, 32, textureData)
glVertexPointer(2, GL_FLOAT, 8, None)
glTexCoordPointer(2, GL_FLOAT, 8, ctypes.c_void_p(32))

PyOpenGL cannot render any vao

I have spent almost 2-3 hours trying to figure out why I am not getting a rectangle renderered. I am using pygame for making a window, and opengl for rendering onto the window.
what should be happening is a red background with a blue rectangle, but what I am seeing is just the empty red background.
this is the code I used below.
am I uploading things to the shader wrong? or did I miss something.
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
print("import sucessfull")
vertex = """
#version 460 core
layout (location=0) in vec3 position;
out vec4 fColor;
void main(){
fColor = vec4(0,1,0,1);
gl_Position = vec4(position, 1.0);
}
"""
fragment = """
#version 460 core
in vec4 fColor;
out vec4 color;
void main(){
color = fColor;
//gl_FragColor = fColor;
}
"""
def main():
clock = pygame.time.Clock()
pygame.init()
display = (800, 600)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
gluPerspective(45, (display[0]/display[1]), 0.1, 1000)
glClearColor(1, 0,0,1)
glTranslatef(0,0,-5)
#############################3
vertexID = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexID, vertex)
fragmentID = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentID, fragment)
glCompileShader(vertexID)
glCompileShader(fragmentID)
program = glCreateProgram()
glAttachShader(program, vertexID)
glAttachShader(program, fragmentID)
glLinkProgram(program)
positions = [
0.5, -0.5, 0.0,
-0.5, 0.5, 0.0,
0.5, 0.5, 0.0,
-0.5, -0.5, 0.0
]
indexes = [
2,1,0,
0,1,3
]
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, np.array(positions, dtype=np.float32), GL_STATIC_DRAW)
ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, np.array(indexes, dtype=np.float32), GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, 0)
glEnableVertexAttribArray(0)
################################
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
#render here
glUseProgram(program)
glBindVertexArray(vao)
glEnableVertexAttribArray(0)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glDisableVertexAttribArray(0)
glBindVertexArray(0)
glUseProgram(0)
#***********
pygame.display.flip()
clock.tick(40)
main()
The type of the indices must be integral and correspond to the type specified in the draw call (GL_UNSIGNED_INT). You have to create a NumPy array with the type uint32 instead of float32:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, np.array(indexes, dtype=np.float32), GL_STATIC_DRAW)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, np.array(indexes, dtype=np.uint32), GL_STATIC_DRAW)
If a named buffer object is bound, then the 6th parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store. But the type of the parameter is a pointer anyway (c_void_p).
So if the offset is 0, then the 6th parameter can either be None or c_void_p(0) else the offset has to be caste to c_void_p(0):
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, 0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, None)
There is a similar problem with glDrawElements. The last argument is treated as a byte offset:
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
gluPerspective and glTranslatef set the deprecated fixed function pipeline matrices. This instructions do not make any sense when you use "modern" shader program. Remove this instructions. With modern OpenGL you have to use Uniform variables.

How to solve Python OpenGL Memory Leakage problem?

I am trying to draw some traingles and render some texts in screen. But I've observed that memory(RAM) is gradually increasing just only for 6 draw calls. I've 8 GB RAM. When I run the program memory usage goes from 4.2 to 6 within 1 minute. Here is the full code.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders
from shader import *
import glfw
import freetype
import glm
import numpy as np
from PIL import Image
import math
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)
def init_chars(shaderProgram,window_height,window_width,font_size=24,fontfile = "Vera.ttf"):
glUseProgram(shaderProgram)
#get projection
shader_projection = glGetUniformLocation(shaderProgram, "projection")
W = window_width
H = window_height
projection = glm.ortho(-W/2, W/2, -H/2, H/2)
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(font_size*64 )
#load first 128 characters of ASCII set
Characters = dict()
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)
glUseProgram(0)
return Characters
def render_text(window,shaderProgram,text,x,y,scale,Characters,color=(170,250,255)):
r,g,b = color
glUseProgram(shaderProgram)
#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_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),r/255,g/255,b/255)
glActiveTexture(GL_TEXTURE0)
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
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0)
#UNBIND and DELETE VAO/VBO
glBindVertexArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glDeleteBuffers(1, id(VBO))
glDeleteBuffers(1, id(VAO))
def triangle(shaderProgram,window,x=0,y=0):
vertices = [-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0]
vertices = np.array(vertices, dtype=np.float32)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
#use shader program
glUseProgram(shaderProgram)
#accessing ourColor variable from shaderProgram
vertexColorLoc = glGetUniformLocation(shaderProgram, "ourColor")
glUniform4f(vertexColorLoc, 255, 28/255.0, 20/255.0, 0.7);
#transform matrix
transform = glm.mat4(1)
transform = glm.translate(transform,glm.vec3(x,y,0))
MVP = glGetUniformLocation(shaderProgram, "MVP")
glUniformMatrix4fv(MVP, 1, GL_FALSE, glm.value_ptr(transform))
#drawing trangle
glLineWidth(3)
glDrawArrays(GL_TRIANGLES, 0, 3)
glUseProgram(0)
#UNBIND and DELETE VAO/VBO
glBindVertexArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glDeleteBuffers(1, id(VBO))
def main():
glfw.init()
window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
glfw.make_context_current(window)
#initliaze shader programs
shaderProgram = get_shaderProgram()
text_shaderProgram = get_text_shaderProgram()
#load characters and VAO/VBO for text rendering
Characters = init_chars(text_shaderProgram,640,640)
#window loop
while not glfw.window_should_close(window):
glfw.poll_events()
#screen
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
#draw functions
render_text(window,text_shaderProgram,"TRIANGLE",-50,-200,1,Characters)
render_text(window,text_shaderProgram,"A",0,180,1,Characters)
render_text(window,text_shaderProgram,"B",-160,-180,1,Characters)
render_text(window,text_shaderProgram,"C",150,-180,1,Characters)
triangle(shaderProgram,window)
triangle(shaderProgram,window,x=0.5,y=0.5)
#swap buffers
glfw.swap_buffers(window)
glfw.swap_interval(1)
glfw.terminate()
if __name__ == '__main__':
main()
The shader program is here. But I think the problems is in buffer object. I've tried to unbind VAO/VBO and delete buffers by following code. But I see no change.
#UNBIND and DELETE VAO/VBO
glBindVertexArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glDeleteBuffers(1, id(VBO))
glDeleteBuffers(1, id(VAO))
Here is the related problem where accepted answer suggested that glGenBuffers causes memory leak. The alternate function glCreateBuffers is not available in pyopengl. How can I solve this issue?
I can't see any good reason for recreating the Vertex Array Object and Array Buffer Object every time when render_text respectively triangle is called. The vertex specification and the number of vertices doesn't change, so it would be sufficient to update the content of the buffer.
Create the Vertex Array Object and the Array Buffer Object once at initialization:
def init_buffers():
global text_VAO, text_VBO, triangle_VAO, triangle_VBO
text_VAO = glGenVertexArrays(1)
glBindVertexArray(text_VAO)
text_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, text_VBO)
glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
vertices = [-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0]
vertices = np.array(vertices, dtype=np.float32)
triangle_VAO = glGenVertexArrays(1)
glBindVertexArray(triangle_VAO)
triangle_VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, triangle_VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
Use then in the functions render_text and triangle:
def render_text(window,shaderProgram,text,x,y,scale,Characters,color=(170,250,255)):
# [...]
glBindVertexArray(text_VAO)
for c in text:
# [...]
glBindBuffer(GL_ARRAY_BUFFER, text_VBO)
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)
# glDeleteBuffers(1, id(VBO)) <--- DELETE
# glDeleteBuffers(1, id(VAO)) <--- DELETE
def triangle(shaderProgram,window,x=0,y=0):
glBindVertexArray(triangle_VAO)
# [...]
# glDeleteBuffers(1, id(VBO)) <--- DELETE
Invoke init_buffers before the application loop:
def main():
# [...]
init_buffers()
while not glfw.window_should_close(window):
# [...]

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;

Using PyOpenGL, how do you apply instancing on an .obj file?

I am trying to apply instancing on a object (.obj) file by following the example on Learn Opengl - Instancing.
My question is actually twofold:
1) How to apply instancing on .obj files? (I think I'm almost there)
2) As I understand, to use instancing it is necessary to store the .obj file in a vertex array buffer (VAO), and therefore need help setting the materials and textures for the .obj file. That is if my assumption is true? (For now I just assigned the object to a solid white colour.)
What I have done so far:
I use Pywavefront to load the .obj file but I'm not stuck on it if another obj loader is needed.
Setting locations of where to place the object:
#Locations
vertices = [[-33.64413, 0.0, 19.03057], [-33.64818, 0.0, 19.0219],
[-33.64921, 0.0, 19.011],[-33.64913, 0.0, 19.03027], [-33.69828, 0.0, 19.0219],
[-33.64991, 0.0, 19.021]]
self.vertices = numpy.array(vertices, dtype='f')
Loading OBJ and collecting vertices and faces (indices):
obj_file = pywavefront.Wavefront('OBJ_PINS/Mcdonalds.obj', collect_faces=True)
obj_vertices = numpy.array(obj_file.vertices, dtype='f')
faces_list = []
for mesh in obj_file.mesh_list:
for face in mesh.faces:
faces_list.append(face)
obj_ind = numpy.array(faces_list, dtype=numpy.int32)
I then create a Vertex Array Object (VAO) with the necessary vertex buffer object (VBO):
VAOs = glGenVertexArrays(1)
glBindVertexArray(VAOs)
# Vertex Buffer Object
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, obj_vertices.nbytes, obj_vertices, GL_STATIC_DRAW)
# Element Buffer Object
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, obj_ind.nbytes, obj_ind, GL_STATIC_DRAW)
# vertices
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, obj_vertices.itemsize * 3, ctypes.c_void_p(0))
Transforming and storing the object for instancing:
# instance locations
amount = len(self.vertices)
#List storing the same object in different places
modelMatrices = []
#Storring the objects in a list
for i in range(0, amount):
model_o = glm.mat4(1.0)
model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
model_o = glm.scale(model_o, glm.vec3(0.0001, 0.0001, 0.0001))
modelMatrices.append(model_o)
The interesting part, instancing the object to different places:
#Create VBO for instancing
instanceVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
glBufferData(GL_ARRAY_BUFFER, glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[0]), GL_STATIC_DRAW)
#Bind each vertex attrib array of matrices (4 vectors in Matrix)
glEnableVertexAttribArray(3)
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(0))
glEnableVertexAttribArray(4)
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(glm.sizeof(glm.vec4)))
glEnableVertexAttribArray(5)
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((2 * glm.sizeof(glm.vec4))))
glEnableVertexAttribArray(6)
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((3 * glm.sizeof(glm.vec4))))
#Set instancing
glVertexAttribDivisor(3, 1)
glVertexAttribDivisor(4, 1)
glVertexAttribDivisor(5, 1)
glVertexAttribDivisor(6, 1)
#End VAO
glBindVertexArray(0)
The shaders:
self.vertex_obj = """
# version 330
in layout(location = 0) vec3 aPos;
in layout (location = 3) mat4 instanceMatrix;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0);
}
"""
self.fragment_obj = """
#version 330
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0,1.0,1.0, 1.0);
}
"""
When I draw the object in the drawing loop I only see one object and not 6:
glBindVertexArray(VAOs)
glDrawElementsInstanced(GL_TRIANGLES, len(obj_ind)*3, GL_UNSIGNED_INT, None, amount)
glBindVertexArray(0)
What I think is wrong:
I need to call the whole list of matrices to be drawn in the instanced glBufferData but don't know how to do that.
Should it change to something like:
glBufferData(GL_ARRAY_BUFFER, amount * glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[?]), GL_STATIC_DRAW)
FULL CODE
import numpy
import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import pyrr
import glm
from pyrr import matrix44
from math import sin, cos, radians
from OBJ_Loader import*
import pywavefront
class threeD_viewer():
def __init__(self):
vertices = [[-33.64413, 0.0, 19.03057], [-33.64818, 0.0, 19.0219],
[-33.64921, 0.0, 19.011],[-33.64913, 0.0, 19.03027], [-33.69828, 0.0, 19.0219],
[-33.64991, 0.0, 19.021]]
self.vertices = numpy.array(vertices, dtype='f')
self.cam = Camera(self.vertices[0][0], self.vertices[0][2])
self.WIDTH, self.HEIGHT = 1280, 720
self.lastX, self.LastY = self.WIDTH / 2, self.HEIGHT / 2
self.first_mouse = True
self.forward, self.backward, self.right, self.left, self.up, self.down = False, False, False, False, False, False
self.x_start, self.y_start = self.vertices[0][0], self.vertices[0][2]
self.vertex_obj = """
# version 330
in layout(location = 0) vec3 aPos;
in layout (location = 3) mat4 instanceMatrix;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0);
}
"""
self.fragment_obj = """
#version 330
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0,1.0,1.0, 1.0);
}
"""
self.main()
# the keyboard input callback
def key_input_clb(self, window, key, scancode, action, mode):
if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
glfw.set_window_should_close(window, True)
if key == glfw.KEY_W and action == glfw.PRESS:
self.forward = True
elif key == glfw.KEY_W and action == glfw.RELEASE:
self.forward = False
if key == glfw.KEY_S and action == glfw.PRESS:
self.backward = True
elif key == glfw.KEY_S and action == glfw.RELEASE:
self.backward = False
if key == glfw.KEY_A and action == glfw.PRESS:
self.left = True
elif key == glfw.KEY_A and action == glfw.RELEASE:
self.left = False
if key == glfw.KEY_D and action == glfw.PRESS:
self.right = True
elif key == glfw.KEY_D and action == glfw.RELEASE:
self.right = False
if key == glfw.KEY_Q and action == glfw.PRESS:
self.up = True
elif key == glfw.KEY_Q and action == glfw.RELEASE:
self.up = False
if key == glfw.KEY_E and action == glfw.PRESS:
self.down = True
elif key == glfw.KEY_E and action == glfw.RELEASE:
self.down = False
def do_movement(self):
if self.forward:
self.cam.process_keyboard("FORWARD", 0.000008)
if self.backward:
self.cam.process_keyboard("BACKWARD", 0.000008)
if self.right:
self.cam.process_keyboard("RIGHT", 0.000008)
if self.left:
self.cam.process_keyboard("LEFT", 0.000008)
if self.up:
self.cam.process_keyboard("UP", 0.000008)
if self.down:
self.cam.process_keyboard("DOWN", 0.000008)
def mmouse_look_clb(self, window, xpos, ypos):
if self.first_mouse:
self.lastX = xpos
self.lastY = ypos
self.first_mouse = False
xoffset = xpos - self.lastX
yoffset = self.lastY - ypos
self.lastX = xpos
self.lastY = ypos
self.cam.process_mouse_movement(xoffset, yoffset)
def window_resize(self, window, width, height):
glViewport(0, 0, width, height)
projection = pyrr.matrix44.create_perspective_projection_matrix(45, width / height, 0.0001, 100)
proj_loc = glGetUniformLocation(self.shader_obj, "projection")
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)
def main(self):
# initializing glfw library
if not glfw.init():
raise Exception("glfw can not be initialized!")
# creating the window
window = glfw.create_window(self.WIDTH, self.HEIGHT, "My OpenGL window", None, None)
# check if window was created
if not window:
glfw.terminate()
raise Exception("glfw window can not be created!")
# set window's position
glfw.set_window_pos(window, 400, 200)
# set the callback function for window resize
glfw.set_window_size_callback(window, self.window_resize)
glfw.set_cursor_pos_callback(window, self.mmouse_look_clb)
glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)
glfw.set_key_callback(window, self.key_input_clb)
# make the context current
glfw.make_context_current(window)
self.shader_obj = compileProgram(compileShader(self.vertex_obj, GL_VERTEX_SHADER),
compileShader(self.fragment_obj, GL_FRAGMENT_SHADER))
obj_file = pywavefront.Wavefront('OBJ_PINS/Mcdonalds.obj', collect_faces=True)
obj_vertices = numpy.array(obj_file.vertices, dtype='f')
faces_list = []
for mesh in obj_file.mesh_list:
for face in mesh.faces:
faces_list.append(face)
obj_ind = numpy.array(faces_list, dtype=numpy.int32)
VAOs = glGenVertexArrays(1)
glBindVertexArray(VAOs)
# Vertex Buffer Object
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, obj_vertices.nbytes, obj_vertices, GL_STATIC_DRAW)
# Element Buffer Object
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, obj_ind.nbytes, obj_ind, GL_STATIC_DRAW)
# vertices
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, obj_vertices.itemsize * 3, ctypes.c_void_p(0))
# instance locations
amount = len(self.vertices)
#List storing the same object in different places
modelMatrices = []
#Storring the objects in a list
for i in range(0, amount):
model_o = glm.mat4(1.0)
model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
model_o = glm.scale(model_o, glm.vec3(0.0001, 0.0001, 0.0001))
modelMatrices.append(model_o)
#Create VBO for instancing
instanceVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
glBufferData(GL_ARRAY_BUFFER, glm.sizeof(glm.mat4), glm.value_ptr(modelMatrices[0]), GL_STATIC_DRAW)
#Bind each vertex attrib array of matrices (4 vectors in Matrix)
glEnableVertexAttribArray(3)
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(0))
glEnableVertexAttribArray(4)
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p(glm.sizeof(glm.vec4)))
glEnableVertexAttribArray(5)
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((2 * glm.sizeof(glm.vec4))))
glEnableVertexAttribArray(6)
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, glm.sizeof(glm.mat4), ctypes.c_void_p((3 * glm.sizeof(glm.vec4))))
#Set instancing
glVertexAttribDivisor(3, 1)
glVertexAttribDivisor(4, 1)
glVertexAttribDivisor(5, 1)
glVertexAttribDivisor(6, 1)
#End VAO
glBindVertexArray(0)
glClearColor(0, 0.1, 0.1, 1)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
#projection = pyrr.matrix44.create_perspective_projection_matrix(45, self.WIDTH / self.HEIGHT, 0.0001, 1000)
projection = glm.perspective(glm.radians(45.0), self.WIDTH / self.HEIGHT, 0.0001, 1000)
glUseProgram(self.shader_obj)
view_loc_obj = glGetUniformLocation(self.shader_obj, "view")
proj_loc_obj = glGetUniformLocation(self.shader_obj, "projection")
model_loc_obj = glGetUniformLocation(self.shader_obj, "model")
glUniformMatrix4fv(proj_loc_obj, 1, GL_FALSE, glm.value_ptr(projection))
glUseProgram(0)
# the main application loop
while not glfw.window_should_close(window):
glfw.poll_events()
self.do_movement()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.shader_obj)
view = self.cam.get_view_matrix()
glUniformMatrix4fv(proj_loc_obj, 1, GL_FALSE, glm.value_ptr(projection))
glUniformMatrix4fv(view_loc_obj, 1, GL_FALSE, view)
glBindVertexArray(VAOs)
glDrawElementsInstanced(GL_TRIANGLES, len(obj_ind)*3, GL_UNSIGNED_INT, None, amount)
glBindVertexArray(0)
glUseProgram(0)
glfw.swap_buffers(window)
# terminate glfw, free up allocated resources
glfw.terminate()
class Camera:
def __init__(self, xstart, ystart):
self.camera_pos = glm.vec3(xstart+0.005, 0, ystart+0.01)
#self.camera_pos = glm.vec3(0, 0, 0)
print(xstart+0.005)
print(ystart+0.01)
self.camera_front = glm.vec3(0.0, 0.0, -200.0)
self.camera_up = glm.vec3(0.0, 1.0, 0.0)
self.camera_right = glm.vec3(1.0, 0.0, 0.0)
self.mouse_sensitivity = 0.25
self.jaw = -90.0
self.pitch = 0.0
def get_view_matrix(self):
return matrix44.create_look_at(self.camera_pos, self.camera_pos + self.camera_front, self.camera_up)
def process_mouse_movement(self, xoffset, yoffset, constrain_pitch=True):
xoffset *= self.mouse_sensitivity
yoffset *= self.mouse_sensitivity
self.jaw += xoffset
self.pitch += yoffset
if constrain_pitch:
if self.pitch > 45:
self.pitch = 45
if self.pitch < -45:
self.pitch = -45
self.update_camera_vectors()
def update_camera_vectors(self):
front = glm.vec3(0.0, 0.0, 0.0)
front.x = cos(radians(self.jaw)) * cos(radians(self.pitch))
front.y = sin(radians(self.pitch))
front.z = sin(radians(self.jaw)) * cos(radians(self.pitch))
self.camera_front = glm.normalize(front)
self.camera_right = glm.normalize(glm.cross(self.camera_front, glm.vec3(0.0, 1.0, 0.0)))
#self.camera_up = glm.normalize(glm.cross(self.camera_right, self.camera_front))
# Camera method for the WASD movement
def process_keyboard(self, direction, velocity):
if direction == "FORWARD":
self.camera_pos += self.camera_front * velocity
if direction == "BACKWARD":
self.camera_pos -= self.camera_front * velocity
if direction == "LEFT":
self.camera_pos -= self.camera_right * velocity
if direction == "RIGHT":
self.camera_pos += self.camera_right * velocity
if direction == "UP":
self.camera_pos += self.camera_up * velocity
if direction == "DOWN":
self.camera_pos -= self.camera_up * velocity
threeD_viewer()
Update
Instancing works now. The solution was to store a list of the vectors rather than the glm.mat4 objects:
# instance locations
amount = len(self.vertices)
#List storing the same object in different places
modelMatrices = []
#Storring the objects in a list
for i in range(0, amount):
mod_list = []
model_o = glm.mat4(1.0)
model_o = glm.translate(model_o, glm.vec3(self.vertices[i][0], self.vertices[i][1], self.vertices[i][2]))
for i in range(4):
for j in range(4):
mod_list.append(model_o[i][j])
modelMatrices.append(mod_list)
modelMatrices = numpy.array(modelMatrices, dtype ="f")
print(modelMatrices.shape)
#Create VBO for instancing
instanceVBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO)
glBufferData(GL_ARRAY_BUFFER, amount* glm.sizeof(glm.mat4), modelMatrices, GL_STATIC_DRAW)

Categories