Stencil test does not clip Texture - python

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)

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))

PyOpenGL numpy texture appears as solid color

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

Showing a dynamic plane

I have written a Python program that continuously returns 4 changing Cartesian coordinates that align to form a square plane that can be at any given orientation; yaw, pitch, or roll. What is the best way to go about displaying the constantly updating plane in 3D space?
Note: This is being done on a Linux machine if that changes anything, however I cannot see how it would.
You can use PyOpenGL for that.
http://pyopengl.sourceforge.net/
It can be installed with pip.
Easiest is to use the "legacy" API and draw a quad.
To change yaw, pitch and roll, use a transformation matrix and glRotate.
You can also use shaders with it and draw up the transformation matrix yourself.
https://en.wikipedia.org/wiki/Rotation_matrix
Example of drawing a textured plane with the OpenGL legacy API:
import sys
import math
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
def init():
global image, texName
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH)
glShadeModel(GL_FLAT)
glEnable(GL_DEPTH_TEST)
import Image, numpy
img = Image.open('flagEn.bmp') # .jpg, .bmp, etc. also work
img_data = numpy.array(list(img.getdata()), numpy.int8)
global texture
texture = glGenTextures(1)
glPixelStorei(GL_UNPACK_ALIGNMENT,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)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.size[0], img.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
def display():
#global texName
global texture
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glEnable(GL_TEXTURE_2D)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
glBindTexture(GL_TEXTURE_2D, texture)
glBegin(GL_QUADS)
glTexCoord2f(0, 0)
glVertex3f(-2, -1, 0)
glTexCoord2f(0, 10)
glVertex3f(-2, 1, 0)
glTexCoord2f(10, 10)
glVertex3f(0, 1, 0)
glTexCoord2f(10, 0)
glVertex3f(0, -1, 0)
glTexCoord2f(0, 0)
glVertex3f(1, -1, 0)
glTexCoord2f(0, 10)
glVertex3f(1, 1, 0)
glTexCoord2f(10, 10)
glVertex3f(1+math.sqrt(2), 1, -math.sqrt(2))
glTexCoord2f(10, 0)
glVertex3f(1+math.sqrt(2), -1, -math.sqrt(2))
glEnd()
glFlush()
glDisable(GL_TEXTURE_2D)
def reshape(w, h):
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, w/h, 1.0, 30.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -3.6);
def keyboard(key, x, y):
pass
glutInit(sys.argv)
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE)
glutInitWindowSize (500, 500)
glutInitWindowPosition (100, 100)
glutCreateWindow ('texture')
init ()
glutDisplayFunc(display)
glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
glutMainLoop()

Categories