How to draw with Vertex Array Objects and glDrawElements in PyOpenGL - python

I have the following code which should simply draw a green triangle to the screen. It is using Vertex Array Objects and index buffers to draw and has the simplest shader I could make.
At first I was not using index buffers and was simply making the draw call with glDrawArrays which worked fine but when I change it to use glDrawElements then nothing is drawn to the screen (it is entirely black).
from OpenGL.GL import shaders
from OpenGL.arrays import vbo
from OpenGL.GL import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \
glBindVertexArray
import pygame
import numpy as np
def run():
pygame.init()
screen = pygame.display.set_mode((800,600), pygame.OPENGL)
#Create the Vertex Array Object
vertexArrayObject = GLuint(0)
glGenVertexArrays(1, vertexArrayObject)
glBindVertexArray(vertexArrayObject)
#Create the VBO
vertices = np.array([[0,1,0],[-1,-1,0],[1,-1,0]], dtype='f')
vertexPositions = vbo.VBO(vertices)
#Create the index buffer object
indices = np.array([0,1,2], dtype='uint16')
indexPositions = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)
indexPositions.bind()
vertexPositions.bind()
glEnableVertexAttribArray(0) # from 'location = 0' in shader
glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, None)
glBindVertexArray(0)
vertexPositions.unbind()
indexPositions.unbind()
#Now create the shaders
VERTEX_SHADER = shaders.compileShader("""
#version 330
layout(location = 0) in vec4 position;
void main()
{
gl_Position = position;
}
""", GL_VERTEX_SHADER)
FRAGMENT_SHADER = shaders.compileShader("""
#version 330
out vec4 outputColor;
void main()
{
outputColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);
}
""", GL_FRAGMENT_SHADER)
shader = shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER)
#The draw loop
while True:
glUseProgram(shader)
glBindVertexArray(vertexArrayObject)
#glDrawArrays(GL_TRIANGLES, 0, 3) #This line works
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0) #This line does not
glBindVertexArray(0)
glUseProgram(0)
# Show the screen
pygame.display.flip()
run()
If I simply comment out the glDrawElements and uncomment the glDrawArrays line then it works correctly so at least the vertex VBO is being input correctly.
What am I doing wrong here? All the OpenGL documentation I have been able to find suggests that I am doing this correctly so I am obviously misunderstanding something either about OpenGL itself or the PyOpenGL wrapper.
EDIT
Changing the VAO setup to use more direct OpenGL function rather than the VBO wrapper like:
vertices = np.array([[0,1,0],[-1,-1,0],[1,-1,0]], dtype='f')
vertexPositions = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vertexPositions)
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW)
#Create the index buffer object
indices = np.array([0,1,2], dtype='uint16')
indexPositions = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexPositions)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexPositions)
glBindBuffer(GL_ARRAY_BUFFER, vertexPositions)
glEnableVertexAttribArray(0) # from 'location = 0' in shader
glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, None)
glBindVertexArray(0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
makes no difference. glDrawArrays still works and glDrawElements still doesn't.

Thanks #NicolBolas. He motivated me to actually take this code and make it work. Instead of theoritizing:)
I have removed vertexArrayObject(it's redundand as we already have VBOs for vertices and indices).
So you just bind index and vertex buffers(along with attributes) prior to glDraw* call.
And of course very important to pass None(null pointer) to glDrawElements indices instead of 0!
from OpenGL.GL import shaders
from OpenGL.arrays import vbo
from OpenGL.GL import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \
glBindVertexArray
import pygame
import numpy as np
def run():
pygame.init()
screen = pygame.display.set_mode((800,600), pygame.OPENGL|pygame.DOUBLEBUF)
#Create the VBO
vertices = np.array([[0,1,0],[-1,-1,0],[1,-1,0]], dtype='f')
vertexPositions = vbo.VBO(vertices)
#Create the index buffer object
indices = np.array([[0,1,2]], dtype=np.int32)
indexPositions = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)
#Now create the shaders
VERTEX_SHADER = shaders.compileShader("""
#version 330
layout(location = 0) in vec4 position;
void main()
{
gl_Position = position;
}
""", GL_VERTEX_SHADER)
FRAGMENT_SHADER = shaders.compileShader("""
#version 330
out vec4 outputColor;
void main()
{
outputColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);
}
""", GL_FRAGMENT_SHADER)
shader = shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER)
#The draw loop
while True:
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glUseProgram(shader)
indexPositions.bind()
vertexPositions.bind()
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, None)
#glDrawArrays(GL_TRIANGLES, 0, 3) #This line still works
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, None) #This line does work too!
# Show the screen
pygame.display.flip()
run()

