PyOpenGL numpy texture appears as solid color - python

I'm trying to set numpy arrays as OpenGl textures in pygame. The problem is that only the first pixel is taken as texture data and instead of a random pattern (for example) I only get a random color every time.
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
#Settings
width = 200
height = 300
resolution = (800,600)
texture_data = np.random.randint(256,size=(height, width, 3))
#pygame init
pygame.init()
gameDisplay = pygame.display.set_mode(resolution,OPENGL)
#OpenGL init
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0,resolution[0],0,resolution[1],-1,1)
glMatrixMode(GL_MODELVIEW)
glDisable(GL_DEPTH_TEST)
glClearColor(0.0,0.0,0.0,0.0)
glEnable(GL_TEXTURE_2D)
#Set texture
texture = glGenTextures(1)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture_data)
glGenerateMipmap(GL_TEXTURE_2D)
glActiveTexture(GL_TEXTURE0)
#Clean start
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
#draw rectangle
glTranslatef(300,200,0)
glBegin(GL_QUADS)
glVertex2f(0,0)
glVertex2f(0,height)
glVertex2f(width,height)
glVertex2f(width,0)
glEnd()
glFlush()
#game loop until exit
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
#throttle
pygame.time.wait(100)

As pointed out by genpfault I was missing texture coordinates. I needed to add them to the rectangle drawing part.
#draw rectangle
glTranslatef(300,200,0)
glBegin(GL_QUADS)
glTexCoord(0,0)
glVertex2f(0,0)
glTexCoord(0,1)
glVertex2f(0,height)
glTexCoord(1,1)
glVertex2f(width,height)
glTexCoord(1,0)
glVertex2f(width,0)
glEnd()
glFlush()
Read this if you are lost on how texture mapping works:
OpenGL Programming Guide - Chapter 9 Texture Mapping

Related

pyOpenGL how to draw 2d image?

I tried many things and found out how to get data using pillow and numpy. Even if I looked around, I could only see how to make vertex and fragments in 3d, and I couldn't find a way to actually draw a 2d image.
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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
tex = Image.open("Resources/bg55.png")
mode = "".join(Image.Image.getbands(tex))
data = tex.tobytes("raw", "RGBA", 0, -1)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
glGenerateMipmap(GL_TEXTURE_2D)
Is this code correct? How can I continue? glfw and pyopengl is in use.
You need to draw a rectangle and wrap the texture on it. This is the usual way to proceed. OpenGL renders primitives and does not draw images. See the very basic example:
image
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from PIL import Image
from numpy import array
def loadTexture(texture):
try:
text = Image.open(texture)
except IOError as ex:
print("Failed to open texture file: ", texture)
text = Image.open("0.png")
textData = array(list(text.getdata()))
textID = glGenTextures(1)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
glBindTexture(GL_TEXTURE_2D, textID)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, text.size[0], text.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, textData)
text.close()
return textID
def drawQuad(centerX, centerY, textureID):
verts = ((1, 1), (1,-1), (-1,-1), (-1,1))
texts = ((1, 0), (1, 1), (0, 1), (0, 0))
surf = (0, 1, 2, 3)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, textureID)
glBegin(GL_QUADS)
for i in surf:
glTexCoord2f(texts[i][0], texts[i][1])
glVertex2f(centerX + verts[i][0], centerY + verts[i][1])
glEnd()
glDisable(GL_TEXTURE_2D)
def main():
pygame.init()
disp = (200, 200)
pygame.display.set_mode(disp, DOUBLEBUF|OPENGL)
gluPerspective(45, (disp[0] / disp[1]), 0.1, 50.0)
glTranslatef(0.0, 0.0, -5)
textID = loadTexture("0.png")
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
drawQuad(0, 0, textID)
pygame.display.flip()
pygame.time.wait(10)
main()

How can I "blit" my Pygame game onto an OpenGL surface?

