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!
Porting over a chapter 7 example of instanced rendering from Superbible OpenGL 7th ed. and run into a problem with the function glBufferSubData
Whatever I do to it, it won't accept the data. Make it into a pointer, a byte string, list itself. Please help any assistance would be very much appreciated. Thank You.
Update: Using the excellent answer from Rabbid76 the function glBufferSubData is now accepting the data and the numpy version is very nice, the ctypes version is an insightful answer and very good to know. Also, about the 2nd parameter, it does indeed need to be a int or long, not a GLuint(0) in python.
Update and Success: Another very fine answer by Rabbid76 to get the rendering working.
The function glVertexAttribPointer needs a pointer to the lengths of the initial lists of data so it can offset. Thank You very much.
source code to: instancedattribs.py
#!/usr/bin/python3
import sys
import time
import ctypes
fullscreen = True
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
import numpy as np
square_buffer = GLuint(0)
square_vao = GLuint(0)
square_program = GLuint(0)
square_vs_source = '''
#version 410 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 instance_color;
layout (location = 2) in vec4 instance_position;
out Fragment
{
vec4 color;
} fragment;
void main(void)
{
gl_Position = (position + instance_position) * vec4(0.25, 0.25, 1.0, 1.0);
fragment.color = instance_color;
}
'''
square_fs_source = '''
#version 410 core
precision highp float;
in Fragment
{
vec4 color;
} fragment;
out vec4 color;
void main(void)
{
color = fragment.color;
}
'''
class Scene:
def __init__(self, width, height):
global square_buffer
global square_vao
global square_program
self.width = width
self.height = height
square_vertices = np.array([
-1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0], dtype='float32')
instance_colors = np.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,
1.0, 1.0, 0.0, 1.0], dtype='float32')
instance_positions = np.array([
-2.0, -2.0, 0.0, 0.0,
2.0, -2.0, 0.0, 0.0,
2.0, 2.0, 0.0, 0.0,
-2.0, 2.0, 0.0, 0.0], dtype='float32')
glGenVertexArrays(1, square_vao)
glGenBuffers(1, square_buffer)
glBindVertexArray(square_vao)
glBindBuffer(GL_ARRAY_BUFFER, square_buffer)
offset = 0 # not GLuint(0)
bufferSize = (len(square_vertices) + len(instance_colors) + len(instance_positions))*4
glBufferData(GL_ARRAY_BUFFER, bufferSize, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(square_vertices)*4, square_vertices)
offset += len(square_vertices)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_colors)*4, instance_colors)
offset += len(instance_colors)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_positions)*4, instance_positions)
offset += len(instance_positions)*4
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
offsetInstanceColor = len(square_vertices)*4
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstanceColor))
offsetInstancPosition = (len(instance_colors) + len(instance_positions))*4
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstancPosition))
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
glVertexAttribDivisor(1, 1)
glVertexAttribDivisor(2, 1)
square_program = glCreateProgram()
square_vs = GLuint(0)
square_vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(square_vs, square_vs_source)
glCompileShader(square_vs)
glAttachShader(square_program, square_vs)
square_fs = GLuint(0)
square_fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(square_fs, square_fs_source)
glCompileShader(square_fs)
glAttachShader(square_program, square_fs)
glLinkProgram(square_program)
glDeleteShader(square_vs)
glDeleteShader(square_fs)
def display(self):
black = [ 0.0, 0.0, 0.0, 0.0 ]
glClearBufferfv(GL_COLOR, 0, black)
glUseProgram(square_program)
glBindVertexArray(square_vao)
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, 4)
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
print('done')
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
start = time.time()
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Instanced Attributes')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
many_cubes = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
expected rendering output:
Ported from: instancedattribs.cpp
In compare to glBufferData, PyOpenGl's glBufferSubData the size parameter can't be omitted.
You've to pass the size of the buffer (in bytes) and a pointer to the buffer. But note, the 2nd and the 3rd parameter have to be a python int or long, even PyOpneGL's GLuint will cause an error.
You've some possibilities, either create an array PyOpenGL's GLfloat
offset = 0
dataArray = (GLfloat*len(square_vertices))(*square_vertices)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
or use pythons built-in ctypes library:
import ctypes
offset = 0
dataArray = (ctypes.c_float*len(square_vertices))(*square_vertices)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
or create a NumPy array:
import numpy as np
offset = 0
dataArray = np.array(square_vertices, dtype='float32')
glBufferSubData(GL_ARRAY_BUFFER, offset, len(dataArray)*4, dataArray)
Note, for the 2nd and 3d parameter you can use a cast to either int (e.g int(offset)) or long (e.g. long(offset)).
Further note, the offset and size parameters to glBufferData, glBufferSubData and glVertexAttribPointer are values in size of bytes rather than the number of elements of the arrays.
The size in bytes is calculated by the number of elements multiplied by the size of 1 element.
The size of 1 element is 4, since the size of float (GLfloat, ctypes.c_float, 'float32') in bytes is 4.
If a named buffer object is bound, then the last parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store, but the type of the parameter is still a pointer.
So you've to use ctypes.c_void_p(offset). If offset is 0 it is possible to pass None.
bufferSize = (len(square_vertices) + len(instance_colors) + len(instance_positions))*4
glBufferData(GL_ARRAY_BUFFER, bufferSize, None, GL_STATIC_DRAW)
glBufferSubData(GL_ARRAY_BUFFER, offset, len(square_vertices)*4, square_vertices)
offset += len(square_vertices)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_colors)*4, instance_colors)
offset += len(instance_colors)*4
glBufferSubData(GL_ARRAY_BUFFER, offset, len(instance_positions)*4, instance_positions)
offset += len(instance_positions)*4
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
offsetInstanceColor = len(square_vertices)*4
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstanceColor))
offsetInstancPosition = (len(instance_colors) + len(instance_positions))*4
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(offsetInstancPosition))
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 would like to be able to combine two textures in a GLSL fragment shader. I am currently using PyOpenGL and everything i've done up to this point using shaders has worked fine.
I am running into difficulty when I try to access multiple textures from the fragment shader, for instance, the following shader displays the correct texture minus the blue pixels:
uniform sampler2D my_texture1;
uniform sampler2D my_texture2;
void main()
{
vec4 color1 = texture2D(my_texture1, gl_TexCoord[1].st);
vec4 color2 = texture2D(my_texture2, gl_TexCoord[2].st);
if (color1.b > 0.8)
discard;
gl_FragColor = color1;
}
but
uniform sampler2D my_texture1;
uniform sampler2D my_texture2;
void main()
{
vec4 color1 = texture2D(my_texture1, gl_TexCoord[1].st);
vec4 color2 = texture2D(my_texture2, gl_TexCoord[2].st);
if (color2.b > 0.8)
discard;
gl_FragColor = color2;
}
results in a blank screen.
I have a feeling that the problem might lie in how i am passing the texture uniforms to the shader but cant for the life of me work out why the first texture works but the second doesn't. Below is the full program.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from Image import *
from OpenGL.GL.shaders import *
ESCAPE = '\033'
global size
size = 512
def drawQuad(B,T,L,R):
glBegin(GL_QUADS)
glMultiTexCoord2f(GL_TEXTURE1, 0.0, 0.0); glMultiTexCoord2f(GL_TEXTURE2, 0.0, 0.0); glVertex3f(B, L, 1.0); ## Bottom Left Of The Texture and Quad
glMultiTexCoord2f(GL_TEXTURE1, 1.0, 0.0); glMultiTexCoord2f(GL_TEXTURE2, 1.0, 0.0); glVertex3f( T, L, 1.0); ## Bottom Right Of The Texture and Quad
glMultiTexCoord2f(GL_TEXTURE1, 1.0, 1.0); glMultiTexCoord2f(GL_TEXTURE2, 1.0, 1.0); glVertex3f( T, R, 1.0); ## Top Right Of The Texture and Quad
glMultiTexCoord2f(GL_TEXTURE1, 0.0, 1.0); glMultiTexCoord2f(GL_TEXTURE2, 0.0, 1.0); glVertex3f(B, R, 1.0); ## Top Left Of The Texture and Quad
glEnd()
def InitGL(Width, Height):
print "Vendor: " + glGetString(GL_VENDOR)
print "Renderer: " + glGetString(GL_RENDERER)
print "OpenGL Version: " + glGetString(GL_VERSION)
print "Shader Version: " + glGetString(GL_SHADING_LANGUAGE_VERSION)
if not glUseProgram:
print 'Missing Shader Objects!'
sys.exit(1)
global program
program = compileProgram(
compileShader('''
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[1] = gl_MultiTexCoord1;
gl_TexCoord[2] = gl_MultiTexCoord2;
}
''',GL_VERTEX_SHADER),
compileShader('''
uniform sampler2D my_texture1;
uniform sampler2D my_texture2;
void main()
{
vec4 color1 = texture2D(my_texture1, gl_TexCoord[1].st);
vec4 color2 = texture2D(my_texture2, gl_TexCoord[2].st);
if (color1.b > 0.8)
discard;
gl_FragColor = color1;
}
''',GL_FRAGMENT_SHADER),
)
#bmp texture 1
image = open("rgb.bmp")
ix = image.size[0]
iy = image.size[1]
image = image.tostring("raw", "RGBX", 0, -1)
glActiveTexture(GL_TEXTURE1)
global my_texture1
my_texture1 = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, my_texture1)
glPixelStorei(GL_UNPACK_ALIGNMENT,1)
glTexImage2D(GL_TEXTURE_2D, 0, 3, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
glGenerateMipmap(GL_TEXTURE_2D)
#bmp texture 2
image = open("rgb2.bmp")
ix = image.size[0]
iy = image.size[1]
image = image.tostring("raw", "RGBX", 0, -1)
glActiveTexture(GL_TEXTURE2)
global my_texture2
my_texture2 = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, my_texture2)
glPixelStorei(GL_UNPACK_ALIGNMENT,1)
glTexImage2D(GL_TEXTURE_2D, 0, 3, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
glGenerateMipmap(GL_TEXTURE_2D)
def DrawGLScene():
global frame, testvar, my_texture1,my_texture2
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, 0)
glUseProgram(program)
myUniformLocation1 = glGetUniformLocation(program, "my_texture1")
glUniform1i(myUniformLocation1, 1)
myUniformLocation2 = glGetUniformLocation(program, "my_texture2")
glUniform1i(myUniformLocation2, 2)
glViewport(0, 0, size,size)
glClearDepth(1.0)
glClearColor (0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-1, 1, -1, 1, -30.0, 30.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_DEPTH_TEST)
drawQuad(-1.0,1.0,-1.0,1.0)
glutSwapBuffers()
def keyPressed(*args):
global texturenumber, shadernumber, frame
# If escape is pressed, kill everything.
if args[0] == ESCAPE:
sys.exit()
def main():
global window
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(size,size)
glutInitWindowPosition(0, 0)
window = glutCreateWindow("Multitexturing")
glutDisplayFunc(DrawGLScene)
glutIdleFunc(DrawGLScene)
glutKeyboardFunc(keyPressed)
InitGL(size,size)
glutMainLoop()
if __name__ == "__main__":
print "Press 'ESC' key to quit."
main()
You actually unbind the second texture before using it:
def DrawGLScene():
global frame, testvar, my_texture1,my_texture2
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, 0)
[...]
After your initialization, texture unit 1 has my_texture1 assigned and texture unit 2 my_texture2. The active texture is still texture 2. So by calling glBindTexture(GL_TEXTURE_2D, 0) you unbind the texture from the active texture unit 2.
What you shoud do is this:
def DrawGLScene():
global frame, testvar, my_texture1,my_texture2
glActiveTexture(GL_TEXTURE1)
glBindTexture(GL_TEXTURE_2D, my_texture1)
glActiveTexture(GL_TEXTURE2)
glBindTexture(GL_TEXTURE_2D, my_texture2)
[...]
You could also simply remove the last four lines (after global frame, testvar, my_texture1,my_texture2) since your init routine takes care of this. However, if you bind and unbind any other textures in your code you have to do the texture unit activation and texture binding as above.