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()
Related
The quad that I have created is not the same color as I specified. The quad is supposed to be completely red, as evidenced in the code below. The last three values in each row are the ones that specify the color.
quad = [-0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0]
Instead of the expected result, I get this:
How can I make the quad look fully red, and not multiple colors?
Full code:
import math
import glfw
import numpy
import pyrr
from OpenGL.GL import *
from OpenGL.GL.shaders import *
width, height = 500, 500
def draw():
global shader
quad = [-0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, 0.5, 0.0, width / height, 1.0, 0.0, 0.0,
0.5, -0.5, 0.0, width / height, 1.0, 0.0, 0.0]
quad = numpy.array(quad,
dtype='float32')
indices = [0, 1, 2, 3]
indices = numpy.array(indices, dtype='uint32')
vertex_shader_ = """
#version 140
in vec4 position;
in vec3 color;
uniform mat4 transform;
out vec4 out_color;
void main(){
gl_Position = position;
out_color = vec4(color.rgb, 1);
};
"""
fragment_shader_ = """
#version 140
in vec4 out_color;
void main(){
gl_FragColor = out_color;
};
"""
shader = compileProgram(compileShader(vertex_shader_, GL_VERTEX_SHADER),
compileShader(fragment_shader_, GL_FRAGMENT_SHADER))
glUseProgram(shader)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, quad.nbytes, quad, GL_STATIC_DRAW)
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW)
position = glGetAttribLocation(shader, "position")
glVertexAttribPointer(position, 4, GL_FLOAT, GL_FALSE, 28, ctypes.c_void_p(0))
glEnableVertexAttribArray(position)
color = glGetAttribLocation(shader, "color")
glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(16))
glEnableVertexAttribArray(color)
def Screen():
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawArrays(GL_POLYGON, 0, 4)
draw()
glViewport(0, 0, width, height)
def main():
global width, height
if not glfw.init():
return
window = glfw.create_window(500, 500, "Opengl GLFW Window", None, None)
if not window:
glfw.terminate()
return
glfw.make_context_current(window)
while not glfw.window_should_close(window):
glfw.poll_events()
Screen()
width, height = glfw.get_window_size(window)
print(width, height)
glViewport(0, 0, width, height)
glfw.swap_buffers(window)
glfw.terminate()
if __name__ == '__main__':
main()
The stride parameter in each glVertexAttribPointer() was given two different numbers. Thanks for #genpfault for pointing that out!
so i am following a tutorial series on pyglet but i cant make shaders because it causes errors v
Traceback (most recent call last):
File "/home/awesomenoob/Coding/Python/window_with_shade.py", line 20, in <module>
window=window1(1280, 720,"tutorial", resizable=True)
File "/home/awesomenoob/Coding/Python/window_with_shade.py", line 9, in __init__
self.triangle=Triangle()
File "/home/awesomenoob/Coding/Python/Triangle.py", line 52, in __init__
glUseProgram(shader)
File "/home/awesomenoob/.local/lib/python3.7/site-packages/pyglet/gl/lib.py", line 107, in errcheck
raise GLException(msg)
pyglet.gl.lib.GLException: b'invalid operation'
and the code i am using is
window_with_shade.py v
from pyglet.gl import *
from Triangle import Triangle
class window1(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_minimum_size(400,300)
glClearColor(0.2,0.3,0.2,1.0)
self.triangle=Triangle()
def on_draw(self):
self.clear()
glDrawArrays(GL_TRIANGLES, 0, 3)
def on_resize(self,width,height):
glViewport(0,0,width,height)
if __name__=="__main__":
window=window1(1280, 720,"tutorial", resizable=True)
pyglet.app.run()
triangle.py v
from pyglet.gl import *
import ctypes
class Triangle:
def __init__(self):
self.triangle = [-0.5, -0.5, 0.0, 1.0, 0.0 ,0.0,
0.5, -0.5, 0.0, 0.0, 1.0, 0.0,
0.0, 0.5, 0.0, 0.0, 0.0, 1.0]
self.vertex_shader_source= b"""
#version 330
in layout(location = 0) vec3 positions
in layout(location = 1) vec3 colors
out vec3 newColor:
void main()
(
gl_Position = vec4(position, 1.0f):
newColor= color:
)
"""
self.fragment_shader_source= b"""
#version 330
in vec3 newColor:
in vec4 outColor:
void main()
(
outColor = vec4(newColor, 1.0f):
)
"""
vertex_buff= ctypes.create_string_buffer(self.vertex_shader_source)
c_vertex=ctypes.cast(ctypes.pointer(ctypes.pointer(vertex_buff)), ctypes.POINTER(ctypes.POINTER(GLchar)))
vertex_shader= glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex_shader, 1, c_vertex, None)
glCompileShader(vertex_shader)
fragment_buff= ctypes.create_string_buffer(self.fragment_shader_source)
c_fragment=ctypes.cast(ctypes.pointer(ctypes.pointer(fragment_buff)), ctypes.POINTER(ctypes.POINTER(GLchar)))
fragment_shader= glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment_shader, 1, c_fragment, None)
glCompileShader(fragment_shader)
shader= glCreateProgram()
glAttachShader(shader, vertex_shader)
glAttachShader(shader, fragment_shader)
glLinkProgram(shader)
glUseProgram(shader)
vbo= GLuint(0)
glGenBuffers(1, vbo)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, 72, (GLfloat * len(self.triangle))(*self.triangle),GL_STATIC_DRAW)
#positions
glVertexAttribPointer(0, 3, GLFLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
#colors
glVertexAttribPointer(1, 3, GLFLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
pls help, the tutorial is a bit old. using python 3.7.5 and pyglet version 1.5.11.
this is filler text because i dont know what else to say.this is filler text because i dont know what else to say.this is filler text because i dont know what else to say.
Found this error also on Github.
Apparently you need an environment variable to be set. Here is
the solution by crramirez:
The right command compatible to most mesa versions is:
export -n LIBGL_ALWAYS_INDIRECT
or
env -u LIBGL_ALWAYS_INDIRECT python3 your-program.py
Just run one of these commands in the terminal of your choice.
The shader code has multiple syntax errors. See the corrected code:
class Triangle:
def __init__(self):
self.triangle = [-0.5, -0.5, 0.0, 1.0, 0.0 ,0.0,
0.5, -0.5, 0.0, 0.0, 1.0, 0.0,
0.0, 0.5, 0.0, 0.0, 0.0, 1.0]
self.vertex_shader_source= b"""
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
out vec3 newColor;
void main()
{
gl_Position = vec4(position, 1.0f);
newColor = color;
}
"""
self.fragment_shader_source= b"""
#version 330
in vec3 newColor;
out vec4 outColor;
void main()
{
outColor = vec4(newColor, 1.0f);
}
"""
# [...]
Furthermore there is a typo. GL_FLOAT rather than GLFLOAT:
#positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
#colors
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
I Discovered The Problem Was Wrong Opengl Version
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.
I'm relatively new to PyOpenGL and started learning about transformations and model, view, and projection matrices. Which was fine until I compiled and ran my code, and found that my object suddenly disappeared, to be honest I don't know if OpenGL is clipping my object or simply not showing it because of some camera error, but I believe it has something to do with the projection matrix implemented in my code, because when I'd cut the projection matrix out of my code and would run my program, everything was suddenly working again, except for the fact I wouldn't have perspective projection implemented. Well anyways any would be very appreciated :D
Here's my PyOpenGL code.
import OpenGL, PIL, pygame, numpy, pyrr, math, sys, os
from OpenGL.GL import *
from PIL import Image
from pyrr import Matrix44, Vector4, Vector3, Quaternion
VERT_DATA = numpy.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="float32")
COLOR_DATA = numpy.array([1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 1.0, 1.0],
dtype="float32")
TEXTURE_COORD_DATA = numpy.array([0.5, 0.5,
0.5, -0.5,
-0.5, -0.5,
-0.5, 0.5],
dtype="float32")
INDICES = numpy.array([0, 1, 3,
1, 2, 3],
dtype="int32")
class GLProgram:
def __init__(self):
self.gl_program = glCreateProgram()
self.mvp_matrix = self.projection()
self.shaders()
self.gl_buffers()
def gl_texture(self, texture_path):
image = Image.open(texture_path).transpose(Image.FLIP_TOP_BOTTOM)
image_data = numpy.fromstring(image.tobytes(), numpy.uint8)
width, height = image.size
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data)
glGenerateMipmap(GL_TEXTURE_2D)
return texture
def gl_buffers(self):
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
self.pos_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.pos_vbo)
glBufferData(GL_ARRAY_BUFFER, VERT_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
self.text_coord_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.text_coord_vbo)
glBufferData(GL_ARRAY_BUFFER, TEXTURE_COORD_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)
self.pos_ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES, GL_STATIC_DRAW)
self.brick_texture = self.gl_texture("check.jpg")
def shaders(self):
vertex_shader = glCreateShader(GL_VERTEX_SHADER)
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
with open("VertexShader.vert", "r") as vert_file:
vert_source = vert_file.read()
with open("FragmentShader.frag", "r") as frag_file:
frag_source = frag_file.read()
glShaderSource(vertex_shader, vert_source)
glShaderSource(fragment_shader, frag_source)
glCompileShader(vertex_shader)
if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(vertex_shader)
print ("Compilation Failure for " + vertex_shader + " shader:\n" + info_log)
glCompileShader(fragment_shader)
if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(fragment_shader)
print ("Compilation Failure for " + fragment_shader + " shader:\n" + info_log)
glAttachShader(self.gl_program, vertex_shader)
glAttachShader(self.gl_program, fragment_shader)
glLinkProgram(self.gl_program)
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
def projection(self):
scale_matrix = pyrr.matrix44.create_from_scale(Vector3([1, 1, 1]))
rot_matrix = Matrix44.identity()
trans_matrix = pyrr.matrix44.create_from_translation(Vector3([1, 1, 0]))
model_matrix = scale_matrix * rot_matrix * trans_matrix
view_matrix = pyrr.matrix44.create_look_at(numpy.array([4, 3, 3]), numpy.array([1, 1, 0]), numpy.array([0, 1, 0]))
proj_matrix = pyrr.matrix44.create_perspective_projection_matrix(45.0, 1280/720, 0.1, 1000.0)
mvp_matrix = proj_matrix * view_matrix * model_matrix
return mvp_matrix
def display(self):
glEnable(GL_DEPTH_TEST)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.gl_program)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.brick_texture)
texture_uniform = glGetUniformLocation(self.gl_program, "the_texture")
glUniform1i(texture_uniform, 0)
trans_uniform = glGetUniformLocation(self.gl_program, "mvp")
glUniformMatrix4fv(trans_uniform, 1, GL_FALSE, self.mvp_matrix)
glBindVertexArray(self.vao)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glUseProgram(0)
def main():
pygame.init()
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.display.set_caption("3D Graphics")
pygame.display.set_mode((1280, 720), pygame.DOUBLEBUF | pygame.OPENGL)
clock = pygame.time.Clock()
gl = GLProgram()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
clock.tick(60)
gl.display()
pygame.display.flip()
if __name__ == "__main__":
main()
Vertex Shader:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 text_coord;
out vec2 final_text_coord;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(position, 1.0);
final_text_coord = text_coord;
}
Fragment Shader:
#version 330 core
in vec2 final_text_coord;
out vec4 frag_color;
uniform sampler2D the_texture;
void main() {
frag_color = texture(the_texture, final_text_coord);
}
There were a couple of issues with your code:
Matrix multiplication. Multiplying two 2-dimensional numpy arrays together results in a component-wise product, not in matrix multiplication. Using numpy.matmul will solve that one.
The matrix conventions were not clear.
As the documentation for pyrr states, matrices are laid out as row-major in memory, which is the opposite of GL's default convention. Furthermore, pyrr creates the matrices transposed to standrad GL conventions. One might think that both things will cancel itself out, that is only true to a certain extent. It will break whenever you actually do some other operations on these matrices (like multiplication), which will then use the native convention, and screw things up.
I did hack around in your code, providing two different solutions. Since you changed your code a bit between your question and your answer, I ended up with a wild mix of both versions. I also disabled the texturing stuff because I'm lacking some libraries (and the image files). I modified the fragment shader accordingly, but that is not important.
import OpenGL, PIL, pygame, numpy, pyrr, math, sys, os
from OpenGL.GL import *
from PIL import Image
from pyrr import Matrix44, Vector4, Vector3, Quaternion
VERT_DATA = numpy.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="float32")
COLOR_DATA = numpy.array([1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 1.0, 1.0],
dtype="float32")
TEXTURE_COORD_DATA = numpy.array([0.5, 0.5,
0.5, -0.5,
-0.5, -0.5,
-0.5, 0.5],
dtype="float32")
INDICES = numpy.array([0, 1, 3,
1, 2, 3],
dtype="int32")
WINDOW_WIDTH=1280
WINDOW_HEIGHT=720
class GLProgram:
def __init__(self):
self.gl_program = glCreateProgram()
self.mvp_matrix = self.projection()
self.shaders()
self.gl_buffers()
self.cube_model_matrix, self.cube_view_matrix, self.cube_proj_matrix = self.gl_translate(Vector3([1.0, 1.0, 1.0]), 45.0, Vector3([0.5, 0.5, 0.5]))
self.cube_mvp = self.gl_translate3(Vector3([1.0, 1.0, 1.0]), -45.0, Vector3([0.5, 0.5, 0.5]))
def gl_texture(self, texture_path):
return 0
def gl_buffers(self):
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
self.pos_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.pos_vbo)
glBufferData(GL_ARRAY_BUFFER, VERT_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
self.text_coord_vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.text_coord_vbo)
glBufferData(GL_ARRAY_BUFFER, TEXTURE_COORD_DATA, GL_STATIC_DRAW)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)
self.pos_ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.pos_ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES, GL_STATIC_DRAW)
self.brick_texture = self.gl_texture("check.jpg")
def shaders(self):
vertex_shader = glCreateShader(GL_VERTEX_SHADER)
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
with open("VertexShader.vert", "r") as vert_file:
vert_source = vert_file.read()
with open("FragmentShader.frag", "r") as frag_file:
frag_source = frag_file.read()
glShaderSource(vertex_shader, vert_source)
glShaderSource(fragment_shader, frag_source)
glCompileShader(vertex_shader)
if not glGetShaderiv(vertex_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(vertex_shader)
print ("Compilation Failure for " + vertex_shader + " shader:\n" + info_log)
glCompileShader(fragment_shader)
if not glGetShaderiv(fragment_shader, GL_COMPILE_STATUS):
info_log = glGetShaderInfoLog(fragment_shader)
print ("Compilation Failure for " + fragment_shader + " shader:\n" + info_log)
glAttachShader(self.gl_program, vertex_shader)
glAttachShader(self.gl_program, fragment_shader)
glLinkProgram(self.gl_program)
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
def projection(self):
scale_matrix = pyrr.matrix44.create_from_scale(Vector3([1, 1, 1]))
rot_matrix = Matrix44.identity()
trans_matrix = pyrr.matrix44.create_from_translation(Vector3([1, 1, 0]))
model_matrix = scale_matrix * rot_matrix * trans_matrix
view_matrix = pyrr.matrix44.create_look_at(numpy.array([4, 3, 3]), numpy.array([1, 1, 0]), numpy.array([0, 1, 0]))
proj_matrix = pyrr.matrix44.create_perspective_projection_matrix(45.0, 1280/720, 0.1, 1000.0)
mvp_matrix = proj_matrix * view_matrix * model_matrix
return mvp_matrix
def gl_translate(self, translation, rotation, scale):
trans_matrix = pyrr.matrix44.create_from_translation(translation)
rot_matrix = numpy.transpose(pyrr.matrix44.create_from_y_rotation(rotation))
scale_matrix = numpy.transpose(pyrr.matrix44.create_from_scale(scale))
model_matrix = scale_matrix * rot_matrix * trans_matrix
view_matrix = pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
numpy.array([0.0, 0.0, 0.0], dtype="float32"),
numpy.array([0.0, 1.0, 0.0], dtype="float32"))
proj_matrix = pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0)
return model_matrix, view_matrix, proj_matrix
def gl_translate2(self, translation, rotation, scale):
trans_matrix = pyrr.matrix44.create_from_translation(translation)
rot_matrix = pyrr.matrix44.create_from_y_rotation(rotation)
scale_matrix = pyrr.matrix44.create_from_scale(scale)
model_matrix = numpy.matmul(numpy.matmul(scale_matrix,rot_matrix),trans_matrix)
view_matrix = pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
numpy.array([0.0, 0.0, 0.0], dtype="float32"),
numpy.array([0.0, 1.0, 0.0], dtype="float32"))
proj_matrix = pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0)
m = numpy.matmul(numpy.matmul(model_matrix,view_matrix),proj_matrix)
return m
def gl_translate3(self, translation, rotation, scale):
trans_matrix = numpy.transpose(pyrr.matrix44.create_from_translation(translation))
rot_matrix = numpy.transpose(pyrr.matrix44.create_from_y_rotation(rotation))
scale_matrix = numpy.transpose(pyrr.matrix44.create_from_scale(scale))
model_matrix = numpy.matmul(numpy.matmul(trans_matrix,rot_matrix),scale_matrix)
view_matrix = numpy.transpose(pyrr.matrix44.create_look_at(numpy.array([2.0, 2.0, 3.0], dtype="float32"),
numpy.array([0.0, 0.0, 0.0], dtype="float32"),
numpy.array([0.0, 1.0, 0.0], dtype="float32")))
proj_matrix = numpy.transpose(pyrr.matrix44.create_perspective_projection(45.0, WINDOW_WIDTH/WINDOW_HEIGHT, 0.1, 200.0))
m = numpy.matmul(numpy.matmul(proj_matrix,view_matrix),model_matrix)
return numpy.transpose(m)
def display(self):
glEnable(GL_DEPTH_TEST)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.gl_program)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.brick_texture)
texture_uniform = glGetUniformLocation(self.gl_program, "the_texture")
glUniform1i(texture_uniform, 0)
trans_uniform = glGetUniformLocation(self.gl_program, "mvp")
glUniformMatrix4fv(trans_uniform, 1, GL_FALSE, self.cube_mvp)
#model_location = glGetUniformLocation(self.gl_program, "model")
#glUniformMatrix4fv(model_location, 1, GL_FALSE, self.cube_model_matrix)
#view_location = glGetUniformLocation(self.gl_program, "view")
#glUniformMatrix4fv(view_location, 1, GL_FALSE, self.cube_view_matrix)
#proj_location = glGetUniformLocation(self.gl_program, "proj")
#glUniformMatrix4fv(proj_location, 1, GL_FALSE, self.cube_proj_matrix)
glBindVertexArray(self.vao)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
glUseProgram(0)
def main():
pygame.init()
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.display.set_caption("3D Graphics")
pygame.display.set_mode((1280, 720), pygame.DOUBLEBUF | pygame.OPENGL)
clock = pygame.time.Clock()
gl = GLProgram()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
clock.tick(60)
gl.display()
pygame.display.flip()
if __name__ == "__main__":
main()
with this Vertex Shader:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 text_coord;
out vec2 final_text_coord;
uniform mat4 mvp;
void main() {
gl_Position = mvp * vec4(position, 1.0);
final_text_coord = text_coord;
}
and this Fragment Shader:
#version 330 core
in vec2 final_text_coord;
out vec4 frag_color;
uniform sampler2D the_texture;
void main() {
frag_color = vec4(1,0,0,1);
}
I specifically added the methods gl_translate2 and gl_translate3. Both result in the same matrix, the 2 variant is just using the native multiplication order convention of the pyrr library, and gl_translate3 use the GL conventions.
I also created the matrix with different parameters as
self.cube_mvp = self.gl_translate3(Vector3([1.0, 1.0, 1.0]), -45.0, Vector3([0.5, 0.5, 0.5]))
which differs in the negative sign for the rotation (and compensates for the additional transpose you do in gl_transaltion).
The result is different to what you got in your posted answer, but that is due to your model matrix being broken in that answer, as a result of the wrong multiplication function (which especially removed the translation part and distorted the rotation into some shear operation).
The result I'm getting with both gl_translate2 and gl_translate3 is:
and this looks very plausible for the parameters specified.
I'm trying to use PyQT and the Python binding for OpenGL to display an image on a rectangle. So far, here's my code:
gVShader = """
attribute vec4 position;
attribute vec2 texture_coordinates;
varying vec4 dstColor;
varying vec2 v_texture_coordinates;
void main() {
v_texture_coordinates = texture_coordinates;
gl_Position = position;
}"""
gFShader = """
uniform sampler2D texture1;
varying vec2 v_texture_coordinates;
void main() {
gl_FragColor = texture2D(texture1, v_texture_coordinates);
}"""
class ProjectiveGLViewer(QtOpenGL.QGLWidget):
def __init__(self, parent=None):
super(ProjectiveGLViewer, self).__init__(parent)
def initializeGL(self):
# load the shaders source code
vshader = QtOpenGL.QGLShader(QtOpenGL.QGLShader.Vertex, self)
if not vshader.compileSourceCode(gVShader):
print vshader.log()
fshader = QtOpenGL.QGLShader(QtOpenGL.QGLShader.Fragment, self)
if not fshader.compileSourceCode(gFShader):
print fshader.log()
# create the shader program, compile, attach shaders to the program, link and use the program
self._program = QtOpenGL.QGLShaderProgram()
self._program.addShader(vshader)
self._program.addShader(fshader)
self._program.link()
self._program.bind()
# data array (2 [position], 2 [texture coordinates])
data = np.array([-1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0], dtype=np.float32)
# create a buffer and bind it to the 'data' array
self.bufferID = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.bufferID)
glBufferData(GL_ARRAY_BUFFER, data.nbytes, data, GL_DYNAMIC_DRAW)
# tell OpenGL how to handle the buffer of data that is already on the GPU
loc = self._program.attributeLocation("position")
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 2, GL_FLOAT, False, 16, ctypes.c_void_p(0))
loc = self._program.attributeLocation("texture_coordinates")
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 2, GL_FLOAT, False, 16, ctypes.c_void_p(8))
self._imageTextureID = glGenTextures(1)
image = QtGui.QImage("image.jpg")
ptr = image.bits()
ptr.setsize(image.byteCount())
image_data = np.asarray(ptr).reshape(image.width(), image.height(), 4)
glBindTexture(GL_TEXTURE_2D, self._imageTextureID)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self._imageTextureID)
self._program.setUniformValue('texture1', 0)
def paintGL(self):
glBindBuffer(GL_ARRAY_BUFFER, self.bufferID)
glClearColor(0, 0.2, 0.3, 1.0)
glClearDepth(1.0)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 6)
def resizeGL(self, w, h):
glViewport(0, 0, w, h)
Not too sure what i'm doing wrong here, but i'm getting back a black rectangle. any ideas?
Your texture is incomplete. You are not setting filtering and wrap modes. Add these after glTexImage2D:
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT )