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))
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))
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)
When I try to draw a cube using the GLSL programmable pipeline of OpenGL I get a fully yellow screen, which is the color of the cube, and even if I used glTranslatef() and tried to zoom out by any value, the screen is just fully yellow. How can I zoom out so I can see the entire cube, not just a pure yellow screen?
Full replicable code:
import time
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import *
import numpy
width = 500
height = 500
vertices = [(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1), (-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)]
faces = [(4, 0, 3, 7), (1, 0, 4, 5), (0, 1, 2, 3), (1, 5, 6, 2), (3, 2, 6, 7), (5, 4, 7, 6)]
def draw_shapes():
cube = []
for i, face in enumerate(faces):
for vertex in face:
cube.append(vertices[vertex])
cube = numpy.array(cube, dtype=numpy.float32)
vertex_shader = """
#version 140
in vec4 position;
void main(){
gl_Position = position;
}
"""
frag_shader = """
#version 140
void main(){
gl_FragColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);
}
"""
shaders = compileProgram(compileShader(vertex_shader, GL_VERTEX_SHADER),
compileShader(frag_shader, GL_FRAGMENT_SHADER))
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 192, cube, GL_STATIC_DRAW)
position = glGetAttribLocation(shaders, "position")
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(position)
glUseProgram(shaders)
def showScreen():
global width, height
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawArrays(GL_TRIANGLES, 0, 24)
draw_shapes()
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glLightfv(GL_LIGHT0, GL_POSITION, (0, 0, 0, 1)) # point light from the left, top, front
glLightfv(GL_LIGHT0, GL_AMBIENT, (1, 1, 1, 1))
glutSwapBuffers()
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
glutReshapeWindow(width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (width / height), 0.0001, 1000)
glViewport(0, 0, width, height)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -5)
glRotatef(3, 1, 0, 0)
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutReshapeFunc(reshapeWindow)
gluPerspective(45, (width / height), 0.0001, 100)
while True:
glutMainLoopEvent()
glutPostRedisplay()
time.sleep(0.01)
As already indicated in the comments by user253751 and derhass, the code mixes the fixed pipeline OpenGL approach with the programmable shader stages approach of modern OpengL, while it should be either one or the other. The legacy functions currently have no effect. It's probably best to start over using a book or tutorial on modern OpenGL, instead of converting a legacy project.
A modern OpenGL version of this project will involve:
Defining the transformation matrices (model/view/projection) on the CPU.
Uploading one or more transformation matrices as uniforms to the GPU.
Uploading the light parameters as uniforms to the GPU.
Multiplying each vertex with the transformation matrix using the vertex shader.
Manually programming the lighting/shading calculations in the vertex shader (for Gouraud shading) or fragment shader (for Blinn-Phong/Phong shading).
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()
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.