Related

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.

Porting C++ OpenGL to PyOpenGL not rendering properly

I've been facing an issue for the past couple of days, and I still haven't been able to figure it out.. I'm trying to port a previous C++ Opengl project to PyOpengl, but I'm not able to get the object to render as it should. I'm simply trying to render a 3D grid model, which works in the original C++ code, but not in Python PyOpenGL.
What it should look like: (C++ OpenGL)
What it looks like (Python PyOpenGL)
This is the code I end up with in PyOpenGL :
import glfw
import glm
import numpy as np
from OpenGL.GL import *
import Shader
# Settings
SCR_WIDTH = 800
SCR_HEIGHT = 600
def framebuffer_size_callback(window, width, height):
if width != 0 and height != 0:
width = width
height = height
glViewport(0, 0, width, height)
def create_v_array():
vertexArray = []
indexArray = []
for x in range(-100, 102, 2):
# To draw lines across x axis from z = -1 to z = 1
vertexArray.append(glm.vec3(x / 100.0, 0.0, -1.0)) # Vertex position 1
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v1
vertexArray.append(glm.vec3(x / 100.0, 0.0, 1.0)) # Vertex position 2
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v2
for z in range(-100, 102, 2):
# To draw lines across z axis from x = -1 to x = 1
vertexArray.append(glm.vec3(-1.0, 0.0, z / 100.0)) # Vertex position 1
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v1
vertexArray.append(glm.vec3(1.0, 0.0, z / 100.0)) # Vertex position 2
vertexArray.append(glm.vec3(1.0, 1.0, 1.0)) # color for v2
for i in range(10000):
indexArray.append(i)
vao = GLuint()
vbo = GLuint()
ebo = GLuint()
vertexArray = np.array(vertexArray, dtype=np.float32)
indexArray = np.array(indexArray, dtype=np.float32)
# Bind vao
glGenVertexArrays(1, vao)
glBindVertexArray(vao)
# Upload Vertex Buffer (VBO) to the GPU, keep a reference to it (vertexBufferObject)
glGenBuffers(1, vbo)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, vertexArray.nbytes, vertexArray, GL_STATIC_DRAW)
# Upload Index Buffer (EBO) to the GPU, keep a reference to it (elementBufferObject)
glGenBuffers(1, ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexArray.nbytes, indexArray, GL_STATIC_DRAW)
# Position
glVertexAttribPointer(0,
3,
GL_FLOAT,
GL_FALSE,
24,
None
)
glEnableVertexAttribArray(0)
# Color
glVertexAttribPointer(1,
3,
GL_FLOAT,
GL_FALSE,
24,
ctypes.c_void_p(12)
)
glEnableVertexAttribArray(1)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
return vao
def main():
# Initialize the library
#glfw.glewExperimental = GL_TRUE
if not glfw.init():
return
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 2)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)
# Create a windowed mode window and its OpenGL context
window = glfw.create_window(SCR_WIDTH, SCR_HEIGHT, "OpenGL", None, None)
if not window:
glfw.terminate()
return
# Make the window's context current
glfw.make_context_current(window)
glfw.set_framebuffer_size_callback(window, framebuffer_size_callback)
#glEnable(GL_DEPTH_TEST)
#glDepthFunc(GL_LESS)
shader = Shader.Shader("VertexShader.vsh", "FragmentShader.fsh")
vao = create_v_array()
glUseProgram(shader.ID)
# Loop until the user closes the window
while not glfw.window_should_close(window):
glClearColor(0.2, 0.3, 0.3, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(shader.ID)
glBindVertexArray(vao)
model = glm.mat4(1)
view = glm.mat4(1)
model = glm.rotate(model, glm.radians(15.0), glm.vec3(1.0, 0.0, 0.0))
view = glm.translate(view, glm.vec3(0.0, 0.0, -1.0))
projection = glm.perspective(glm.radians(45.0), (SCR_WIDTH/SCR_HEIGHT), 0.1, 100.0)
shader.set_mat4("model", model)
shader.set_mat4("view", view)
shader.set_mat4("projection", projection)
glBindVertexArray(vao)
glDrawElements(GL_LINES, 20000, GL_UNSIGNED_INT, None)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
if __name__ == "__main__":
main()
Custom Shader class:
class Shader:
def __init__(self, vertex_path, fragment_path):
vertex_code = get_file_content(vertex_path)
fragment_code = get_file_content(fragment_path)
vertex = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex, vertex_code)
glCompileShader(vertex)
result = glGetShaderiv(vertex, GL_COMPILE_STATUS)
if not (result):
print("Vertex shader ERROR!")
raise RuntimeError(glGetShaderInfoLog(vertex))
fragment = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment, fragment_code)
glCompileShader(fragment)
result2 = glGetShaderiv(fragment, GL_COMPILE_STATUS)
if not (result2):
print("Fragment shader ERROR!")
raise RuntimeError(glGetShaderInfoLog(vertex))
self.ID = glCreateProgram()
glAttachShader(self.ID, vertex)
glAttachShader(self.ID, fragment)
glLinkProgram(self.ID)
success = glGetProgramiv(self.ID, GL_LINK_STATUS)
if not success:
print("gad darn")
infolog = glGetProgramInfoLog(self.ID)
print("ERROR::SHADER::PROGRAM::LINKING_FAILED\n", infolog)
glDeleteShader(vertex)
glDeleteShader(fragment)
def set_mat4(self, name, matrix):
glUniformMatrix4fv(glGetUniformLocation(self.ID, name), 1, GL_FALSE, glm.value_ptr(matrix))
def use(self):
glUseProgram(self.ID)
# Helper Function
def get_file_content(file):
with open(file) as f:
content = f.read()
return content
glsl files:
Vertex Shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 vertexColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
vertexColor = aColor;
}
Fragment Shader:
#version 330 core
in vec3 vertexColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(vertexColor.r, vertexColor.g, vertexColor.b, 1.0f);
}
The type of the indices must be integral instead of the floating point:
indexArray = np.array(indexArray, dtype=np.float32)
indexArray = np.array(indexArray, dtype=np.uint32)

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;