I built my entire game around Pygame and want to put it on Steam. I learned at the end that I would need OpenGL support to be able to run Steam's Overlay. The code to initialize the display:
screen = pygame.display.set_mode((screen_width, screen_height), HWSURFACE | DOUBLEBUF | OPENGL)
Is there any way that I can create an OpenGL surface and blit my entire game onto that surface, so I can get OpenGL functionality (the Steam Overlay), without having to redo a lot of code and recreate a lot of the game? The game doesn't use a lot of resources, so I don't think there will be much of a lag (hopefully), so it's definitely a route I'd like to try.
Do I have any options here, aside from redoing the game in a different library?
Based on this question: Draw rectangle over texture OpenGL which discussed texture-mapping an OpenGL rectangle in PyGame ~
Here is some code which draws to an "off screen" PyGame surface. On each main-loop iteration, that surface is converted to an OpenGL texture map. This texture map is then mapped onto a rectangle which fills the screen.
The code is a fairly simple example, and perhaps you will need to optimise it a bit.
import pygame
import sys
from OpenGL.GL import *
from pygame.locals import *
# set pygame screen
pygame.init()
pygame.display.set_mode((500, 500), OPENGL | DOUBLEBUF)
pygame.display.init()
info = pygame.display.Info()
#colours
MIDNIGHT = ( 15, 0, 100 )
BUTTER = ( 255, 245, 100 )
# basic opengl configuration
glViewport(0, 0, info.current_w, info.current_h)
glDepthRange(0, 1)
glMatrixMode(GL_PROJECTION)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glShadeModel(GL_SMOOTH)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glDisable(GL_DEPTH_TEST)
glDisable(GL_LIGHTING)
glDepthFunc(GL_LEQUAL)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glEnable(GL_BLEND)
###
### Function to convert a PyGame Surface to an OpenGL Texture
### Maybe it's not necessary to perform each of these operations
### every time.
###
texID = glGenTextures(1)
def surfaceToTexture( pygame_surface ):
global texID
rgb_surface = pygame.image.tostring( pygame_surface, 'RGB')
glBindTexture(GL_TEXTURE_2D, texID)
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_CLAMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
surface_rect = pygame_surface.get_rect()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, surface_rect.width, surface_rect.height, 0, GL_RGB, GL_UNSIGNED_BYTE, rgb_surface)
glGenerateMipmap(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, 0)
# create pygame clock
clock = pygame.time.Clock()
# make an offscreen surface for drawing PyGame to
offscreen_surface = pygame.Surface((info.current_w, info.current_h))
text_font = pygame.font.Font( None, 30 ) # some default font
done = False
while not done:
# get quit event
for event in pygame.event.get():
if event.type == QUIT:
done = True
# Do all the PyGame operations to the offscreen surface
# So any backgrounds, sprites, etc. will get drawn to the offscreen
# rather than to the default window/screen.
offscreen_surface.fill( MIDNIGHT )
# write some nonsense to put something changing on the screen
words = text_font.render( "β-Moé-Moé count: "+str( pygame.time.get_ticks() ), True, BUTTER )
offscreen_surface.blit( words, (50, 250) )
# prepare to render the texture-mapped rectangle
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glDisable(GL_LIGHTING)
glEnable(GL_TEXTURE_2D)
#glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
#glClearColor(0, 0, 0, 1.0)
# draw texture openGL Texture
surfaceToTexture( offscreen_surface )
glBindTexture(GL_TEXTURE_2D, texID)
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()
pygame.display.flip()
clock.tick(60)
pygame.quit()

Draw rectangle over texture OpenGL

I'm using python 3 with pygame and OpenGL to try to accomplish what I thought it would be a simple task: Drawing a rectangle.
The idea is to have a white rectangle over (or bellow) a transparent texture, but whenever I add the texture to the screen the rectangle vanishes, whether I render it before or after the texture.
Bellow is a sample code displaying the problem (you can add any Player1.png image of your choice, the problem will remain the same - at least in my computer)
import pygame
import sys
from OpenGL.GL import *
from pygame.locals import *
# set pygame screen
pygame.display.set_mode((500, 500), OPENGL | DOUBLEBUF)
info = pygame.display.Info()
# basic opengl configuration
glViewport(0, 0, info.current_w, info.current_h)
glDepthRange(0, 1)
glMatrixMode(GL_PROJECTION)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glShadeModel(GL_SMOOTH)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glDisable(GL_DEPTH_TEST)
glDisable(GL_LIGHTING)
glDepthFunc(GL_LEQUAL)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glEnable(GL_BLEND)
# load texture
surf = pygame.image.load('Player1.png')
s = pygame.image.tostring(surf, 'RGBA')
texID = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texID)
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_CLAMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 142, 65, 0, GL_RGBA, GL_UNSIGNED_BYTE, s)
glGenerateMipmap(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, 0)
# create pygame clock
MAINCLOCK = pygame.time.Clock()
# init screen
pygame.display.init()
while True:
# get quit event
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# prepare to render screen
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glDisable(GL_LIGHTING)
glEnable(GL_TEXTURE_2D)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glClearColor(0, 0, 0, 1.0)
# draw texture
glBindTexture(GL_TEXTURE_2D, texID)
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()
# draw rectangle
glColor3fv((1, 1, 1))
glRectf(-1, 1, 0, 0.5)
pygame.display.flip()
MAINCLOCK.tick(60)
It most likely has to do something on how OpenGL treats Textures vs Rects, but I'm not sure what.
BTW: I know the image is upside down
Thanks in advance
You have to enable two-dimensional texturing before you draw the texture, as you do it (glEnable(GL_TEXTURE_2D)).
But you have to disable two-dimensional texturing again, before you draw the rectangle:
# draw rectangle
glDisable(GL_TEXTURE_2D)
glColor3fv((1, 1, 1))
glRectf(-1, 1, 0, 0.5)
Note, the texture is still bound, when you draw the rectangle. Since you do not provide texture coordinates, when you draw the rectangle. This causes that the current texture coordinate is applied to the rectangle and a single texel is drawn all over the rectangle.
e.g. The last texture coordinate set was glTexCoord2f(1, 0):
Further note, if you would change the color for the rectangle, then the entire texture get tint by this color. If texturing is enabled, then by default the color of the texel is multiplied by the current color, because by default the texture environment mode (GL_TEXTURE_ENV_MODE) is GL_MODULATE. See glTexEnv.
glDisable(GL_TEXTURE_2D)
glColor3fv((1, 0, 0)) # red
glRectf(-1, 1, 0, 0.5)
Set the "white" color before you draw the texture:
glEnable(GL_TEXTURE_2D)
glColor3fv((1, 1, 1))

