I'm fairly new to OpenGL and I tried recreating the tutorial from https://learnopengl.com/Getting-started/Hello-Triangle to draw a rectangle in PyOpenGL.
(Original source code: https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp)
The first part of the tutorial that only draws a triangle using glDrawArrays works perfectly but when I try to use glDrawElements nothing is drawn. It doesn't even raise an error, it just shows me a black screen. I'm pretty sure I copied the instructions from the tutorial one by one and since there is no error message, I have no idea what I did wrong.
I would appreciate any sort of help.
My code:
from OpenGL.GL import *
import OpenGL.GL.shaders
import numpy as np
class Shaders:
def vertex(self):
v = """
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
"""
return OpenGL.GL.shaders.compileShader(v, GL_VERTEX_SHADER)
def fragment(self):
f = """
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
"""
return OpenGL.GL.shaders.compileShader(f, GL_FRAGMENT_SHADER)
def main():
# glfw: initialize and configure
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)
window = glfw.create_window(1920, 1080, "Hello World", None, None)
if not window:
glfw.terminate()
return
glfw.make_context_current(window)
# build and compile shaders
s = Shaders()
shader = OpenGL.GL.shaders.compileProgram(s.vertex(), s.fragment())
# set up vertex data and buffers and configure vertex attributes
vertices = np.array([
0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0
], dtype=np.float32)
indices = np.array([
0, 1, 3,
1, 2, 3
])
VAO = glGenVertexArrays(1)
VBO = glGenBuffers(1)
EBO = glGenBuffers(1)
glBindVertexArray(VAO)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 48, vertices, GL_STATIC_DRAW)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 12, indices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0)
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
# render loop
while not glfw.window_should_close(window):
glClearColor(0, 0, 0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(shader)
glBindVertexArray(VAO)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
if __name__ == "__main__":
main()
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):
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
The index buffer consist of 6 indices of the type uint32. Hence the size of the index buffer is 24 instead of 12:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 12, indices, GL_STATIC_DRAW)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 24, indices, GL_STATIC_DRAW)
When using PyOpenGL the size parameter can be ommited (see glBufferData). In this case the size off the array is used:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
Related
I'm making a basic voxel rendering class with python using glfw. I'm using glBufferSubData instead of glBufferData, but it won't render. Here is the code that is suspected to be causing the problem:
# imports
import glfw, numpy as np
from OpenGL.GL import *
from ctypes import *
from core.texture_manager import *
glfw.init()
class VBOManager:
def __init__(self, renderer):
self.renderer = renderer
self.run()
def run(self):
for i in self.renderer.to_add[:self.renderer.to_add_count]:
self.renderer.vertices.extend(i[0])
self.renderer.texCoords.extend(i[1])
vertices = np.array(i[0], dtype=np.float32)
texCoords = np.array(i[1], dtype=np.float32)
# use glBufferSubData
glBindBuffer(GL_ARRAY_BUFFER, self.renderer.vbo)
glBufferData(GL_ARRAY_BUFFER, self.renderer.vertices.nbytes, vertices.nbytes, (c_float * len(vertices))(*vertices))
glFlush()
glVertexPointer(3, GL_FLOAT, 0, None)
glTexCoordPointer(3, GL_FLOAT, 0, None)
glBindBuffer(GL_ARRAY_BUFFER, self.renderer.vbo_1)
glBufferSubData(GL_ARRAY_BUFFER, self.renderer.texCoords.nbytes, texCoords.nbytes, (c_float * len(texCoords))(*texCoords))
glFlush()
self.renderer.to_add.remove(i)
class TerrainRenderer:
def __init__(self, window):
self.window = window
self.vertices = np.array([])
self.texCoords = np.array([])
self.to_add = []
self.to_add_count = 256
self.vbo, self.vbo_1 = glGenBuffers (2)
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glBufferData(GL_ARRAY_BUFFER, 12 * 4, None, GL_STATIC_DRAW)
self.vbo_manager = VBOManager(self)
self.texture_manager = TextureAtlas()
glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glEnableClientState (GL_VERTEX_ARRAY)
def render(self):
try:
self.vbo_manager.run()
except RuntimeError:
pass
glClear (GL_COLOR_BUFFER_BIT)
glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glBindBuffer (GL_ARRAY_BUFFER, self.vbo)
glVertexPointer (3, GL_FLOAT, 0, None)
glBindBuffer(GL_ARRAY_BUFFER, self.vbo_1)
glTexCoordPointer(2, GL_FLOAT, 0, None)
glDrawArrays (GL_QUADS, 0, len(self.vertices))
glDisable(GL_TEXTURE_2D)
glDisable(GL_BLEND)
def add(self, posList, texCoords):
self.to_add.append((numpy.array(posList), numpy.array(texCoords)))
When I start the application that uses this code, it shows no logs, warnings or errors, but it also doesn't render the vertices at all. NOTHING is visible on the screen.
glBufferSubData just updates a buffer object's data store but doesn't creates a buffer object's data store. You need to create the buffer object's data store with glBufferData, after that you can update the data with glBufferSubData. Furthermore, The 2nd argument of glBufferSubData specifies the offset into the buffer object's data store where data replacement will begin.
glBindBuffer(GL_ARRAY_BUFFER, self.vbo_1)
glBufferData(GL_ARRAY_BUFFER, texCoords.nbytes, None, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, self.renderer.vbo_1)
glBufferSubData(GL_ARRAY_BUFFER, 0, texCoords.nbytes, (c_float * len(texCoords))(*texCoords))
code:
import glfw
import numpy as np
from OpenGL.GL import *
def main():
if not glfw.init():
raise RuntimeError('Can not initialize glfw library')
window = glfw.create_window(500, 500, 'Demo', None, None)
if not window:
glfw.terminate()
raise RuntimeError('Can not create glfw window')
glfw.make_context_current(window)
glClearColor(0, 0, 0, 1)
glColor(1, 0, 0, 1)
glPointSize(10.0)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
# The result of following two lines are looks the same
# glBufferData(GL_ARRAY_BUFFER, np.array([0, 0, 0], dtype='float32'), GL_STATIC_DRAW)
glBufferData(GL_ARRAY_BUFFER, np.array([999999999, 999999999, 999999999], dtype='float32'), GL_STATIC_DRAW)
while not glfw.window_should_close(window):
glClear(GL_COLOR_BUFFER_BIT)
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0)
glDrawArrays(GL_POINTS, 0, 1)
glDisableVertexAttribArray(0)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
if __name__ == "__main__":
main()
I'm studying OpenGL and I'm trying to follow the tutorial here. However, I found the position of the point never change even if I change the data in "glBufferData".
I don't known how this happened. Is the function glBufferData not working? Or maybe I made some low-level mistakes.
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. However the type of the parameter is c_void_p.
Therfore 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, 0, 0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
Minimal example:
import glfw
import numpy as np
from OpenGL.GL import *
def main():
if not glfw.init():
raise RuntimeError('Can not initialize glfw library')
window = glfw.create_window(500, 500, 'Demo', None, None)
if not window:
glfw.terminate()
raise RuntimeError('Can not create glfw window')
glfw.make_context_current(window)
glClearColor(0, 0, 0, 1)
glColor(1, 0, 0, 1)
glPointSize(10.0)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, np.array([0.2, -0.2, 0, -0.2, -0.2, 0, 0, 0.2, 0], dtype='float32'), GL_STATIC_DRAW)
while not glfw.window_should_close(window):
glClear(GL_COLOR_BUFFER_BIT)
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glDrawArrays(GL_POINTS, 0, 3)
glDisableVertexAttribArray(0)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
if __name__ == "__main__":
main()
I have written this Python code which will draw a triangle in a window created using GLFW:
import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import numpy as np
vertex_src = """
# version 330 core
in vec3 a_position;
void main() {
gl_position = vec4(a_position, 1.0);
}
"""
fragment_src = """
# version 330 core
out vec4 out_color;
void main() {
out_color = vec4(1.0, 0.0, 0.0, 1.0);
}
"""
if not glfw.init():
print("Cannot initialize GLFW")
exit()
window = glfw.create_window(320, 240, "OpenGL window", None, None)
if not window:
glfw.terminate()
print("GLFW window cannot be creted")
exit()
glfw.set_window_pos(window, 100, 100)
glfw.make_context_current(window)
vertices = [-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0]
colors = [1, 0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0]
vertices = np.array(vertices, dtype=np.float32)
colors = np.array(colors, dtype=np.float32)
shader = compileProgram(compileShader(
vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER))
buff_obj = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, buff_obj)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
position = glGetAttribLocation(shader, "a_position")
glEnableVertexAttribArray(position)
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glUseProgram(shader)
glClearColor(0, 0.1, 0.1, 1)
while not glfw.window_should_close(window):
glfw.poll_events()
glfw.swap_buffers(window)
glfw.terminate()
On running the program, I got this error:
Traceback (most recent call last):
File "opengl.py", line 43, in <module>
shader = compileProgram(compileShader(
File "/usr/local/lib/python3.8/dist-packages/OpenGL/GL/shaders.py", line 235, in compileShader
raise ShaderCompilationError(
OpenGL.GL.shaders.ShaderCompilationError: ("Shader compile failure (0): b'0:2(10): error: GLSL 3.30 is not supported. Supported versions are: 1.10, 1.20, 1.30, 1.00 ES, and 3.00 ES\\n'", [b'\n# version 330 core\nin vec3 a_position;\nvoid main() {\n gl_position = vec4(a_position, 1.0);\n}\n'], GL_VERTEX_SHADER)
It clearly indicates that GLSL 3.30 is not supported. But, this does work in C by setting window hints:
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
How can I set these window hints in Python?
With Python syntax it is
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
Note, there is a typo in your fragment shader. GLSL is case sensitive. It has to be gl_Position rather than gl_position.
In a core profile OpenGL Context you've to use a named Vertex Array Object, because the default Vertex Array Object (0) is not valid:
vao = glGenVertexArrays(1) # <----
glBindVertexArray(vao) # <----
position = glGetAttribLocation(shader, "a_position")
glEnableVertexAttribArray(position)
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
Finally you missed to draw the geometry. Clear the frame buffer and draw the array:
while not glfw.window_should_close(window):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawArrays(GL_TRIANGLES, 0, 3)
glfw.poll_events()
glfw.swap_buffers(window)
Complete example:
import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import numpy as np
vertex_src = """
# version 330 core
in vec3 a_position;
void main() {
gl_Position = vec4(a_position, 1.0);
}
"""
fragment_src = """
# version 330 core
out vec4 out_color;
void main() {
out_color = vec4(1.0, 0.0, 0.0, 1.0);
}
"""
if not glfw.init():
print("Cannot initialize GLFW")
exit()
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
window = glfw.create_window(320, 240, "OpenGL window", None, None)
if not window:
glfw.terminate()
print("GLFW window cannot be creted")
exit()
glfw.set_window_pos(window, 100, 100)
glfw.make_context_current(window)
vertices = [-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0]
colors = [1, 0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0]
vertices = np.array(vertices, dtype=np.float32)
colors = np.array(colors, dtype=np.float32)
shader = compileProgram(compileShader(
vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER))
buff_obj = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, buff_obj)
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
position = glGetAttribLocation(shader, "a_position")
glEnableVertexAttribArray(position)
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
glUseProgram(shader)
glClearColor(0, 0.1, 0.1, 1)
while not glfw.window_should_close(window):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawArrays(GL_TRIANGLES, 0, 3)
glfw.poll_events()
glfw.swap_buffers(window)
glfw.terminate()
I recently got into OpenGL, and used PyOpenGL and the fixed-function pipeline (I know, I know) to draw cubes and stuff.
Anyway, everyone told me that fixed-function is horrible and deprecated, so I got into core-profile OpenGL just now. I've been followig this tutorial, which is in C++, by just transforming everything into Python with basically the same libraries.
I've gotten to the point where I want to render a single 2D triangle using VBOs and VAOs, and the code runs, but doesn't actually draw anything.
Here it is (bear in mind, I'm totally new to this, so I probably messed up an elementary function call somewhere, and I don't really know how everything works):
import numpy as np
import glfw
from OpenGL.GL import *
def main():
glfw.init()
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 5)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
window = glfw.create_window(800, 600, "helo wold", None, None)
glfw.make_context_current(window)
vertices = np.array([-0.5, -0.5, 0, 0.5, -0.5, 0, 0, 0.5, 0], dtype = 'float32')
vertexShaderSource = '''#version 450 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
'''
vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexShaderSource)
glCompileShader(vertexShader)
fragmentShaderSource = '''#version 450 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
'''
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fragmentShaderSource)
glCompileShader(fragmentShader)
shaderProgram = glCreateProgram(1)
glAttachShader(shaderProgram, vertexShader)
glAttachShader(shaderProgram, fragmentShader)
glLinkProgram(shaderProgram)
glDeleteShader(vertexShader)
glDeleteShader(fragmentShader)
vbo, vao = glGenBuffers(1), glGenVertexArrays(1)
glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, len(vertices), vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, 0)
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
while not glfw.window_should_close(window):
glClearColor(0.2, 0.3, 0.3, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(shaderProgram)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, 3)
glfw.swap_buffers(window)
glfw.poll_events()
glfw.terminate()
if __name__ == '__main__':
main()
It opens the window fine, and has the background color I gave it, but it doesn't draw the triangle in any way.
I'd love it if someone could tell me what I'm doing wrong, thanks!
The 2nd parameter of glBufferData has to be the size in bytes, len(vertices)*4 rather than len(vertices):
glBufferData(GL_ARRAY_BUFFER, len(vertices)*4, vertices, GL_STATIC_DRAW)
Since PyOpenGL's glBufferData is overloaded, the size parameter can be omitted (if data is a ctypes array or numpy.array):
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW)
The last parameter of glVertexAttribPointer ahs to be of type const GLvoid *. Thus it has to be None or ctypes.c_void_p(0) rather than 0:
Either
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, None)
or
import ctypes
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, ctypes.c_void_p(0))
Further more I recommend to evaluate if the shaders are compiled successfully (glGetShaderiv):
vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexShaderSource)
glCompileShader(vertexShader)
if not glGetShaderiv(vertexShader, GL_COMPILE_STATUS ):
print('vertex shader compile error:')
print(glGetShaderInfoLog(vertexShader))
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fragmentShaderSource)
glCompileShader(fragmentShader)
if not glGetShaderiv(fragmentShader, GL_COMPILE_STATUS):
print('fragment shader compile error:')
print(glGetShaderInfoLog(fragmentShader))
And the program is linked successfully (glGetProgramiv):
glLinkProgram(shaderProgram)
if not glGetProgramiv(shaderProgram, GL_LINK_STATUS):
print('link error:')
print(glGetProgramInfoLog(shaderProgram))
So I've been racking my brain with nonstop trial and error. I feel like I keep coming back with questions about the same thing and it is frustrating.
I am following the tutorials and looking at the C++ code and following along with Python trying to replicate the result. I've noticed there are subtle differences through trial and error and I've searched high and low on the internet to see if someone else has experienced my issue. 9/10 times it is because the texture was not bound, however, it my case it is.
https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/4.1.textures/textures.cpp
So I am wondering what I am doing wrong... any help?
import os # For mac... I am using a mac
import glfw # We're using this instead of GLUT as we have more flexibility
import numpy as np # We will use numpy for our arrays
# Using the API wrapper instead of something mugh higher. Keep in mind that the API is a state machine
from OpenGL.GL import *
from OpenGL.arrays import *
from ctypes import c_void_p
from PIL import Image
class HelloWindow():
width = 800
height = 640
title = 'Hello Window'
window = None
shader_program = None
vao = None
vbo = None
texture = None
vertex_gsl = """
#version 410 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 3) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main() {
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = vec2(aTexCoord);
}
"""
fragment_gsl = """
#version 410 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D texture1;
void main() {
FragColor = texture(texture1, TexCoord);
}
"""
def __init__(self):
if not glfw.init():
raise TypeError('Unable to initalize glfw')
self.main()
def main(self):
# Set window hints
self.set_window_hints()
# Create the window
self.create_window()
max_vertex_attributes = glGetIntegerv(GL_MAX_VERTEX_ATTRIBS)
print('Maximum number of vertex attributes in a vertex shader is: ' + str(max_vertex_attributes))
# Keep the window open in a loop
self.loop()
def set_window_hints(self):
glfw.window_hint(glfw.SAMPLES, 4)
# Using the core version in Mac OS but can be set to something else
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 1)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
def create_window(self):
self.window = glfw.create_window(self.width, self.height, self.title, None, None)
if not self.window:
raise TypeError('Unable to create the window')
glfw.terminate()
glfw.make_context_current(self.window)
glfw.set_framebuffer_size_callback(self.window, self.frame_buffer_size)
### Let's setup our data
verts = np.array([
# positions # colors # texture coords
[-0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
[-0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],
[0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0],
[0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]
], dtype=np.float32) # OpenGL expects 32 bit data. Not 64 bit if you're on a 64 bit machine
indicies = np.array([
[0, 1, 3],
[1, 2, 3]
], dtype=np.uint32)
self.vao = glGenVertexArrays(1)
self.vbo = glGenBuffers(1)
self.ebo = glGenBuffers(1)
glBindVertexArray(self.vao)
glBindBuffer(GL_ARRAY_BUFFER, self.vbo) # Bind the buffer as an array buffer and not an element buffer
glBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, False, 8 * verts.itemsize, None)
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 3, GL_FLOAT, False, 8 * verts.itemsize, c_void_p(3 * verts.itemsize))
glEnableVertexAttribArray(1)
glVertexAttribPointer(2, 2, GL_FLOAT, False, 8 * verts.itemsize, c_void_p(6 * verts.itemsize))
glEnableVertexAttribArray(2)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
self.setup_shader()
self.texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.texture)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
# Set the wrapping texture parameters for x,y equivalents
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# Set the texture filtering parameters from min to max
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
# Load our image
img = Image.open('../res/container.jpg')
img.transpose(Image.FLIP_TOP_BOTTOM)
img.convert('RGB')
data = np.array(list(img.getdata()), np.uint8)
# Set the texture data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data)
glGenerateMipmap(GL_TEXTURE_2D)
#img.close()
def loop(self):
while not glfw.window_should_close(self.window):
glClearColor(0.2, 0.3, 0.3, 1.0)
glClear(GL_COLOR_BUFFER_BIT) # We always use this after we clear the color
# Maintain aspect ratio (optional)
glfw.set_window_aspect_ratio(self.window, self.width, self.height)
# Handle input
self.capture_input()
# Bind the texture
glBindTexture(GL_TEXTURE_2D, self.texture)
# Do some rendering
glUseProgram(self.shader_program)
glBindVertexArray(self.vao)
#glEnableVertexAttribArray(0)
#glDrawArrays(GL_TRIANGLES, 0, 3)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glfw.swap_interval(1)
glfw.swap_buffers(self.window)
glfw.poll_events()
glfw.destroy_window(self.window)
# Add the vao and vbo to an array so they're deleted. We can use this when creating several vaos and vbos
vao_list = np.array([self.vao], dtype=np.uint32)
vbo_list = np.array([self.vbo], dtype=np.uint32)
glDeleteVertexArrays(1, vao_list)
glDeleteBuffers(1, vbo_list)
glfw.terminate()
def capture_input(self):
if glfw.get_key(self.window, glfw.KEY_ESCAPE) == glfw.PRESS: # Get the key pressed and check if it is escape key
glfw.set_window_should_close(self.window, True)
def frame_buffer_size(self, window, width, height):
glViewport(0, 0, width, height)
def setup_shader(self):
vertex_shader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex_shader, self.vertex_gsl)
glCompileShader(vertex_shader)
if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
glGetShaderInfoLog(vertex_shader, 512, None)
raise TypeError('vertex_shader did not compile correctly. Check the GSL')
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment_shader, self.fragment_gsl)
glCompileShader(fragment_shader)
if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
glGetShaderInfoLog(fragment_shader, 512, None)
raise TypeError('fragment_shader did not compile correctly. Check the GSL')
self.shader_program = glCreateProgram()
glAttachShader(self.shader_program, vertex_shader)
glAttachShader(self.shader_program, fragment_shader)
glLinkProgram(self.shader_program)
if not glGetProgramiv(self.shader_program, GL_LINK_STATUS):
glGetProgramInfoLog(self.shader_program, 512, None)
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
if __name__ == '__main__':
hello_window = HelloWindow()
In the program the attribute index specified for the array of texture coordinates is 2:
glVertexAttribPointer(2, 2, GL_FLOAT, False, 8 * verts.itemsize, c_void_p(6 * verts.itemsize))
glEnableVertexAttribArray(2)
But in the vertex shader the attribute index specified for the texture coordinates is 3, by Layout Qualifier:
layout (location = 3) in vec2 aTexCoord;
Use the same attribute index in both cases and your texture will show up.