PyOpenGL passing variable to the vertex shader

After a few suggestion I decided the learn a little bit OpenGL with the hard way. I tried to pass a (float - named myAttrib) variable to the vertex shader and it seemed to work (line 33), but obviously doesn't. Later I would like to divide the code into further parts.
Code:
from OpenGL.GL import *
from OpenGL.GL.shaders import *
import pygame
from pygame.locals import *
import numpy, time
def getFileContent(file):
content = open(file, 'r').read()
return content
def initDraw():
pygame.init()
pygame.display.set_mode((640, 480), HWSURFACE | OPENGL | DOUBLEBUF)
vertices = [0.0, 0.5,
0.5, -0.5,
-0.5, -0.5,]
vertices = numpy.array(vertices, dtype = numpy.float32)
myAttrib = 0.3
vertexShader = compileShader(getFileContent("helloTriangle.vert"), GL_VERTEX_SHADER)
fragmentShader = compileShader(getFileContent("helloTriangle.frag"), GL_FRAGMENT_SHADER)
shaderProgram = glCreateProgram()
glAttachShader(shaderProgram, vertexShader)
glAttachShader(shaderProgram, fragmentShader)
glBindAttribLocation(shaderProgram, 1, "myAttrib")
glLinkProgram(shaderProgram)
print(glGetAttribLocation(shaderProgram, "myAttrib"))
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices)
glEnableVertexAttribArray(0)
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glUseProgram(shaderProgram)
glDrawArrays(GL_TRIANGLES, 0, 3)
pygame.display.flip()
time.sleep(2)
initDraw()
Vertex shader:
#version 130
attribute vec2 vPosition;
attribute float myAttrib;
void main()
{
gl_Position = vec4(vPosition.x, vPosition.y + myAttrib, 0.0, 1.0);
}
Fragment shader:
#version 130
void main()
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
If you use an attribute, the you have to define an array of generic vertex attributes, with one attribute value per vertex coordinate (3 in your case). See Vertex Specification.
If you would use a uniform, then you can set one value for the program stored in an uniform variable in the default uniform block (you can imagine this somehow like a global variable). See Uniform (GLSL):
Case 1 - Attribute myAttrib:
Vertex shader
#version 130
attribute vec2 vPosition;
attribute float myAttrib;
void main()
{
gl_Position = vec4(vPosition.x, vPosition.y + myAttrib, 0.0, 1.0);
}
You have to define an array of generic vertex attributes as you do it for the vertex coordinates:
attrib = [-0.2, 0.2, 0.0]
myAttrib = numpy.array(attrib, dtype = numpy.float32)
# .....
glLinkProgram(shaderProgram)
myAttrib_location = glGetAttribLocation(shaderProgram, "myAttrib")
# .....
glVertexAttribPointer(myAttrib_location, 1, GL_FLOAT, GL_FALSE, 0, myAttrib)
glEnableVertexAttribArray(myAttrib_location)
or
glBindAttribLocation(shaderProgram, 1, "myAttrib")
glLinkProgram(shaderProgram)
# .....
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, myAttrib)
glEnableVertexAttribArray(1)
Case 2 - Uniform myUniform
Vertex shader
#version 130
attribute vec2 vPosition;
uniform float myUniform;
void main()
{
gl_Position = vec4(vPosition.x, vPosition.y + myUniform, 0.0, 1.0);
}
Get the location of the uniform ("myUniform") by glGetUniformLocation and set the value to the uniform by glUniform:
myUniform = 0.3
# .....
glLinkProgram(shaderProgram)
myUniform_location = glGetUniformLocation(shaderProgram, "myUniform")
# .....
glUseProgram(shaderProgram)
glUniform1f(myUniform_location, myUniform)
glDrawArrays(GL_TRIANGLES, 0, 3)