Stencil test does not clip Texture

I'm trying to clip a texture using a Stencil Test.
The idea is to create a surface (in this example a simple rectangle) to select a region of the texture to be shown (as the image bellow)
I created a simple code to do so, where I first perform an ALWAYS stencil test to set all the bits on the stencil buffer to 2, and then change the test to KEEP, which I thought would output the desired result, but nothing happens
import pygame
import sys
from OpenGL.GL import *
from pygame.locals import *
# set pygame screen
pygame.display.set_mode((1000, 500), OPENGL | DOUBLEBUF)
info = pygame.display.Info()
# basic opengl configuration
glViewport(0, 0, info.current_w, info.current_h)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# set up texturing
glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
# load texture
surf = pygame.image.load('Player1.png')
s = pygame.image.tostring(surf, 'RGBA')
texID = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texID)
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_CLAMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf.get_width(), surf.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, s)
glGenerateMipmap(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, 0)
# create pygame clock
MAINCLOCK = pygame.time.Clock()
# init screen
pygame.display.init()
while True:
# get quit event
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# prepare to render screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# Enable stencil test
glEnable(GL_STENCIL_TEST)
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
glStencilFunc(GL_ALWAYS, 2, ~0)
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE)
# draw rectangle
glDisable(GL_TEXTURE_2D)
glColor3fv((0, 0, 0))
glRectf(-1, 1, 0, 0.5)
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
glStencilFunc(GL_EQUAL, 2, ~0)
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
# draw texture
glEnable(GL_TEXTURE_2D)
glColor3fv((1, 1, 1))
glBindTexture(GL_TEXTURE_2D, texID)
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()
# disable stencil test
glDisable(GL_STENCIL_TEST)
pygame.display.flip()
MAINCLOCK.tick(60)
What am I missing?
Thanks in advance
The stencil test is proper implemented, but you forgot to setup the size of the stencil buffer when you initialize the PyGame OpenGL window. In your case the stencil test does not work, because there is no stencil buffer.
The stecil buffer can be set up by setting the GL_STENCIL_SIZE attribute with the method pygame.display.gl_set_attribute
Add the following to your code:
pygame.display.init()
pygame.display.gl_set_attribute(GL_STENCIL_SIZE, 8)
pygame.display.set_mode((1000, 500), OPENGL | DOUBLEBUF)

OpenGL - Show Texture Only

