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.
Related
I have a scene with a sphere spinning, just like Earth and the Sun. I can activate the lighting over the object but i notice that the light is moving with the object when spin, illuminating the same face ever. I want to set the light on a fixed point so, the illumination will be fixed while the "Earth" spin, giving the effect of day and night.
I know that if an object is rotated, all the "coordinate system" moves, not the object, so, i think that the ligth is fixed to the system. I tried to find the point where initialy the light is placed using trigonometry but without positive results. How can i place the light in a fixed spot to reach the day/night effect?
This is my code:
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
#<some other libraries imports>
def sup_texture(surf):
rgbsurf = pygame.image.tostring(surf, 'RGB')
textID = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texID)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
surfrc = surf.get_rect()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, surfrc[2], surfrc[3], 0, GL_RGB, GL_UNSIGNED_BYTE, rgbsurf)
return textID
def texture(arch,arch2):
textID = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, textID)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
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)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,arch2[0], arch2[1], 0, GL_RGB, GL_UNSIGNED_BYTE, arch)
glGenerateMipmap(GL_TEXTURE_2D)
return textID
def oglprint(sup):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glDisable(GL_LIGHTING)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, sup)
glBegin(GL_QUADS)
glTexCoord2f(0, 0); glVertex2f(-1, 1)
glTexCoord2f(0, 1); glVertex2f(-1, -1)
glTexCoord2f(1, 1); glVertex2f(1, -1)
glTexCoord2f(1, 0); glVertex2f(1, 1)
glEnd()
glDisable(GL_TEXTURE_2D)
def esfep(vesf,resol,texture,rotpt,punt,tama):
light_ambient = [0.0, 0.0, 0.0, 1.0]
light_diffuse = [1.0, 1.0, 1.0, 1.0]
light_position = [1, 1, 0, 0.0]
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient)
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse)
glLightfv(GL_LIGHT0, GL_POSITION, light_position)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
rotpt = rotpt + vesf
glOrtho(0,resol[0],resol[1],0,-resol[0],resol[1])
glTranslatef(float(punt[0]),float(punt[1]),-resol[1])
glRotatef(270, 1, 0, 0)
glRotatef(rotpt, 0, 0, 1)
glScalef(1*tama/resol[1],1*tama/resol[1],1*tama/resol[1])
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE )
esf = gluNewQuadric()
gluQuadricTexture(esf, GL_TRUE)
glDisable(GL_DEPTH_TEST)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, texture)
gluSphere(esf,round(resol[1]/2), 50, 50)
gluDeleteQuadric(esf)
glDisable(GL_TEXTURE_2D)
glDisable(GL_DEPTH_TEST)
return rotpt
pygame.init()
resol = (1366,768)
opcp = pygame.HWSURFACE | pygame.DOUBLEBUF | pygame.OPENGL | pygame.FULLSCREEN
displayG = pygame.display.set_mode(resol,opcp)
display = pygame.Surface(resol)
imgpl = pygame.image.load('texture.jpg')
imgplt = imgpl.get_size()
imgpl = pygame.transform.smoothscale(imgpl,(1000,500))
imgpltd = pygame.image.tostring(imgpl,'RGB',False)
planpres = texture(imgpltd,imgplt)
rotpt = randint(270,360)
timer = pygame.time.Clock()
RE = tab(display)
while True:
#<some pygame stuff and display blitting>
opsurf = pygame.Surface.copy(display)
pantsp = sup_texture(opsurf)
botact = 1
while botact == 1:
timer.tick(20)
oglprint(pantsp)
rotpt = esfep(0.05,resol,planpres,rotpt,(0,resol[1] + resol[1]/4.5),1000) #this is the printing of the sphere.
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
x_mouse, y_mouse = pygame.mouse.get_pos()
dot = (x_mouse,y_mouse)
for x in range(len(RE)):
if RE[x].collidepoint(dot):
#<some stuff>
botact = 0
glDeleteTextures(pantsp)
The light position (GL_POSITION) an direction (GL_SPOT_DIRECTION) is transformed by the current modelview matrix when glLighv is called. Make sure that the model view matrix is the Identity matrix, before calling glLight, but set the transformations to the model view matrix:
def esfep(vesf,resol,texture,rotpt,punt,tama):
light_ambient = [0.0, 0.0, 0.0, 1.0]
light_diffuse = [1.0, 1.0, 1.0, 1.0]
light_position = [1, 1, 0, 0.0]
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient)
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse)
# set projection matrix
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0,resol[0],resol[1],0,-resol[0],resol[1])
# set model view identity matrix
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# set light position (multiplied by identity matrix)
glLightfv(GL_LIGHT0, GL_POSITION, light_position)
# set model view transformations
rotpt = rotpt + vesf
glTranslatef(float(punt[0]),float(punt[1]),-resol[1])
glRotatef(270, 1, 0, 0)
glRotatef(rotpt, 0, 0, 1)
glScalef(1*tama/resol[1],1*tama/resol[1],1*tama/resol[1])
# [...]
The latest code below is a ported python program of the tunnel.cpp program from Superbible OpenGL 7th edition.
The cube appears, however the textures do not. There is also supposed to be slight movement toward the tunnel, and that's not happening either. Any ideas what could be causing this?
Update: Thanks to Rabbid76 the textures now appear and they are rendering correctly.
#!/usr/bin/python3
import sys
import time
sys.path.append("./shared")
#from sbmloader import SBMObject # location of sbm file format loader
from ktxloader import KTXObject # location of ktx file format loader
from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44
fullscreen = True
#import numpy.matlib
#import numpy as np
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()
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
render_prog = GLuint(0)
render_vao = GLuint(0)
class uniforms:
mvp = GLint
offset = GLint
tex_wall = GLuint(0)
tex_ceiling = GLuint(0)
tex_floor = GLuint(0)
uniform = uniforms()
class Scene:
def __init__(self, width, height):
global render_prog
global render_vao
global uniform
global tex_wall, tex_ceiling, tex_floor
self.width = width
self.height = height
vs = GLuint(0)
fs = GLuint(0)
vs_source = '''
#version 420 core
out VS_OUT
{
vec2 tc;
} vs_out;
uniform mat4 mvp;
uniform float offset;
void main(void)
{
const vec2[4] position = vec2[4](vec2(-0.5, -0.5),
vec2( 0.5, -0.5),
vec2(-0.5, 0.5),
vec2( 0.5, 0.5));
vs_out.tc = (position[gl_VertexID].xy + vec2(offset, 0.5)) * vec2(30.0, 1.0);
gl_Position = mvp * vec4(position[gl_VertexID], 0.0, 1.0);
}
'''
fs_source = '''
#version 420 core
layout (location = 0) out vec4 color;
in VS_OUT
{
vec2 tc;
} fs_in;
layout (binding = 0) uniform sampler2D tex;
void main(void)
{
color = texture(tex, fs_in.tc);
}
'''
vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vs, vs_source)
glCompileShader(vs)
glGetShaderInfoLog(vs)
fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fs, fs_source)
glCompileShader(fs)
glGetShaderInfoLog(vs)
render_prog = glCreateProgram()
glAttachShader(render_prog, vs)
glAttachShader(render_prog, fs)
glLinkProgram(render_prog)
glDeleteShader(vs)
glDeleteShader(fs)
glGetProgramInfoLog(render_prog)
uniform.mvp = glGetUniformLocation(render_prog, "mvp")
uniform.offset = glGetUniformLocation(render_prog, "offset")
glGenVertexArrays(1, render_vao)
glBindVertexArray(render_vao)
ktxobj = KTXObject()
tex_wall = ktxobj.ktx_load("brick.ktx")
tex_ceiling = ktxobj.ktx_load("ceiling.ktx")
tex_floor = ktxobj.ktx_load("floor.ktx")
textures = [ tex_floor, tex_wall, tex_ceiling ]
for i in range (0, 3):
glBindTexture(GL_TEXTURE_2D, textures[i])
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glBindVertexArray(render_vao)
def display(self):
green = [ 0.0, 0.1, 0.0, 0.0 ]
currentTime = time.time()
glViewport(0, 0, self.width, self.height)
glClearBufferfv(GL_COLOR, 0, green)
glUseProgram(render_prog)
proj_matrix = (GLfloat * 16)(*identityMatrix)
proj_matrix = m3dPerspective(m3dDegToRad(60.0), float(self.width) / float(self.height), 0.1, 100.0)
glUniform1f(uniform.offset, -(currentTime * 0.03) % 1) # negative sign to postive changes direction
textures = [ tex_wall, tex_ceiling, tex_wall, tex_floor ]
for i in range(0, 4):
RZ = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RZ, i * m3dDegToRad(90.0), 0.0, 0.0, 1.0)
T = (GLfloat * 16)(*identityMatrix)
m3dTranslateMatrix44(T, -5, 0, -10)
RY = (GLfloat * 16)(*identityMatrix)
m3dRotationMatrix44(RY, m3dDegToRad(90.0), 0.0, 1.0, 0.0)
S = (GLfloat * 16)(*identityMatrix)
m3dScaleMatrix44(S, 300.0, 10.0, 1.0)
mv_matrix = (GLfloat * 16)(*identityMatrix)
mv_matrix = m3dMultiply(RZ, m3dMultiply(T, m3dMultiply(RY, S)))
mvp = (GLfloat * 16)(*identityMatrix)
mvp = m3dMultiply(proj_matrix , mv_matrix )
glUniformMatrix4fv(uniform.mvp, 1, GL_FALSE, mvp)
glBindTexture(GL_TEXTURE_2D, textures[i]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 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 - Tunnel')
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()
The program is supposed to appear like:
Update: With the amazing code and insights by Rabbid76 the output is now rendering correctly. An animated gif of the output is below.
Dependency files: brick.ktx , ceiling.ktx , floor.ktx ,
and in 'shared' folder ktxloader.py , sbmath.py
You've to use the global statement to set the variables tex_wall, tex_ceiling, tex_floor in global namespace, in the constructor of Scene:
class Scene:
def __init__(self, width, height):
global tex_wall, tex_ceiling, tex_floor
# [...]
tex_wall = ktxobj.ktx_load("brick.ktx")
tex_ceiling = ktxobj.ktx_load("ceiling.ktx")
tex_floor = ktxobj.ktx_load("floor.ktx")
# [...]
Further there are some issues when you set the model matrices. The y-scale has to be 10.0, to scale the walls to proper height and width:
m3dScaleMatrix44(S, 30.0, 1.0, 1.0)
m3dScaleMatrix44(S, 300.0, 10.0, 1.0)
The translation has to be done before the rotation around the z-axis, because walls, floor and ceiling should be rotated displaced. Scaling has to be done first:
mv_matrix = m3dMultiply(T, m3dMultiply(RZ, m3dMultiply(S, RY)))
mv_matrix = m3dMultiply(RZ, m3dMultiply(T, m3dMultiply(RY, S)))
In "ktxloader" module ptr is used, when the data bytes are read from the bitmap:
glTexSubImage2D(GL_TEXTURE_2D, i, 0, 0, width, height, h.glformat, h.gltype, data[ptr:])
So ptr has to be incremented by h.keypairbyte:
data_start = ptr + h.keypairbytes
dt = data[data_start:]
ptr += h.keypairbytes
The floor and the ceiling are swapped:
textures = [ tex_wall, tex_floor, tex_wall, tex_ceiling ]
textures = [ tex_wall, tex_ceiling, tex_wall, tex_floor ]
Before the texture offset uniform (uniform float offset) is set you've to use % (modulo) operator, because the value in currentTime is top large and doesn't fit in single precision (32 bit) floating point value. Since the texture coordinates are in range [0.0, 1.0], the fraction part of the offset can be calculated by % 1. e.g.:
glUniform1f(uniform.offset, (currentTime * -0.03) % 1)
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 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 )
I want to overlay 3d objects on OpenCV feed. I found an example here That uses webcam video as texture in OpenGL. I changed some part so that it works with cv2. Now the output is something strange
CODE
import cv2
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import numpy as np
import sys
#window dimensions
width = 1280
height = 720
nRange = 1.0
global capture
capture = None
def cv2array(im):
h,w,c=im.shape
a = np.fromstring(
im.tostring(),
dtype=im.dtype,
count=w*h*c)
a.shape = (h,w,c)
return a
def init():
#glclearcolor (r, g, b, alpha)
glClearColor(0.0, 0.0, 0.0, 1.0)
glutDisplayFunc(display)
glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
glutIdleFunc(idle)
def idle():
#capture next frame
global capture
_,image = capture.read()
cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#you must convert the image to array for glTexImage2D to work
#maybe there is a faster way that I don't know about yet...
#print image_arr
# Create Texture
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
720,1280,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
image)
cv2.imshow('frame',image)
glutPostRedisplay()
def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glEnable(GL_TEXTURE_2D)
#glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
#glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
#glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
#this one is necessary with texture2d for some reason
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
# Set Projection Matrix
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0, width, 0, height)
# Switch to Model View Matrix
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# Draw textured Quads
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0)
glVertex2f(0.0, 0.0)
glTexCoord2f(1.0, 0.0)
glVertex2f(width, 0.0)
glTexCoord2f(1.0, 1.0)
glVertex2f(width, height)
glTexCoord2f(0.0, 1.0)
glVertex2f(0.0, height)
glEnd()
glFlush()
glutSwapBuffers()
def reshape(w, h):
if h == 0:
h = 1
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# allows for reshaping the window without distoring shape
if w <= h:
glOrtho(-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange)
else:
glOrtho(-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def keyboard(key, x, y):
global anim
if key == chr(27):
sys.exit()
def main():
global capture
#start openCV capturefromCAM
capture = cv2.VideoCapture(0)
print capture
capture.set(3,1280)
capture.set(4,720)
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(width, height)
glutInitWindowPosition(100, 100)
glutCreateWindow("OpenGL + OpenCV")
init()
glutMainLoop()
main()
I find it very unlikely that your camera is providing a 720 pixel wide and 1280 pixel high image as you tell to OpenGL here:
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
720,1280, // <---- HERE
0,
GL_RGB,
GL_UNSIGNED_BYTE,
image)
It seems you just mixed up those two parametes, so the data is reinterpreted as such, resulting in the output you got.