How to update data with a VBO and Pyglet

I would like to make a mesh with Pyglet that is changing every frame. Therefore I need to update the vertices very often and I thought that a VBO would be the fastest way to go here (correct me if I am wrong). Below an example for Points. Is this the correct way of doing it? I read that the number of glBindBuffer calls should be minimised, but here it is called every frame. also GL_DYNAMIC_DRAW is enabled, but if I change it to GL_STATIC_DRAW it is still working. It makes me wondering if this is a correct setup for fast computation
import pyglet
import numpy as np
from pyglet.gl import *
from ctypes import pointer, sizeof
vbo_id = GLuint()
glGenBuffers(1, pointer(vbo_id))
window = pyglet.window.Window(width=800, height=800)
glClearColor(0.2, 0.4, 0.5, 1.0)
glEnableClientState(GL_VERTEX_ARRAY)
c = 0
def update(dt):
global c
c+=1
data = (GLfloat*4)(*[500+c, 100+c,300+c,200+c])
glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
glBufferData(GL_ARRAY_BUFFER, sizeof(data), 0, GL_DYNAMIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data)
pyglet.clock.schedule(update)
glPointSize(10)
#window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0, 0, 0)
glVertexPointer(2, GL_FLOAT, 0, 0)
glDrawArrays(GL_POINTS, 0, 2)
pyglet.app.run()
You don't need to call glBufferData every single time in update - create and fill the VBO once (see setup_initial_points) and only update it with glBufferSubData. In case you are only working with a single VBO, you can also comment out the glBindBuffer call in update() (see code below).
GL_DYNAMIC_DRAW vs GL_STATIC_DRAW won't make a big difference in this example since you are pushing very few data onto the GPU.
import pyglet
from pyglet.gl import *
from ctypes import pointer, sizeof
window = pyglet.window.Window(width=800, height=800)
''' update function '''
c = 0
def update(dt):
global c
c+=1
data = calc_point(c)
# if there's only on VBO, you can comment out the 'glBindBuffer' call
glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data)
pyglet.clock.schedule(update)
''' draw function '''
#window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0, 0, 0)
glVertexPointer(2, GL_FLOAT, 0, 0)
glDrawArrays(GL_POINTS, 0, 2)
''' calculate coordinates given counter 'c' '''
def calc_point(c):
data = (GLfloat*4)(*[500+c, 100+c, 300+c, 200+c])
return data
''' setup points '''
def setup_initial_points(c):
vbo_id = GLuint()
glGenBuffers(1, pointer(vbo_id))
data = calc_point(c)
glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
glBufferData(GL_ARRAY_BUFFER, sizeof(data), 0, GL_DYNAMIC_DRAW)
return vbo_id
############################################
vbo_id = setup_initial_points(c)
glClearColor(0.2, 0.4, 0.5, 1.0)
glEnableClientState(GL_VERTEX_ARRAY)
glPointSize(10)
pyglet.app.run()

Categories