I'm working on a 2D isometric game, using pygame and pyopengl.
I'm drawing sprites as quads, with a texture. I managed to get the alpha transparency to work for the texture, but the quad itself is still filled in a solid color (whatever colour gl pipeline is set with at the time).
How do I hide the quad shape, and just show the texture?
Here is a pic showing the problem (gl pipeline set to pink/purple color):
The code is a bit messy, and I've been blindly copy 'n pasting gl calls hoping it solves the problem so there are bound to be quite a few calls in the wrong place or duplicated (or both).
GL Setup code (called once at start of script)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glViewport(0, 0, screen_size[0], screen_size[1])
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0.0, screen_size[0], 0.0, screen_size[1], 0.0, 1.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glDisable(GL_LIGHTING)
glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
Drawing setup code (called once at the start of each frame)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glViewport(0, 0, screen_size[0], screen_size[1])
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0.0, screen_size[0], 0.0, screen_size[1], 0.0, 1.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glDisable(GL_LIGHTING)
glEnable(GL_TEXTURE_2D)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
Quad draw code (called for every sprite draw call):
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_TEXTURE_2D)
glEnable(GL_ALPHA_TEST)
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)
# Start new transformation matrix
glPushMatrix()
# Apply translation
glTranslatef(self.rect.centerx, self.rect.centery, 0.0)
# Start gl drawing cursor
glColor4f(1.0, 0.0, 1.0, 1.0)
# Bind the texture to this draw session
glBindTexture(GL_TEXTURE_2D, self.texture.id)
# Start drawing a quad
glBegin(GL_QUADS)
# Grab new copy of rect, and move to the origin
r = self.rect.copy()
r.center = (0, 0)
# Draw top left point
glTexCoord2f(1.0, 0.0)
glVertex2f(*r.topleft)
# Draw top right point
glTexCoord2f(0.0, 0.0)
glVertex2f(*r.topright)
# Draw bottom right point
glTexCoord2f(0.0, 1.0)
glVertex2f(*r.bottomright)
# Draw bottom left point
glTexCoord2f(1.0, 1.0)
glVertex2f(*r.bottomleft)
# End quad
glEnd()
# Apply transformation matrix
glPopMatrix()
The colored background behind your tiles is probably due to this line when you set up your texture:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
Just remove this as the default texture environment settings are probably fine for standard tile rendering. As an example of what messing with these parameters can do, if you wanted glColor calls to "tint" your texture instead, then replace GL_DECAL with GL_BLEND.
There is no need for any of those lighting calls included in your code as far as I can tell unless you are working with 3d models and ancient per-vertex lighting (I assume you are not since this is a 2d isometric game). Also you only need blending for this, no need for alpha testing. Assuming you are working with images with alpha (RGBA format), here is a simple demo that displays two tiles with a transparent background (supply your own image of course instead of ./images/grass.png):
import pygame
from pygame.locals import *
from OpenGL.GL import *
import sys
class Sprite(object):
def __init__(self):
self.x = 0
self.y = 0
self.width = 0
self.height = 0
self.texture = glGenTextures(1)
def load_texture(self, texture_url):
tex = pygame.image.load(texture_url)
tex_surface = pygame.image.tostring(tex, 'RGBA')
tex_width, tex_height = tex.get_size()
glBindTexture(GL_TEXTURE_2D, self.texture)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_surface)
glBindTexture(GL_TEXTURE_2D, 0)
self.width = tex_width
self.height = tex_height
def set_position(self, x, y):
self.x = x
self.y = y
def render(self):
#glColor(1, 1, 1, 1)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glBindTexture(GL_TEXTURE_2D, self.texture)
glBegin(GL_QUADS)
glTexCoord(0, 0)
glVertex(self.x, self.y, 0)
glTexCoord(0, 1)
glVertex(self.x, self.y + self.height, 0)
glTexCoord(1, 1)
glVertex(self.x + self.width, self.y + self.height, 0)
glTexCoord(1, 0)
glVertex(self.x + self.width, self.y, 0)
glEnd()
glBindTexture(GL_TEXTURE_2D, 0)
def init_gl():
window_size = width, height = (550, 400)
pygame.init()
pygame.display.set_mode(window_size, OPENGL | DOUBLEBUF)
glEnable(GL_TEXTURE_2D)
glMatrixMode(GL_PROJECTION)
glOrtho(0, width, height, 0, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
if __name__ == "__main__":
init_gl()
tile1 = Sprite()
tile1.load_texture("./images/grass.png")
tile1.set_position(50, 100)
tile2 = Sprite()
tile2.load_texture("./images/grass.png")
tile2.set_position(80, 130)
tiles = [tile1, tile2]
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
glClear(GL_COLOR_BUFFER_BIT)
glColor(1, 0, 0, 1)
for tile in tiles:
tile.render()
pygame.display.flip()
Let me know if this helps!
Well, either use blending, so that the alpha value actually has effect on opacity. Or use alpha testing, so that incoming fragments with an alpha below/above a certain threshold are discarded.
Blending requires to sort geometry back to front. And given what you want to do alpha testing may be the easier, more straightforward solution.
Update:
Either way it's imperative that the texture's alpha value makes it through to the fragment. If you were using shaders this would be as simple as making sure that the fragment output alpha would receive its value from the texture. But you're using the fixed function pipeline and the mess that's the texture environment state machine.
Using only a single texture your best bet would be a GL_REPLACE texture mode (completely ignores the vertex color). Or GL_MODULATE that takes the vertex color into account. Right now you're assumingly using GL_DECAL mode.
My suggestion: Drop the fixed function pipeline and use shaders. Much easier to get things related to texturing working. Also you'll hard pressed to find hardware that's not using shaders anyway (unless you're planning to run your program on stuff that's been built before 2004).

Categories