I just started learning pyOpenGL and ran into an issue, my first project is very simple: I'm trying to open a window and draw a simple triangle using shaders. I'm using glfw to create the window and everything compiles properly, but the Triangle isn't being drawn.
Is something wrong with my main loop or my use of a vertex buffer? My shaders and program objects (seem to) work.
Any help would be greatly appreciated.
import OpenGL.GL
import OpenGL
import glfw
import numpy
def createAndCompileShader(type, source):
shader = OpenGL.GL.glCreateShader(type)
OpenGL.GL.glShaderSource(shader, source)
OpenGL.GL.glCompileShader(shader)
result = OpenGL.GL.glGetShaderiv(shader, OpenGL.GL.GL_COMPILE_STATUS)
if result != 1:
raise Exception("Shader didn't compile properly\nShader compilation Log:\n" + str(OpenGL.GL.glGetShaderInfoLog(shader)))
return shader
def createProgramWithShaders(shaders):
ProgramIdentification = OpenGL.GL.glCreateProgram()
for s in shaders:
OpenGL.GL.glAttachShader(ProgramIdentification, s)
OpenGL.GL.glLinkProgram(ProgramIdentification)
linkStatus = OpenGL.GL.glGetProgramiv(ProgramIdentification, OpenGL.GL.GL_LINK_STATUS)
infoLogLength = OpenGL.GL.glGetProgramiv(ProgramIdentification, OpenGL.GL.GL_INFO_LOG_LENGTH)
for s in shaders:
OpenGL.GL.glDetachShader(ProgramIdentification, s)
for s in shaders:
OpenGL.GL.glDeleteShader(s)
return ProgramIdentification
glfw.init()
glfw.window_hint(glfw.SAMPLES, 4)
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, OpenGL.GL.GL_TRUE)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
window = glfw.create_window(1000,800,"TESTING GLFW", None, None)
glfw.make_context_current(window)
glfw.set_input_mode(window,glfw.STICKY_KEYS, OpenGL.GL.GL_TRUE)
VAO = OpenGL.GL.glGenVertexArrays(1)
OpenGL.GL.glBindVertexArray(VAO)
vektoren = numpy.array([-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0], dtype='float32')
vertexBuffer = OpenGL.GL.glGenBuffers(1)
OpenGL.GL.glBindBuffer(OpenGL.GL.GL_ARRAY_BUFFER, vertexBuffer)
OpenGL.GL.glBufferData(OpenGL.GL.GL_ARRAY_BUFFER,len(vektoren), vektoren, OpenGL.GL.GL_STATIC_DRAW)
VertexSource = open("simpleVertexShader.glsl")
FragmentSource = open("simpleFragmentShader.glsl")
shaders = [createAndCompileShader(OpenGL.GL.GL_VERTEX_SHADER, VertexSource.read()), createAndCompileShader(OpenGL.GL.GL_FRAGMENT_SHADER, FragmentSource.read())]
VertexSource.close()
FragmentSource.close()
ProgramID = createProgramWithShaders(shaders)
OpenGL.GL.glClearColor(0.0,1.0,0.0,1.0)
while not glfw.window_should_close(window) and glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS:
OpenGL.GL.glClear( OpenGL.GL.GL_COLOR_BUFFER_BIT)
OpenGL.GL.glUseProgram(ProgramID)
OpenGL.GL.glEnableVertexAttribArray(0)
OpenGL.GL.glBindBuffer(OpenGL.GL.GL_ARRAY_BUFFER, vertexBuffer)
OpenGL.GL.glVertexAttribPointer(0, 3, OpenGL.GL.GL_FLOAT, OpenGL.GL.GL_FALSE, 0, None)
OpenGL.GL.glDrawArrays(OpenGL.GL.GL_TRIANGLES, 0, 3)
OpenGL.GL.glDisableVertexAttribArray(0)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
simpleVertexShader.glsl:
#version 330 core
layout(location = 0) in vec3 vertexposition_modelspace;
void main(){
gl_Position.xyz = vertexposition_modelspace;
gl_Position.w = 1.0;
}
simpleFragmentShader.glsl:
#version 330 core
out vec3 color;
void main(){
color = vec3(1,0,0);
}
The 2nd parameter glBufferData has to be the size of the buffer in bytes and not the number of elements of the array:
Calculate the size of the buffer in bytes, to solve your issue:
buffer_size = len(vektoren) * vektoren.itemsize
OpenGL.GL.glBufferData(OpenGL.GL.GL_ARRAY_BUFFER, buffer_size, vektoren, OpenGL.GL.GL_STATIC_DRAW)
Note, the array can be passed to glBufferData without setting the size explicitly:
OpenGL.GL.glBufferData(OpenGL.GL.GL_ARRAY_BUFFER, vektoren, OpenGL.GL.GL_STATIC_DRAW)
Related
I currently use Asus TUF FX505G that has both integrated and 1050Ti graphic unit. While trying to render array of points to screen, I found out that I would get two different results depeding on which GPU is in use. On 1050Ti the code works not intended. Python version 3.10.8
Main.py
import glfw
import Shader
from OpenGL.GL import *
from Math_3d import Vector3f
class Window:
def __init__(self, width: int, height: int, title: str):
# Exit keyboard flag
self.exitNow = False
# Vertex Buffer Object
# Create point vertex data
self.v3f = Vector3f.data
if not glfw.init():
raise Exception("glfw can not be initialized")
# OpenGL 4.0 version for shader subroutines and Dynamically uniform expression support
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)
self._win = glfw.create_window(width, height, title, None, None)
if not self._win:
glfw.terminate()
raise Exception("glfw window can not be created")
glfw.set_window_pos(self._win, 400, 200)
glfw.make_context_current(self._win)
# Enable key events
# glfw.set_input_mode(self._win, glfw.STICKY_KEYS, GL_TRUE)
# Enable key event callback from keyboard
# glfw.set_key_callback(self._win, self.onKeyboard)
self.program = glCreateProgram()
self.buffer = glGenBuffers(1)
def initialize(self):
# Request program and shader slots from the GPU
vertex = glCreateShader(GL_VERTEX_SHADER)
fragment = glCreateShader(GL_FRAGMENT_SHADER)
# Set shader sources
glShaderSource(vertex, Shader.vertex_code)
glShaderSource(fragment, Shader.fragment_code)
# Compile shaders and check that they have compiled
glCompileShader(vertex)
glCompileShader(fragment)
if not glGetShaderiv(vertex, GL_COMPILE_STATUS):
report_shader = glGetShaderInfoLog(vertex)
print(report_shader)
raise RuntimeError("Vertex shader compilation error")
if not glGetShaderiv(fragment, GL_COMPILE_STATUS):
report_frag = glGetShaderInfoLog(fragment)
print(report_frag)
raise RuntimeError("Fragment shader compilation error")
# Link shaders to program
glAttachShader(self.program, vertex)
glAttachShader(self.program, fragment)
glLinkProgram(self.program)
if not glGetProgramiv(self.program, GL_LINK_STATUS):
print(glGetProgramInfoLog(self.program))
raise RuntimeError('Linking error')
# Get rid of shaders
glDetachShader(self.program, vertex)
glDetachShader(self.program, fragment)
# Make default program to run
glUseProgram(self.program)
# Request a buffer slot from GPU
# buffer = glGenBuffers(1)
# Make this buffer the default one
glBindBuffer(GL_ARRAY_BUFFER, self.buffer)
# Vertex Array Buffer
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
loc = glGetAttribLocation(self.program, 'position')
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, self.v3f.itemsize, GL_FLOAT, False, self.v3f.data.strides[0], None)
glBufferData(GL_ARRAY_BUFFER, self.v3f.data.nbytes, self.v3f.data, GL_DYNAMIC_DRAW)
def renderscene(self):
while not glfw.window_should_close(self._win) and not self.exitNow:
glClear(GL_COLOR_BUFFER_BIT)
glPointSize(30)
glDrawArrays(GL_POINTS, 0, self.v3f.itemsize)
glfw.swap_buffers(self._win)
# Do not refresh screen before receiving event
glfw.wait_events()
glDisableVertexAttribArray(0)
glUseProgram(0)
glfw.terminate()
if __name__ == '__main__':
win = Window(1024, 768, "GLFW Window - Rotate glPoints")
win.initialize() # Create and initialize shaders and initialize Vertex Buffer Object
win.renderscene() # Swap buffer and render scene
Shader.py
vertex_code = """
attribute vec3 position;
void main()
{
gl_Position = vec4(position, 1.0);
}
"""
fragment_code = """
void main()
{
gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0);
}
"""
Math_3d.py
from OpenGL.GL import *
import numpy as np
np.set_printoptions(precision=3, suppress=True)
class Vector3f:
data = np.array([[0.5, 0.5, 0],
[-0.5, 0.5, 0],
[-0.5, -0.5, 0],
[0.5, -0.5, 0]], dtype=np.float32)
def Rotate(self):
pass
if __name__ == '__main__':
vec3 = Vector3f
print(vec3.data)
print("Item size: ", vec3.data.itemsize)
print("Strides: ", vec3.data.strides[0])
print("Bytes:", vec3.data.nbytes)
Ran the code while using both gpus. There should be no apparent buffer overflow.
The problem is here:
glVertexAttribPointer(loc, self.v3f.itemsize, GL_FLOAT, False, self.v3f.data.strides[0], None)
The 2nd argument of glVertexAttribPointer is the number of components of the attribute (x, y, z), which is 3. However, itemsize is the number of bytes of an item, which is 4 for an item of type float.
Correct is:
glVertexAttribPointer(loc, 3, GL_FLOAT, False, self.v3f.data.strides[0], None)
When I press left or right key to rotate 4 points around origin, the screen won't update correctly causing broken rendering. The vertex array is properly introduced to buffer, but I have not found relevant information regarding why this won't render properly.
Here are the relevant codes:
import Shader
from Math_3d import Vector3f
from OpenGL.GL import *
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from PyQt6.QtCore import Qt
class GLWidget(QOpenGLWidget):
def __init__(self):
super().__init__()
# Vertex Buffer Object
# Create point vertex data
self.v3f = Vector3f([[0.5, 0.5, 0.0],
[-0.5, -0.5, 0.0],
[0.5, -0.5, 0.0],
[-0.5, 0.5, 0.0]])
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
def initializeGL(self):
program = glCreateProgram()
buffer = glGenBuffers(1)
# Request program and shader slots from the GPU
vertex = glCreateShader(GL_VERTEX_SHADER)
fragment = glCreateShader(GL_FRAGMENT_SHADER)
# Set shader sources
glShaderSource(vertex, Shader.vertex_code)
glShaderSource(fragment, Shader.fragment_code)
# Compile shaders and check that they have compiled
glCompileShader(vertex)
glCompileShader(fragment)
if not glGetShaderiv(vertex, GL_COMPILE_STATUS):
report_shader = glGetShaderInfoLog(vertex)
print(report_shader)
raise RuntimeError("Vertex shader compilation error")
if not glGetShaderiv(fragment, GL_COMPILE_STATUS):
report_frag = glGetShaderInfoLog(fragment)
print(report_frag)
raise RuntimeError("Fragment shader compilation error")
# Link shaders to program
glAttachShader(program, vertex)
glAttachShader(program, fragment)
glLinkProgram(program)
if not glGetProgramiv(program, GL_LINK_STATUS):
print(glGetProgramInfoLog(program))
raise RuntimeError('Linking error')
# Get rid of shaders
glDetachShader(program, vertex)
glDetachShader(program, fragment)
# Make default program to run
glUseProgram(program)
# Make this buffer the default one
glBindBuffer(GL_ARRAY_BUFFER, buffer)
# Vertex Array Buffer
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
strides = int(self.v3f.buffdata.nbytes/self.v3f.buffdata.itemsize)
loc = glGetAttribLocation(program, 'position')
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 3, GL_FLOAT, False, strides, None)
glBufferData(GL_ARRAY_BUFFER, self.v3f.buffdata.nbytes, self.v3f.buffdata, GL_DYNAMIC_DRAW)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT)
glPointSize(20)
glDrawArrays(GL_POINTS, 0, self.v3f.buffdata.itemsize)
def keyPressEvent(self, e):
if e.key() == Qt.Key.Key_Left:
self.v3f.Rotate(-0.1)
print("Left")
elif e.key() == Qt.Key.Key_Right:
self.v3f.Rotate(0.1)
print("Right")
elif e.key() == Qt.Key.Key_Escape or Qt.Key.Key_Q:
exit()
self.update()
Other module:
from OpenGL.GL import *
import numpy as np
np.set_printoptions(precision=3, suppress=True)
class Vector3f:
def __init__(self, ndarray):
self.data = np.array([ndarray], dtype=np.float32)
# Convert n-dimension arrays to one long array
self.buffdata = self.data.ravel()
self.orig = self.data
self.__rval = 0.0
self.__sval = 0.0
def Rotate(self, val): # Rotation Matrix
self.__rval += val # Create a memory value and add to that value
m1 = np.asmatrix(self.data) # Convert n-dimension array to matrix
rot2 = np.matrix([[np.cos(self.__rval), np.sin(-self.__rval), 0.0], # Create rotation matrix
[np.sin(self.__rval), np.cos(self.__rval), 0.0],
[0.0, 0.0, 0.0]], dtype=np.float32)
rotator = m1 * rot2 # Matrix multiplication
self.buffdata = rotator.ravel() # Convert result to array to be used on GPU buffer
glBufferSubData(GL_ARRAY_BUFFER, 0, self.buffdata.data.nbytes, self.buffdata.data)
For any OpenGL instruction a current and valid OpenGL Context is required. QOpenGLWidget creates an OpenGL context, but the context is only made current before the initializeGL, paintGL, or resizeGL callback.
Therefore you cannot use OpenGL instructions outside the scope of these functions and this is the reason why the call to glBufferSubData fails when called through keyPressEvent.
I am trying to test the following simple example wherein I pass something to the vertex shader and read its output to print.
import moderngl
import numpy as np
import struct
vertex_shader_source = '''
#version 330
in vec3 attPosition;
out vec2 varScreenCoord;
void main ()
{
varScreenCoord = vec2(1.0, 1.5);
}
'''
ctx = moderngl.create_standalone_context()
prog = ctx.program(vertex_shader=vertex_shader_source, varyings=['varScreenCoord'])
# input
verts = np.array([[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0]], dtype='f4')
verts_buf = ctx.buffer(verts.tobytes())
# output
n, c = verts.shape
varScreenCoord = np.zeros((n, c-1)).astype(np.float32)
varScreenCoord_buf = ctx.buffer(varScreenCoord.tobytes())
vao = ctx.vertex_array(prog, content=[
(verts_buf, '3f', 'attPosition')
])
vao.transform(varScreenCoord_buf, vertices=n)
data = struct.unpack("{}f".format(n*2), varScreenCoord_buf.read())
for i in range(0, n*2):
print("value = {}".format(data[i]))
I get the following error when I run this
Traceback (most recent call last):
File "/somepath/moderngl_sample.py", line 32, in <module>
vao = ctx.vertex_array(prog, content=[
File "/somepath/lib/python3.9/site-packages/moderngl/context.py", line 1140, in vertex_array
return self._vertex_array(*args, **kwargs)
File "/somepath/lib/python3.9/site-packages/moderngl/context.py", line 1169, in _vertex_array
res.mglo, res._glo = self.mglo.vertex_array(program.mglo, content, index_buffer_mglo,
moderngl.error.Error: content[0][2] must be an attribute not NoneType
I have checked the buffer format here and it seems to be correct. This indicates that the name might not be referenced in the shader, but my shader clearly has that vertex attribute.
What could be causing this issue?
Thanks.
After using the attPosition value in the shader, this issue was fixed. For example, the shader must be changed as follows
#version 330
in vec3 attPosition;
out vec2 varScreenCoord;
void main ()
{
varScreenCoord = vec2(1.0, 1.5) * attPosition.xy;
}
I am wanting to:
Open the texture from an image via cv2 instead of via ModernGL's load_texture_2d method.
Save the resulting image (in the write method) via cv2 rather than Pillow.
I currently have the following code:
from pathlib import Path
from array import array
import cv2
import numpy as np
from PIL import Image
import moderngl
import moderngl_window
class ImageProcessing(moderngl_window.WindowConfig):
window_size = 3840 // 2, 2160 // 2
resource_dir = Path(__file__).parent.resolve()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.image_processing = ImageTransformer(self.ctx, self.window_size)
# Not working:
#img = cv2.imread("test6.png")
#self.texture = img.astype('f4')
self.texture = self.load_texture_2d("test6.png")
def render(self, time, frame_time):
# View in Window
self.image_processing.render(self.texture, target=self.ctx.screen)
# Headless
self.image_processing.render(self.texture)
self.image_processing.write("output.png")
class ImageTransformer:
def __init__(self, ctx, size, program=None):
self.ctx = ctx
self.size = size
self.program = None
self.fbo = self.ctx.framebuffer(
color_attachments=[self.ctx.texture(self.size, 4)]
)
# Create some default program if needed
if not program:
self.program = self.ctx.program(
vertex_shader="""
#version 330
in vec2 in_position;
in vec2 in_uv;
out vec2 uv;
void main() {
gl_Position = vec4(in_position, 0.0, 1.0);
uv = in_uv;
}
""",
fragment_shader = """
#version 330
uniform sampler2D image;
in vec2 uv;
out vec4 out_color;
void main() {
vec4 color = texture(image, uv);
// do something with color here
out_color = vec4(color.r, 0, 0, color.a);
}
""",
)
# Fullscreen quad in NDC
self.vertices = self.ctx.buffer(
array(
'f',
[
# Triangle strip creating a fullscreen quad
# x, y, u, v
-1, 1, 0, 1, # upper left
-1, -1, 0, 0, # lower left
1, 1, 1, 1, # upper right
1, -1, 1, 0, # lower right
]
)
)
self.quad = self.ctx.vertex_array(
self.program,
[
(self.vertices, '2f 2f', 'in_position', 'in_uv'),
]
)
def render(self, texture, target=None):
if target:
target.use()
else:
self.fbo.use()
texture.use(0)
self.quad.render(mode=moderngl.TRIANGLE_STRIP)
def write(self, name):
# This doesn't work:
raw = self.fbo.read(components=4, dtype='f4')
buf = np.frombuffer(raw, dtype='f4')
cv2.imwrite("OUTPUT_IMAGE.png", buf)
# But this does:
## image = Image.frombytes("RGBA", self.fbo.size, self.fbo.read())
## image = image.transpose(Image.FLIP_TOP_BOTTOM)
## image.save(name, format="png")
if __name__ == "__main__":
ImageProcessing.run()
Currently, when the code is run as-is, no image is saved whatsoever. The window just hangs and nothing happens. I am not sure if I have something wrong in my code or if the datatypes are wrong.
The pillow code (if you uncomment it) works to save it, but please note: While I could convert to a numpy array from Pillow, I would prefer not to in my use-case.
Clarification: The window loads and shows the image result just fine, but doesn't save correctly in the write method.
There is som code missing in your application
The method load_texture_2d creates a moderngl.Texture object. Hence the method loads the file, creates a texture object and loads the texture image from the CPU to the GPU.
cv2.imread just load the image file to a NumPy array, but it doesn't create a moderngl.Texture object.
You have to generate a moderngl.Texture object from the NumPy array:
img = cv2.imread("test6.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # optional
img = np.flip(img, 0).copy(order='C') # optional
self.texture = self.ctx.texture(img.shape[1::-1], img.shape[2], img)
Before writing the buffer into an image, you must reshape the NumPy array according to the image format. For instance:
raw = self.fbo.read(components=4, dtype='f1')
buf = np.frombuffer(raw, dtype='uint8').reshape((*self.fbo.size[1::-1], 4))
cv2.imwrite("OUTPUT_IMAGE.png", buf)
can you tell, in the write() method, what buf.shape is? I think it's a 1-d array at that point and it probably is height * width * 4 elements long.
imwrite() needs it shaped right. try this before imwrite():
buf.shape = (self.size[1], self.size[0], 4)
that should reshape the data as (height, width, 4) and then the imwrite() should accept it.
I recently started to learn OpenGL through Python thanks to several tutorial (especially the Nicolas P. Rougier one: http://www.labri.fr/perso/nrougier/teaching/opengl/).
I am now switching to 3D and I am trying to draw a cube.
Thus, I manage to get some triangles which do not render a cube (this seems to be normal as I do not duplicate my vertices and I use the glDrawArrays function).
However, after, I build an index "vector" to further use the glDrawElements function to render my cube. As a result, I do not get any error but nothing appears on screen.
I hope you could be of some help!
Here is my code:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import ctypes
import numpy as np
import OpenGL.GL as gl
import OpenGL.GLUT as glut
vertex_code = """
uniform float scale;
uniform mat4 matCam;
attribute vec4 color;
attribute vec3 position;
varying vec4 v_color;
void main()
{
gl_Position = matCam*vec4(scale*position, 1.0);
v_color = color;
} """
fragment_code = """
varying vec4 v_color;
void main()
{
gl_FragColor = v_color;
} """
def display():
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
#gl.glDrawArrays(gl.GL_TRIANGLES, 0, 12)
gl.glDrawElements(gl.GL_TRIANGLES, len(index), gl.GL_UNSIGNED_INT, index) # render nothing (i.e. only the background color)
glut.glutSwapBuffers()
def reshape(width,height):
gl.glViewport(0, 0, width, height)
def keyboard( key, x, y ):
if key == '\033':
sys.exit( )
def timer(fps):
global clock
clock += 0.0005*1000.0/fps
print(clock)
# eye = np.array([0,0,1])
# center = np.array([0,clock,0])
# up = np.array([0,1,0])
# mat = computeLookAtMatrix(eye, center, up)
theta = clock;
mat = np.array([[np.cos(theta), 0, np.sin(theta), 0],
[0, 1, 0, 0],
[-np.sin(theta), 0, np.cos(theta), 0],
[0, 0, 0, 1]])
loc = gl.glGetUniformLocation(program, "matCam")
gl.glUniformMatrix4fv(loc, 1, False, mat)
glut.glutTimerFunc(1000/fps, timer, fps)
glut.glutPostRedisplay()
# GLUT init
# --------------------------------------
glut.glutInit()
glut.glutInitDisplayMode(glut.GLUT_DOUBLE | glut.GLUT_RGBA)
glut.glutCreateWindow('Hello world!')
glut.glutReshapeWindow(512,512)
glut.glutReshapeFunc(reshape)
glut.glutDisplayFunc(display)
glut.glutKeyboardFunc(keyboard)
glut.glutTimerFunc(1000/60, timer, 60)
# Build data
# --------------------------------------
data = np.zeros(8, [("position", np.float32, 3),
("color", np.float32, 4)])
data['color'] = [ (1,0,0,1), (0,1,0,1), (0,0,1,1), (1,1,0,1),
(1,0,0,1), (0,1,0,1), (0,0,1,1), (1,1,0,1) ]
data['position'] = [ (-1,-1,1),
(1,-1,1),
(1,1,1),
(-1,1,1),
(-1,-1,-1),
(1,-1,-1),
(1,1,-1),
(-1,1,-1)]
index = np.array([0,1,2,
2,3,0,
1,5,6,
6,2,1,
7,6,5,
5,4,7,
4,0,3,
3,7,4,
4,5,1,
1,0,4,
3,2,6,
6,7,3])
# Build & activate program
# --------------------------------------
# Request a program and shader slots from GPU
program = gl.glCreateProgram()
vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER)
fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
# Set shaders source
gl.glShaderSource(vertex, vertex_code)
gl.glShaderSource(fragment, fragment_code)
# Compile shaders
gl.glCompileShader(vertex)
gl.glCompileShader(fragment)
# Attach shader objects to the program
gl.glAttachShader(program, vertex)
gl.glAttachShader(program, fragment)
# Build program
gl.glLinkProgram(program)
# Get rid of shaders (no more needed)
gl.glDetachShader(program, vertex)
gl.glDetachShader(program, fragment)
# Make program the default program
gl.glUseProgram(program)
# Build buffer
# --------------------------------------
# Request a buffer slot from GPU
buffer = gl.glGenBuffers(1)
# Make this buffer the default one
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buffer)
# Upload data
gl.glBufferData(gl.GL_ARRAY_BUFFER, data.nbytes, data, gl.GL_DYNAMIC_DRAW)
# same for index buffer
buffer_index= gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, buffer_index)
gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, index.nbytes, index, gl.GL_STATIC_DRAW)
# Bind attributes
# --------------------------------------
stride = data.strides[0]
offset = ctypes.c_void_p(0)
loc = gl.glGetAttribLocation(program, "position")
gl.glEnableVertexAttribArray(loc)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buffer)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)
offset = ctypes.c_void_p(data.dtype["position"].itemsize)
loc = gl.glGetAttribLocation(program, "color")
gl.glEnableVertexAttribArray(loc)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buffer)
gl.glVertexAttribPointer(loc, 4, gl.GL_FLOAT, False, stride, offset)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, buffer_index)
# Bind uniforms
# --------------------------------------
loc = gl.glGetUniformLocation(program, "scale")
gl.glUniform1f(loc, 0.5)
clock = 0
loc = gl.glGetUniformLocation(program, "matCam")
print(loc)
gl.glUniformMatrix4fv(loc, 1, False, np.eye(4))
# Enter mainloop
# --------------------------------------
glut.glutMainLoop()
gl.glDrawElements(gl.GL_TRIANGLES, len(index), gl.GL_UNSIGNED_INT, index)
The index argument has two different meanings depending on whether an ELEMENT_ARRAY_BUFFER is bound or not:
If no bound: Then it specifies a pointer to client memory where the indices are stored
If a ELEMENT_ARRAY_BUFFER is bound, then the index parameter specifies an offset into this buffer. This defines where in the buffer the indices start.
In your case, a buffer is bound, so you tell OpenGL to start somewhere in the buffer. But what you want is to start at the beginning of the buffer, thus you have to set index to 0.
gl.glDrawElements(gl.GL_TRIANGLES, len(index), gl.GL_UNSIGNED_INT, ctypes.c_void_p(0))